
有序表只是一個接口,實現有很多,如: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
