数据结构学习笔记--BST与AVL

数据结构学习笔记–BST与AVL

前言

BST和AVL复习。

BST

二叉排序树的3个主要性质:

  1. 若左子树不空,则左子树上的所有节点的值均小于根节点的值。
  2. 若右子树不空,则右子树上的所有节点的值均大于根节点的值。
  3. 左、右子树也分别为二叉排序树。

对于插入操作,按照上述规则,递归即可。
但是对于删除操作,涉及到排序规则的保持,可分3中情况考虑:

  1. 若删除节点为叶子节点,则删除该节点不影响整棵树的结果,只需修改父节点指针即可。
  2. 若删除节点只有左子树或只有右子树,此时,只要令左子树或右子树替换删除节点即可。
  3. 若左子树与右子树均不为空,则使用删除节点的直接后继节点的值代替删除节点的值,然后删除直接后继节点即可。

严蔚敏版的数据结构,对于第3中情况,有不同两种处理方式:

  1. 使删除节点的左子树代替删除节点,使删除节点的右子树成为删除节点直接前驱的右子树。
  2. 使用删除节点的直接前驱的值代替删除节点的值,然后删除直接前驱节点。

其中第2种方式与上面第3中情况的处理方式类似,容易理解,因为要保持顺序性,要么直接前驱、要么直接后继来代替,才能保持有序; 第1种方式稍微有点绕,不过删除节点右子树成为直接前驱的右子树,也是保持着有序性的。

不过从代码实现的简洁程度和便于理解的角度,我倾向于使用上面第3种情况的处理方式。

AVL

BST在插入关键字有序时,会蜕化为单支树,所以引出了平衡二叉树。平衡二叉树除了具有二叉搜索树的性质外,还有两个性质:

  1. 左子树和右子树都是平衡二叉树。
  2. 左子树和右子树高度只差的绝对值不超过1。

节点高度的定义为从该节点到叶子节点的路径(边)的长度,叶子节点的高度为0。

从多出来的两个性质可以看出,变化主要在插入和删除操作上,对于这两个操作,除了要保持顺序性,现在还要保持平衡。

数据结构学习笔记--BST与AVL_第1张图片
假设在节点v上出现了不平衡:
bf = height(v.left) - height(v.right)
bf2 = height(v.left.left) - height(v.left.right)
bf3 = height(v.right.left) - height(v.right.right)

则有四种不平衡的场景:

  1. bf=2,bf2=1
  2. bf=2, bf2=-1
  3. bf=-2, bf3=1
  4. bf=-2, bf3=-1

场景梳理出来,就有对应的解决方法:

  1. 单向右旋。 (对应场景1。)
  2. 单向左旋。 (对应场景4。)
  3. 先右旋,后左旋。 (对应场景3)
  4. 先左旋,后右旋。 (对应场景2)

另一种方式是使用平衡因子,也就是上面算的bf来作为节点属性进行平衡操作,具体参见严蔚敏版的数据结果。 不过,同样,从代码实现和便于理解的角度来看,我倾向于使用高度来作为节点属性。

总结

BST和AVL原理简单清晰,但是真要自己动手实现起来,还是很麻烦的,尤其是想把代码写的简洁。 惯例吐槽,严蔚敏版的数据结构,读起来确实累。

附录:

  1. 《数据结构(C语言版)》 – 严蔚敏
  2. 《算法导论》
  3. 算法可视化网站–visualgo

附上自己的实现(JAVA版)

代码参考了VISUALGO的实现,没有注释,结合上面的描述对应着看吧。

/**
* avl tree
* @author lhz
*/
public class AvlTree extends BinarySearchTree {

    public void insert(Comparable v) {
        if (v == null) {
            throw new NullPointerException();
        }

        root = insert(root, v);
    }

    private int height(Node v) {
        return v == null ? -1 : v.height;
    }

    private Node rotateRight(Node v) {
        Node lc = v.left;

        v.left = lc.right;
        if (lc.right != null) {
            lc.right.parent = v;
        }

        lc.right = v;
        lc.parent = v.parent;
        v.parent = lc;

        v.height = Math.max(height(v.left), height(v.right)) + 1;
        lc.height = Math.max(height(lc.left), height(lc.right)) + 1;

        return lc;
    }

