树,二叉树,二叉排序树、AVL树

222 阅读5分钟

对于大量的数据,链表的线性访问速度过慢,不宜使用。而树的运行平均时间再logN。

1、树

  • 节点分类:跟节点、叶子结点、非叶子结点
  • 度、深度、高度、层

度:节点拥有的子节点数 深度:跟节点到节点的路径长度 高度: 节点到叶子节点所有路径上包含节点个数的最大值 层次:根节点在第一层,向下以此

  • 二叉树,完全二叉树,满二叉树

二叉树:最多有两个叶子节点 完全二叉树:最下面两层的结点的度数可以小于2 满二叉树:除了叶子结点,所有节点的度都是2 对于任何非空二叉树,叶节点的数目是度为2的节点的总数加一。

  • 计算

在二叉树的第i层上至多有2^(i-1)个结点 深度为k的二叉树至多有2^k - 1个结点  对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2

2、二叉排序树

二叉排序树是一种特殊的二叉树,它的左孩子小于跟节点,右孩子大于跟节点,对其进行算法实现,它内部维护一个树节点类

public class LitGrassBinarySearchTree<T extends Comparable<? super T>> {  
    private BinaryNode<T> root;  
      
    public LitGrassBinarySearchTree(){  
        root = null;  
    }  
    public void makeEmpty(){  
        root = null;  
    }  
    public boolean isEmpty(){  
        return root == null;  
    }  
    public boolean contains(T e){  
        return contains(e,root);  
    }  
    public T findMin(T e){  
        if (isEmpty()) {  
            throw new RuntimeException("空树");  
        }  
        return findMin(root).element;  
    }  
    public T findMax(T e){  
        if (isEmpty()) {  
            throw new RuntimeException("空树");  
        }  
        return findMax(root).element;  
    }  
    public void insert(T e){  
        root = insert(e,root);  
    }  
    public void remove(T e){  
        root = remove(e,root);  
    }  
  
    private static class BinaryNode<T>{  
        BinaryNode<T> left;  
        BinaryNode<T> right;  
        T element;  
  
        public BinaryNode(BinaryNode<T> left, T element, BinaryNode<T> right) {  
            this.left = left;  
            this.right = right;  
            this.element = element;  
        }  
  
        public BinaryNode(T e){  
            this(null,e,null);  
        }  
    }  
  
    //遍历  
    public void preOrderTree(BinaryNode<T> node){  
        if(node != null){  
            System.out.println(node.element);  
            preOrderTree(node.left);  
            preOrderTree(node.right);  
        }  
    }  
  
    public void midOrderTree(BinaryNode<T> node){  
        if (node != null) {  
            midOrderTree(node.left);  
            System.out.println(node.element);  
            midOrderTree(node.right);  
        }  
    }  
  
    public void postOrderTree(BinaryNode<T> node){  
        if (node != null) {  
            postOrderTree(node.left);  
            postOrderTree(node.right);  
            System.out.println(node.element);  
        }  
    }  
  
    private boolean contains(T e,BinaryNode<T> node){  
        if(node == null){  
            return false;  
        }  
        int i = e.compareTo(node.element);  
        if(i == 0){  
            return true;  
        } else if (i < 0) {  
            return contains(e,node.left);  
        }else {  
            return contains(e,node.right);  
        }  
    }  
  
    private BinaryNode<T> findMin(BinaryNode<T> node){  
        if (node == null) {  
            return null;  
        } else if (node.left == null) {  
            return node;  
        }else {  
            return findMin(node.left);  
        }  
    }  
  
    private BinaryNode<T> findMax(BinaryNode<T> node){  
        if (node != null) {  
            while (node.right != null) {  
                node = node.right;  
            }  
        }  
        return node;  
    }  
  
  
    private BinaryNode<T> insert(T e,BinaryNode<T> node){  
        if (node == null) {  
            return new BinaryNode<>(null,e,null);  
        }  
        int compareResult = e.compareTo(node.element);  
  
        if (compareResult < 0) {  
            node.left = insert(e,node.left);  
        } else if (compareResult > 0) {  
            node.right = insert(e,node.right);  
        }else {  
            System.out.println("已存在该元素");  
        }  
        return node;  
    }  
  
