有序表只是一個接口,實現有很多,如:AVL,SB,RedBlackTree ,skipTable
AVL,SB,RedBlackTree是基於搜索二叉樹設計出來的,增刪改查是O(logn)
無重復節點,改成有重復節點可以這么設計:K,List<V>
二叉搜索樹的增刪改查:
查:val 大->root.right,val小->root.left,== return
增:查,查到空掛上
改:查到改值
刪:分下面幾種情況
前提是先查,查到后判段這個節點的情況
1、無左無右,直刪
2、有左無右(讓左該子去替它的環境)或有右無左,
3、有左有右
可以有兩種方法:1:讓左孩子的最右節點去替,2:讓右孩子的最左節點去替
各個有序表的實現增刪改查都一樣,只是調整平衡不一樣
二叉搜索樹的增刪改:
public class AbstractBinarySearchTree { public static class Node{ public Integer value; public Node parent; public Node left; public Node right; public Node(Integer value,Node parent,Node left,Node right){ this.value=value; this.parent=parent; this.left=left; this.right=right; } public boolean isLeaf(){ return left==null && right==null; } } Node root; int size; public Node search(int element){ Node node=root; while(node!=null && node.value!=null && node.value!=element){ if(element> node.value){ node=node.right; }else { node=node.left; } } return node; } public void update(int element){ Node node=search(element); if(node==null){ return; } node.value=element; } protected Node createNode(int element,Node parent,Node left,Node right){ return new Node(element,parent,left,right); } public Node insert(int element){ if(root==null){ root=createNode(element,null,null,null); size++; return root; } //search Node insertParentNode=null; Node searchTemNode=root; while(searchTemNode!=null && searchTemNode.value!=null){ insertParentNode=searchTemNode;//最后一個不空的節點就是待加節點的父 if(element> searchTemNode.value){ searchTemNode=searchTemNode.right; }else{ searchTemNode=searchTemNode.left; } } Node newNode=createNode(element,insertParentNode,null,null); if(insertParentNode.value>newNode.value){ insertParentNode.left=newNode; }else{ insertParentNode.right=newNode; } size++; return newNode; } public Node delete(int element){ Node deleteNode=search(element); if(deleteNode==null){ return null; } return delete(deleteNode); } protected Node delete(Node deleteNode){ if(deleteNode==null){ return null; } Node nodeToReturn=null; if(deleteNode.left==null){//綜合了無左無右和有右無左的情況 //transplant(a,b) b去替a的環境,a斷聯,把b返回 nodeToReturn=transplant(deleteNode,deleteNode.right); }else if(deleteNode.right==null){//綜合了無左無右和有左無右的情況 nodeToReturn=transplant(deleteNode,deleteNode.left); }else{ //找到右孩子的最左節點 Node successorNode=getMinimun(deleteNode.right); if(successorNode.parent!=deleteNode){ transplant(successorNode,successorNode.right); successorNode.right=deleteNode.right; successorNode.right.parent=successorNode; } transplant(deleteNode,successorNode); successorNode.left=deleteNode.left; successorNode.left.parent=successorNode; nodeToReturn=successorNode; } size--; return nodeToReturn; } }
擴展,可以改出很多API,TeeMap 中沒有如下API,但可以通過SB樹改
1:找到<=num離它最近的,>=num離它最近的,二分后大於往右,小於往左,
標記最后一個比它小的,和最后一個比它大的
2:找最小的第100個key是什么
二叉搜索樹通過增刪改后,搜索的效率有可能會退化成O(n)
AVL,SB,RBT都是在維持平衡性
AVL樹左右子樹高度差不超過1
如何維持平衡性?左旋,右旋
調整策略:4種違規
LL型:左邊的左孩子過多->右旋
RR型:右邊的右孩子過多->左旋
LR型:左邊的右孩子過多->針對X想讓它成於頭,對Y先左旋,再整顆樹右旋
RL型:右邊的左孩子過多->先右旋,再左旋
private void avlRebalance(Node node,Node parent){ int lH=node.left==null?-1:node.left.height; int rH=node.right==null?-1:node.right.height; int nodeBalance=rH-lH; if(nodeBalance==2){ if(node.right.right!=null){ node=avlRotaleft(node); break; }else{ node=doubleRotateRightLeft(node); break; } }else if(nodeBalance==-2){ if(node.left.left!=null){ node=avlRotateRight(node); break; }else{ node=doubleRotateLeftRight(node); break; } }else{ updateHeight(node); } }
SB樹:API好改
AVL樹維持了一個高度信息,高度差不能超過1,
而SB樹是要求叔叔節點數不能小於任何侄子的節點數,如此規定后,左樹子樹的節點數不會超過2倍多
調整策略和AVL樹一樣,4種違規
1、LL型:對X來說,是R違規了,a的節點數比R多
2、LR型:對X來說,是R違規了,b的節點數比R多
3、RR型:對X來說,是L違規了,d的節點數比R多
4、RL型:對X來說,是L違規了,c的節點數比R多
為什么這4種只有一種違規,因為插入和刪除都是一個個插入和刪除的,插刪后都是從下往上查平衡,所以不會出現多種違規的情況
在這4種情況下如何調整?maintain
LL:X右旋,誰的子樹發生了變化,還要miantain一下
RR:X左旋,誰的子樹發生了變化,還要miantain一下
LR:先對L左旋,再對X右旋,再m(L),m(x),m(b)
RL:同理
SB左右旋后,還有遞歸行為,但時間復雜度進了是O(logn)
在刪除節點時可以不調,在插入時統一調
平衡指標只能是size(不同的key的數量),每個節點收了多少個節點用times, size,times是兩個不同的字段
不同的key, 一共的key