debug了好久才写出来的平衡树,代码里有注释可以方便理解
可能有代码冗余或者有误的地方,欢迎指出错误
(好像因为是看了别人的博客,所以混淆了结点高度这个概念,我先解释一下我代码里的Height=当前结点到叶子结点的经过的边+1)
很抱歉影响了你的观看体验!
推荐一个网站(数据结构操作可视化)
(我这里给出的是平衡树界面)
AVL Tree Visualzation
(我debug就是看着可视化的树查错的)
还是讲下一些关键操作吧:
1、左旋(基操)
左旋不会改变中序遍历
2、右旋(基操)
右旋不会改变中序遍历
3、LL(左左)
用下图举例的话,就是插入操作为在结点1的左孩子的左孩子处插入结点3,这样会使得平衡度== 2
就是对结点1进行一个右旋
4、RR(右右,在结点1的右孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是对结点1进行一个左旋
5、LR(左右,在结点1的左孩子的右孩子处插入结点3,这样会使得平衡度==-2)
就是先对结点2右旋,再对结点1左旋
6、RL(开摆!你们自己对照着LR比划去罢)
7、计算平衡度
首先得到当前结点的左右孩子的高度(高度的更新是在回溯的时候进行的,所以可以保证子结点的高度已经是正确的了)
然后就是return leftHeight-rightHeight了
8、找到左子树的最大值
删除一个左右子结点都存在的结点时,需要用左子树的最大结点或右子树的最小结点来替代当前结点(我的代码里用的是左子树的最大结点)
其实左子树的最大结点就是左子树的最右下角的结点
下面开始放代码(欢迎喷我代码的问题)
import java.util.linkedList; public class AVLTree,V> { //树结点类 private class Node{ //键 private K key; //值 private V value; //高度 private Integer height; //左右父 private Node left,right,parent; public Node(K key,V value) { this.key=key; this.value = value; height=1; } private int getBalanceFactor(){ int leftHeight=left==null?0:left.getHeight(); int rightHeight=right==null?0:right.getHeight(); return leftHeight-rightHeight; } private int getHeight(){ return height; } private void setHeight(Integer height){ this.height=height; } private void updateHeight(){ int leftHeight=left==null?0:left.getHeight(); int rightHeight=right==null?0:right.getHeight(); height=1+Math.max(leftHeight,rightHeight); } private K getKey() { return key; } private void setKey(K key) { this.key = key; } private V getValue() { return value; } private void setValue(V value) { this.value = value; } private void setKeyAndValue(K key,V value){ setKey(key); setValue(value); } private Node getLeft() { return left; } private void setLeft(Node left) { this.left = left; } private Node getRight() { return right; } private void setRight(Node right) { this.right = right; } private Node getParent() { return parent; } private void setParent(Node parent) { this.parent = parent; } @Override public String toString() { return "Node{" + "key=" + key + ", value=" + value + '}'; } } //结点个数 private int size; //根结点 private Node root; public AVLTree(){ clear(); } private Node leftRotate(Node node1){ Node node2=node1.getRight(); node1.setRight(node2.getLeft()); node1.setParent(node2); if(node1.getRight()!=null){ node1.getRight().setParent(node1); } node2.setLeft(node1); node1.updateHeight(); node2.updateHeight(); return node2; } private Node rightRotate(Node node1){ Node node2=node1.getLeft(); node1.setLeft(node2.getRight()); node1.setParent(node2); if(node1.getLeft()!=null){ node1.getLeft().setParent(node1); } node2.setRight(node1); node1.updateHeight(); node2.updateHeight(); return node2; } private Node addNode(Node root,K key,V value){ if(root==null){ //直接返回结点,让父节点更新子结点,同时不走下面的reBalance return new Node(key,value); } int cmpRes=key.compareTo(root.getKey()); //小于,看左子树 if(cmpRes<0){ //这里需要更新左子结点 Node leftChild=addNode(root.getLeft(),key,value); root.setLeft(leftChild); leftChild.setParent(root); }else if(cmpRes>0){//大于,看右子树 //这里需要更新右子结点 Node rightChild=addNode(root.getRight(),key,value); root.setRight(rightChild); rightChild.setParent(root); }else{ //key值相等就认为是更新 root.setValue(value); } return reBalance(root); } private Node reBalance(Node root){ int bf=root.getBalanceFactor(); //左子树高 if(bf>1){ Node leftChild=root.getLeft(); int childBF=leftChild.getBalanceFactor(); if(childBF>0){ //右旋 root=rightRotate(root); } else{ //先左旋再右旋 //注意左旋是以2为根结点 root.setLeft(leftRotate(root.getLeft())); root=rightRotate(root); } }else if(bf<-1){//右子树高 Node rightChild=root.getRight(); int childBF=rightChild.getBalanceFactor(); if(childBF>0){ //注意右旋是以2为根结点 root.setRight(rightRotate(root.getRight())); root=leftRotate(root); } else{ //左旋 root=leftRotate(root); } } //这里可以在回溯时更新路径上的所有结点的高度(已平衡的结点会直接来这里更新高度) root.updateHeight(); return root; } private Node findNode(Node root,K key){ if(root==null){ return null; } int cmpRes=key.compareTo(root.getKey()); if(cmpRes<0){ return findNode(root.getLeft(),key); }else if(cmpRes>0){ return findNode(root.getRight(),key); }else{ return root; } } private Node replaceNode(Node root,Node deletedNode){ if(root.getRight()==null){ deletedNode.setKeyAndValue(root.getKey(),root.getValue()); return null; } root.setRight(replaceNode(root.getRight(),deletedNode)); root.updateHeight(); return reBalance(root); } private Node deleteNode(Node root,K key){ if(root==null){ return null; } int cmpRes=key.compareTo(root.getKey()); if(cmpRes<0){ //找左子树 Node leftChild=deleteNode(root.getLeft(), key); root.setLeft(leftChild); if(leftChild!=null){ // leftChild.setParent(root); } }else if(cmpRes>0) { //找右子树 Node rightChild=deleteNode(root.getRight(), key); root.setRight(rightChild); if(rightChild!=null){ rightChild.setParent(root); } }else{ //找到要删除的结点了 if(root.getLeft()==null&&root.getRight()==null){//叶子结点 if(root.getParent()==null){ setRoot(null); } return null; }else if(root.getLeft()==null){//左子树为空,右子树不为空 root.getRight().setParent(root.getParent()); if(root.getParent()==null){ setRoot(root.getRight()); } return root.getRight(); } else if(root.getRight()==null){//右子树为空,左子树不为空 root.getLeft().setParent(root.getParent()); if(root.getParent()==null){ setRoot(root.getLeft()); } return root.getLeft(); } else{//有两个儿子结点 Node leftChild=root.getLeft(); if(leftChild.getRight()==null){ //直接把左子树根节点置为null root.setKeyAndValue(leftChild.getKey(),leftChild.getValue()); root.setLeft(null); leftChild.setParent(null); }else{ //用左子树的最大节点覆盖被删除的结点(可视为删除),并更新左孩子 Node newLeft=replaceNode(leftChild,root); root.setLeft(newLeft); if(newLeft!=null){ newLeft.setParent(root); } } } } root.updateHeight(); return reBalance(root); } public void put(K key,V value){ if(root==null){ setRoot(new Node(key,value)); } else{ setRoot(addNode(root,key,value)); //因为这些操作不涉及根结点的处理,所以需要单独写一个根结点的父节点置空 getRoot().setParent(null); } size++; } public V get(K key){ if(key==null){ throw new NullPointerException("key cannot be null"); } Node node=findNode(root,key); if(node==null){ return null; } return node.getValue(); } public void remove(K key){ if(key==null){ throw new NullPointerException("key cannot be null"); } setRoot(deleteNode(root,key)); if(getRoot()!=null){ getRoot().setParent(null); } size--; } public void clear(){ size=0; root=null; } public boolean isEmpty(){ return size==0; } public int size() { return size; } private void setSize(int size) { this.size = size; } private Node getRoot() { return root; } private void setRoot(Node root) { this.root = root; } private StringBuilder preOrderTraverse(Node root,StringBuilder sb){ if(root==null){ //null结点为# sb.append("# "); return sb; } sb.append(root.getKey()).append(" "); sb=preOrderTraverse(root.getLeft(),sb); sb=preOrderTraverse(root.getRight(),sb); return sb; } @Override public String toString(){ StringBuilder sb=new StringBuilder(); sb.append("preOrderTraverseSequenceOfKey is ["); sb=preOrderTraverse(root,sb); sb.deleteCharAt(sb.length()-1); sb.append("]"); return sb.toString(); } } class AVLTreeDemo{ public static void main(String[] args) { AVLTree tree=new AVLTree<>(); linkedList list=new linkedList<>(); for(int i=0;i<=10;i++){ list.add(i); } list.add(-1); System.out.println(list); System.out.println("----------put测试----------"); System.out.println("输出的是前序遍历的key序列:"); for(Integer it:list){ tree.put(it,it); System.out.println(tree); } System.out.println("------------get测试--------------"); System.out.println(tree.get(2)); System.out.println("------------delete测试--------------"); tree.remove(2); System.out.println(tree); tree.remove(1); System.out.println(tree); tree.remove(-1); System.out.println(tree); tree.remove(0); System.out.println(tree); tree.put(11,11); System.out.println(tree); tree.remove(8); System.out.println(tree); tree.remove(9); tree.remove(11); System.out.println(tree); tree.remove(3); System.out.println(tree); tree.remove(6); tree.remove(4); System.out.println(tree); } }