    private BinaryNode<T> remove(T e,BinaryNode<T> node){  
        if (node == null) {  
            return node;//没有找到  
        }  
        int compareToResult = e.compareTo(node.element);  
        if(compareToResult < 0){  
            node.left = remove(e,node.left);  
        } else if (compareToResult > 0) {  
            node.right = remove(e,node.right);  
        } else if (node.left != null && node.right != null) {  
            node.element = findMin(node.right).element;  
            node.right = remove(node.element,node.right);  
        }else {  
            node = node.left != null ? node.left : node.right;  
        }  
        return node;  
    }  
  
}
  • 二叉排序树的删除,在删除后为了保持二叉排序树的性质,需要通过左子树的最大元素或者右子树最小节点进行替换来消除该不平衡 ==删除只有一个子节点== image.png ==删除有两个子节点== image.png image.png
  • 当然删除树也可采用惰性删除的方法进行删除:当一个元素被删除的时候,他仍然留在树中,只是进行标记删除。这样删除还有一个好处就是处理重复项的问题,删除重复的时候,只需标记频率的数减去1,添加的时候加上1。
  • 平均情况分析:操作的平均时间为logN,是根据树的深度进行变化的。但是由于删除的算法等特殊情况,特别容易出现左子树比右子树深的情况或者右子树比左子树深的情况,此时二叉树可能蜕变成一个单链表的情况,所以我们删除或者添加的时候,要有一个平衡的条件,就是我们的平衡二叉树;还有一种方法是放弃平衡条件,允许树的任意深度,但每次操作之后都要进行调整,我们叫其为伸展树

3、AVL树

它是一中带有平衡条件的二叉查找树,要求是:他需要左子树和右子树的高度差最多是1。这也就是的树的深度为logN
  • 由于树的平衡条件的限制,当我们进行插入或者进行删除的时候,可能为破坏AVL树的性质,此时需要进行修正,即进行旋转,当进行插入的时候,我们发现沿着这条插入路径,会出现高度差为2,共分呢为4种情况,我们吧重新平衡的节点叫做a
    • a的左儿子的左子树进行插入
    • a的左儿子的右子树进行插入
    • a的右儿子的右子树进行插入
    • a的右儿子的左子树进行插入 image.png
  • 可以看出他们是对称的,也就是可以分为2种情况进行分析。及左左或者右右情况进行一次单旋转,还有一种是左右或者右左的情况,需要进行复杂的双旋转进行处理 ==单旋转== image.png
//旋转左子树
private AVLNode<T> rotateWithLeftChild(AVLNode<T> node){  
    AVLNode<T> newRoot = node.left;  
    node.left = newRoot.right;  
    newRoot.right = node;  
    node.height = Math.max(height(node.left),height(node.right));  
    newRoot.height = Math.max(height(newRoot.left), node.height);  
    return newRoot;  
}  
//旋转右子树 
private AVLNode<T> rotateWithRightChild(AVLNode<T> node){  
    AVLNode<T> newRoot = node.right;  
    node.right = newRoot.left;  
    newRoot.left = node;  
    node.height = Math.max(height(node.left),height(node.right));  
    newRoot.height = Math.max(node.height,height(newRoot.right));  
    return newRoot;  
}

==双旋转== image.png

private AVLNode<T> doubleWithLeftChild(AVLNode<T> node){  
    node.left = rotateWithRightChild(node.left);  
    return rotateWithLeftChild(node);  
}  
  
private AVLNode<T> doubleWithRightChild(AVLNode<T> node){  
    node.right = rotateWithLeftChild(node.right);  
    return rotateWithRightChild(node);  
}
  • 代码实现
