zoukankan      html  css  js  c++  java
  • Size Balanced Tree

      Size Balanced Tree(SBT)是目前速度最快的平衡二叉搜索树,且能够进行多种搜索操作,区间操作;和AVL、红黑树、伸展树、Treap类似,SBT也是通过对节点的旋转来维持树的平衡,而相比其他平衡树,SBT维持平衡所需要的额外数据很少,只需要维持以当前节点为根的子树的大小;且SBT的编写复杂度低。因此具有空间优势、速度优势、编写优势。

    SBT的节点

        SBT节点维持很少的额外信息,只需要知道以当前节点为根的子树的大小。

        struct TreeNode{
        int data;
        TreeNode* child[2];
        int size; //以该节点为根的子树的大小(节点的个数)
        TreeNode(int d){
        data = d;
        child[0] = child[1] = NULL;
        size = 1;
        }
        };
    

    SBT的平衡性质

        一棵平衡的SBT树满足如下要求:

        记S[t]为以节点t为根的子树的大小,则对于每个节点T,记其左子节点L, 右子节点R, 左子结点的左子结点LL, 左子结点的右子节点LR, 右子节点的左子结点RL, 右子节点的右子节点RR。 
        则有, S[L] >= max(S[RL], S[RR]), S[R] >= max(S[LL], S[LR]). 
    即任何一个节点的size均大于等于其侄子节点的size。 (侄子节点:定义为一个节点的兄弟节点的两个子节点)

    SBT的维护操作

        一棵平衡的SBT在进行插入和删除之后,可能会不再平衡,此时需要进行维护操作,维护操作需要进行左旋或者右旋操作,旋转操作和其他平衡树的旋转类似(具体见zig-zag旋转) . 
        SBT的非平衡情况分为两类:左子结点和左子结点的侄子节点不平衡或者右子节点和右子节点的侄子节点不平衡。这里以右子节点和右子节点的侄子节点为例,进行Maintain操作。 
        失衡情形1:    S[LL] > S[R] 
    sbt失衡1_1 
    (1)执行 RightRotate(T),得到如下结果 
    sbt失衡1_2 
    (2)此时以T为根的树可能不平衡,递归调用Maintain(T) 
    sbt失衡1_3 
    (3)此时T成为平衡SBT, 再次对L调用Maintain(L)将整体变为平衡SBT 
    sbt失衡1_4

        失衡情形2:    S[LR] > S[R] 
    sbt失衡2_1 
    (1)执行 LeftRotate(L),得到如下结果 
    sbt失衡2_2 
    (2)执行 RightRotate(T),得到如下结果 
    sbt失衡2_3 
    (2)此时以B和R为根的树可能不平衡,递归调用Maintain(B)、Maintain(R) 
    sbt失衡2_4 
    (3)此时T成为平衡SBT, 再次对L调用Maintain(L)将整体变为平衡SBT 
    sbt失衡2_5

        由于Maintain操作是个递归执行的函数,貌似可能会出现无限循环,但实际上,陈启峰在论文里分析过了,Maintain操作的平坦复杂度为O(1)。因此Maintain操作不会出现无法结束的情况。

    SBT的其他操作

        和其他的二叉搜索树一样,SBT支持插入、删除、查找等操作。插入和删除操作可能会破坏SBT的平衡性质,因此,需要在普通的插入和删除之后对节点进行维护,即调用Maintain函数。

    实现(c++)

    #include<iostream>
    using namespace std;
    struct TreeNode{
    	int data;
    	TreeNode* child[2];
    	int size;
    	int count;
    	TreeNode(int d){
    		data = d;
    		child[0] = child[1] = NULL;
    		size = count = 1;
    	}
    	void Update(){
    		size = count;
    		if (child[0]){
    			size += child[0]->size;
    		}
    		if (child[1]){
    			size += child[1]->size;
    		}
    	}
    };
    struct SBT{
    	TreeNode* root;
    	SBT() :root(NULL){};
    	
    	void Rotate(TreeNode*& node, int dir){
    		TreeNode* ch = node->child[dir];
    		node->child[dir] = ch->child[!dir];
    		ch->child[!dir] = node;
    		node = ch;
    	}
    	//返回node节点为根的子树的大小
    	int GetSize(TreeNode* node){
    		if (node)
    			return node->size;
    		return 0; //对于空节点,直接返回0
    	}
    
    	//维持平衡
    	void Maintain(TreeNode*& node, bool flag){
    		TreeNode* R = node->child[1];
    		TreeNode* L = node->child[0];
    		TreeNode* LL = NULL,*LR = NULL,*RL = NULL,*RR = NULL;
    		if (L){
    			LL = L->child[0];
    			LR = L->child[1];
    		}
    		if (R){
    			RL = R->child[0];
    			RR = R->child[1];
    		}
    
    		if (flag == false){ //左边维护
    			if (GetSize(LL) > GetSize(R)){	//失衡情况1 
    				Rotate(node, 0);
    			}
    			else if (GetSize(LR) > GetSize(R)){	//失衡情况2
    				Rotate(L, 1);
    				Rotate(node, 0);
    			}
    			else{
    				return;			//不失衡,直接返回
    			}
    		}
    		else{
    			if (GetSize(RR) > GetSize(L)){
    				Rotate(node, 1);
    			}
    			else if (GetSize(RL) > GetSize(L)){
    				Rotate(R, 0);
    				Rotate(node, 1);
    			}
    			else
    			{
    				return;
    			}
    		}
    		Maintain(node->child[0], false);		//继续将 左子树维持平衡,注意这里不能直接使用L,因为之前进行了旋转操作
    		Maintain(node->child[1], true);			//继续将 右子树维持平衡
    		Maintain(node, true);					//再维持 node
    		Maintain(node, false);		
    	}
    
    	void Insert(TreeNode*& node, int data){
    		if (!node){
    			node = new TreeNode(data);
    			return;
    		}
    		else if (node->data == data){
    			node->count++;
    			node->Update(); //更新本节点以及其祖先节点的size
    			return;
    		}
    		else {
    			int dir = node->data < data;
    			Insert(node->child[dir], data);
    
    			Maintain(node, ! dir); 
    			//如果新插入的数据 小于 当前节点的数据,则被插入左子树,
    			//此时左子树的左右子节点的size可能大于右子节点,因此Maintain(x, false)
    
    			node->Update();
    		}				
    	}
    
    	void Delete(TreeNode*& node, int w){
    		if (!node){
    			return;
    		}
    		if (node->data == w){
    			if (node->child[0] && node->child[1]){
    				TreeNode* succ = node->child[1];
    				while (succ->child[0]){
    					succ = succ->child[0];
    				}
    				node->data = succ->data;
    				succ->data = w;
    				Delete(node, w);
    			}
    			else{
    				TreeNode* tmp_node = NULL;
    				if (node->child[0])
    					tmp_node = node->child[0];
    				else
    					tmp_node = node->child[1];
    				delete node;
    				node = tmp_node;
    			}
    		}
    		Maintain(node, false);
    		node->Update();
    	}
    
    };

     参考: 
    SBT-陈启峰

  • 相关阅读:
    编程之美1的个数统计
    组合以及可以重复的排列
    Swing之图书管理系统
    经常熬夜的人一定要看
    利用SharePoint Designer开发可循环工作流
    SharePoint Software DownLoad Address 微软软件 下载地址
    日期验证 正则表达式
    时间验证 正则表达式
    Reporting Services SharePoint AddIn Setup Problems
    char、varchar、text和nchar、nvarchar、ntext的区别
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4724288.html
Copyright © 2011-2022 走看看