有序表

 

 

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

 

posted @ 2021-10-10 14:29  sherry001  阅读(509)  评论(0编辑  收藏  举报