有序表

有序表只是一个接口,实现有很多,如: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

浙公网安备 33010602011771号