public class LitGrassAVLTree<T extends Comparable<? super T>> {  
  
    private static class AVLNode<T>{  
        T element;  
        AVLNode<T> left;  
        AVLNode<T> right;  
        int height;  
  
        public AVLNode(T element, AVLNode<T> left, AVLNode<T> right) {  
            this.element = element;  
            this.left = left;  
            this.right = right;  
            this.height = 0;  
        }  
  
        public AVLNode(T element) {  
            this(element,null,null);  
        }  
    }  
  
    private int height(AVLNode<T> node){  
        return node == null ? -1 : node.height;  
    }  
  
    private AVLNode<T> findMin(AVLNode<T> node){  
        if(node == null){  
            return null;  
        } else if (node.left == null) {  
            return node;  
        }  
        return findMin(node.left);  
    }  
  
    private AVLNode<T> insert(T e,AVLNode<T> node){  
        if (node == null) {  
            return new AVLNode<>(e,null,null);  
        }  
        int compareToResult = e.compareTo(node.element);  
        if(compareToResult < 0){  
            node.left = insert(e,node);  
        } else if (compareToResult > 0) {  
            node.right = insert(e,node.right);  
        }else ;  
        return balance(node);  
    }  
  
    private AVLNode<T> remove(T e,AVLNode<T> node){  
        if (node == null) {  
            throw new RuntimeException("无该节点");  
        }  
        int compareToResult = e.compareTo(node.element);  
        if (compareToResult < 0) {  
            node.left = remove(e,node.left);  
        } else if (compareToResult > 0) {  
            node.right = remove(e,node.right);  
        }else if (node.left != null && node.right != null){  
            node.element = findMin(node.right).element;  
            node.right = remove(node.element,node.right);  
        }else {  
            node = node.left != null ? node.left : node.right;  
        }  
        return balance(node);  
    }  
  
    private static final int ALLOWED_IMBALANCE = 1;  
  
    private AVLNode<T> balance(AVLNode<T> node){  
        if (node == null) {  
            return node;  
        }  
        if (height(node.left) - height(node.right) > ALLOWED_IMBALANCE) {  
            if (height(node.left.left) >= height(node.left.right)) {  
                node = rotateWithLeftChild(node);  
            }else {  
                node = doubleWithLeftChild(node);  
            }  
        }else {  
            if (height(node.right.right) >= height(node.right.left)) {  
                node = rotateWithRightChild(node);  
            }else {  
                node = doubleWithRightChild(node);  
            }  
        }  
        node.height = Math.max(height(node.left),height(node.right)) + 1;  
        return node;  
    }  
  
    private AVLNode<T> rotateWithLeftChild(AVLNode<T> node){  
        AVLNode<T> newRoot = node.left;  
        node.left = newRoot.right;  
        newRoot.right = node;  
        node.height = Math.max(height(node.left),height(node.right));  
        newRoot.height = Math.max(height(newRoot.left), node.height);  
        return newRoot;  
    }  
  
    private AVLNode<T> rotateWithRightChild(AVLNode<T> node){  
        AVLNode<T> newRoot = node.right;  
        node.right = newRoot.left;  
        newRoot.left = node;  
        node.height = Math.max(height(node.left),height(node.right));  
        newRoot.height = Math.max(node.height,height(newRoot.right));  
        return newRoot;  
    }  
  
    private AVLNode<T> doubleWithLeftChild(AVLNode<T> node){  
        node.left = rotateWithRightChild(node.left);  
        return rotateWithLeftChild(node);  
    }  
  
    private AVLNode<T> doubleWithRightChild(AVLNode<T> node){  
        node.right = rotateWithLeftChild(node.right);  
        return rotateWithRightChild(node);  
    }  
  
}

注意insert方法和remove方法,他和二叉查找树的相同,只是多了一步要进行平衡