    private Node rotateLeft(Node v) {
        Node rc = v.right;

        v.right = rc.left;
        if (rc.left != null) {
            rc.left.parent = v;
        }

        rc.parent = v.parent;
        rc.left = v;
        v.parent = rc;

        v.height = Math.max(height(v.left), height(v.right)) + 1;
        rc.height = Math.max(height(rc.left), height(rc.right)) + 1;

        return rc;
    }

    @Override
    protected Node insert(Node v, Comparable k) {
        v = super.insert(v, k);
        v = balance(v);
        return v;
    }

    public void remove(Comparable v) {
        root = remove(root, v);
    }
	
	@Override
    protected Node remove(Node v, Comparable k) {
        v = super.remove(v, k);
        v = balance(v);
        return v;
    }

    protected Node balance(Node k) {
        if (k == null) {
            return null;
        }

        int balance = height(k.left) - height(k.right);

        if (balance == 2) {
            int balance2 = height(k.left.left) - height(k.left.right);
            if (balance2 == 1) {
                k = rotateRight(k);
            } else {
                k.left = rotateLeft(k.left);
                k = rotateRight(k);
            }
        } else if (balance == - 2) {
            int balance2 = height(k.right.left) - height(k.right.right);
            if (balance2 == -1) {
                k = rotateLeft(k);
            } else {
                k.right = rotateRight(k.right);
                k = rotateLeft(k);
            }
        }

        k.height = Math.max(height(k.left), height(k.right)) + 1;

        return k;
    }
}


/**
* avl tree
* @author lhz
*/
public class BinarySearchTree {
    class Node {
        public Comparable value;
        public int bf; //balance factor
        public int height;
        public int ref;//same value reference count
        public Node left;
        public Node right;
        public Node parent;

        public Node(Comparable v) {
            value = v;
        }

        public String toString() {
            return String.format("[%s:%d, h=%d, bf=%d]", value, ref, height, bf);
        }
    }

    protected Node root;


    public Comparable min(Node v) {
        if (v == null) {
            return null;
        }

        while (v.left != null) {
            v = v.left;
        }
        return v.value;
    }

    public Comparable max(Node v) {
        if (v == null) {
            return null;
        }

        while (v.right != null) {
            v = v.right;
        }

        return v.value;
    }

    public Node search(Node v, Comparable k) {
        if (v == null) {
            return null;
        }

        int compared = k.compareTo(v.value);

        if (compared > 0) {
            return search(v.right, k);
        } else if (compared < 0) {
            return search(v.left, k);
        } else {
            return v;
        }
    }

    protected Node insert(Node v, Comparable k) {
        if (v == null) {
            return new Node(k);
        }

        int compared  = k.compareTo(v.value);

        if (compared > 0) {
            v.right = insert(v.right, k);
            v.right.parent = v;
        } else if (compared < 0) {
            v.left = insert(v.left, k);
            v.left.parent = v;
        } else {
            v.ref++;
        }

        return v;
    }

    protected Node remove(Node v, Comparable k) {
        if (v == null) {
            return null;
        }

        int compared  = k.compareTo(v.value);

        if (compared > 0) {
            v.right = remove(v.right, k);
            if (v.right != null) v.right.parent = v;
        } else if (compared < 0) {
            v.left = remove(v.left, k);
            if (v.left != null) v.left.parent = v;
        } else {
            if (v.right == null && v.left == null) {
                return null;
            } else if (v.left != null && v.right != null) {
                Comparable s = successor(v);
                v.value = s;
                v.right = remove(v.right, s);
            } else if (v.left != null) {
                return v.left;
            } else if (v.right != null) {
                return v.right;
            }
        }

        return v;
    }

    protected Comparable successor(Node v) {
        if (v.right != null) {
            return min(v.right);
        } else {
            Node p = v.parent;
            Node c = v;
            while (p != null && p.right == c) {
                c = p;
                p = p.parent;
            }
            return p == null ? null : p.value;
        }
    }

    protected Comparable predecessor(Node v) {
        if (v.left != null) {
            return max(v.left);
        } else {
            Node p = v.parent;
            Node c = v;
            while (p != null && p.left == c) {
                c = p;
                p = p.parent;
            }
            return  p == null ? null : p.value;
        }
    }

}

你可能感兴趣的:(算法)