zoukankan      html  css  js  c++  java
  • 0x46蓝书习题:普通平衡树

    Treap/平衡二叉树


    蓝书习题:普通平衡树

    这道题是一道平衡树模板题,可以用多种解法,这里用最简单的Treap,下面简单说一下各种操作的思路

    • 添加
      当要添加一个值时,先判断所要加入的以p为根节点的子树是否为空,为空添加新的节点:New(val)。 当然平衡树,当加入新节点后,子节点dat变得大于a[p].dat时,要旋转,左旋右旋见代码,好理解。

      void Insert(int &p,int val){
      	if(!p){
      		p=New(val);
      		return;
      	}
      	if(val==a[p].val){
      		a[p].cnt++,Updata(p);
      		return;
      	}
      	if(val<a[p].val){
      		Insert(a[p].l,val);
      		if(a[p].dat<a[a[p].l].dat) zig(p);
      	}
      	else{
      		Insert(a[p].r,val);
      		if(a[p].dat<a[a[p].r].dat) zag(p);
      	}
      	Updata(p);
      }
      
    • 删除 :
      平衡树删除要容易,就是将要删除的节点不停旋转到叶子上,再删掉。见代码

      void Remove(int &p,int val){
      	if(p==0) return;
      	if(val==a[p].val){
      		if(a[p].cnt>1){		//val这个值有重复,直接减少cnt即可
      			a[p].cnt--,Updata(p);
      			return;
      		}
      		if(a[p].l||a[p].r){		//p有子树,不是叶子
      			if(a[p].r==0||a[a[p].l].dat > a[a[p].r].dat) zig(p),Remove(a[p].r,val);		//左子树长于右子树,向右旋
      			else zag(p),Remove(a[p].l,val);
      			Updata(p);
      		}
      		else p=0;				//p是叶子直接删除
      		return;
      	}
      	if(val<a[p].val) Remove(a[p].l,val);
      	else Remove(a[p].r,val);
      	Updata(p);
      
      }
      
    • 输出一个值的排名:
      根据平衡二叉树的性质,最左边的值最小,那么如果我知道p的左子树siz,那么p的排名就是 a[a[p].l].siz+1,根据这一点。如果val<a[p].val,说明val在左子树中,递归处理左子树。当val>a[p].val,时,递归右子树时返回值要加上p左子树的siz和p的重复次数cnt这是由于递归右子树返回值,表示的是val在右子树中的排名,但是易知,val大于左子树和p的val。

      int GetRankByVal(int p,int val){
      	if(p==0) return 0;
      	if(val==a[p].val) return a[a[p].l].siz+1;
      	else if(val<a[p].val) return GetRankByVal(a[p].l,val);
      	return GetRankByVal(a[p].r,val)+a[a[p].l].siz+a[p].cnt;
      }
      
      
    • 输出某排名的值:
      这个看代码吧。

      int GetvalByRank(int p,int rak){
      	if(p==0) return INF;
      	if(a[a[p].l].siz>=rak) return GetvalByRank(a[p].l,rak);	
      	if(a[a[p].l].siz+a[p].cnt>=rak) return a[p].val;		
      	return GetvalByRank(a[p].r,rak-a[a[p].l].siz-a[p].cnt);	
      }
      //2.左子树siz>rak,说明排名为rak的节点在左子树中
      //3.左子树的siz+p点的cnt>rank,而且左子树的siz<rak,说明排名为rak的就是p点的val
      //4.其他就,递归处理右子树,注意要减去左子树siz和p点的重复值
      
    • 找前驱/后继:
      前驱是比val小的最大值 ,后继是比val大的最小值**

      int GetPre(int val){
      	int ans=1;
      	int p=root;
      	while(p){
      		if(a[p].val==val){
      			if(a[p].l>0){
      				p=a[p].l;
      				while(a[p].r>0) p=a[p].r;
      				ans=p;
      			}
      			break;
      		}
      		if(a[p].val<val&&a[p].val>a[ans].val) ans=p;
      		p= val<a[p].val ? a[p].l : a[p].r;
      	}
      	return a[ans].val;
      }
      
      int GetNext(int val){
      	int ans=2;
      	int p=root;
      	while(p){
      		if(val==a[p].val){
      			if(a[p].r){
      				p=a[p].r;
      				while(a[p].l>0)p=a[p].l;
      				ans=p;
      			}
      			break;
      		}
      		if(a[p].val>val&&a[p].val<a[ans].val) ans=p;
      		p= val<a[p].val ? a[p].l : a[p].r;
      	}
      	return a[ans].val;
      }
      

    完整代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MA=1e5+5;
    const int INF=0x3f3f3f3f;
    
    struct Treap
    {
    	int l,r;
    	int val,dat;	//val:权值,dat:随机值,用来判断左右旋
    	int cnt,siz;	//cnt:记录这个权值的重复次数,siz:子树大小
    }a[MA];
    int tot,n,root;
    
    int New(int val){
    	a[++tot].val=val;
    	a[tot].siz=a[tot].cnt=1;
    	a[tot].dat=rand();
    	return tot;
    }
    
    void Updata(int p){
    	a[p].siz=a[a[p].l].siz+a[a[p].r].siz+a[p].cnt;
    }
    
    void Build(){
    	New(-INF),New(INF);
    	root=1,a[1].r=2;
    	Updata(root);
    }
    
    void zig(int &p){
    	int q=a[p].l;
    	a[p].l=a[q].r,a[q].r=p;
    	p=q;
    	Updata(a[p].r);
    	Updata(p);
    }
    
    void zag(int &p){
    	int q=a[p].r;
    	a[p].r=a[q].l,a[q].l=p;
    	p=q;
    	Updata(a[p].l);
    	Updata(p);
    }
    
    void Insert(int &p,int val){
    	if(!p){
    		p=New(val);
    		return;
    	}
    	if(val==a[p].val){
    		a[p].cnt++,Updata(p);
    		return;
    	}
    	if(val<a[p].val){
    		Insert(a[p].l,val);
    		if(a[p].dat<a[a[p].l].dat) zig(p);
    	}
    	else{
    		Insert(a[p].r,val);
    		if(a[p].dat<a[a[p].r].dat) zag(p);
    	}
    	Updata(p);
    }
    
    void Remove(int &p,int val){
    	if(p==0) return;
    	if(val==a[p].val){
    		if(a[p].cnt>1){		//val这个值有重复,直接减少cnt即可
    			a[p].cnt--,Updata(p);
    			return;
    		}
    		if(a[p].l||a[p].r){		//p有子树,不是叶子
    			if(a[p].r==0||a[a[p].l].dat > a[a[p].r].dat) zig(p),Remove(a[p].r,val);		//左子树长于右子树,向右旋
    			else zag(p),Remove(a[p].l,val);
    			Updata(p);
    		}
    		else p=0;				//p是叶子直接删除
    		return;
    	}
    	if(val<a[p].val) Remove(a[p].l,val);
    	else Remove(a[p].r,val);
    	Updata(p);
    
    }
    
    int GetRankByVal(int p,int val){
    	if(p==0) return 0;
    	if(val==a[p].val) return a[a[p].l].siz+1;
    	else if(val<a[p].val) return GetRankByVal(a[p].l,val);
    	return GetRankByVal(a[p].r,val)+a[a[p].l].siz+a[p].cnt;
    }
    
    int GetvalByRank(int p,int rak){
    	if(p==0) return INF;
    	if(a[a[p].l].siz>=rak) return GetvalByRank(a[p].l,rak);	//左子树siz>rak,说明排名为rak的节点在左子树中
    	if(a[a[p].l].siz+a[p].cnt>=rak) return a[p].val;		//左子树的siz+p点的cnt>rank,而且左子树的siz<rak,说明排名为rak的就是p点的val
    	return GetvalByRank(a[p].r,rak-a[a[p].l].siz-a[p].cnt);	//其他就,递归处理右子树,注意要减去左子树siz和p点的重复值
    }
    
    int GetPre(int val){
    	int ans=1;
    	int p=root;
    	while(p){		//子树为处理完
    		if(a[p].val==val){		//找到val的节点
    			if(a[p].l>0){		//如果p有左子树,那就让p->左子树,然后一直向右子树移动,最后得到的p就是小于val的最大值
    				p=a[p].l;
    				while(a[p].r>0) p=a[p].r;
    				ans=p;
    			}
    			break;
    		}
    		if(a[p].val<val&&a[p].val>a[ans].val) ans=p;	//用p的值更新ans,找小于val的最大值
    		p= val<a[p].val ? a[p].l : a[p].r;				//递归合适的子树
    	}
    	return a[ans].val;
    }
    
    int GetNext(int val){
    	int ans=2;
    	int p=root;
    	while(p){
    		if(val==a[p].val){
    			if(a[p].r){
    				p=a[p].r;
    				while(a[p].l>0)p=a[p].l;
    				ans=p;
    			}
    			break;
    		}
    		if(a[p].val>val&&a[p].val<a[ans].val) ans=p;
    		p= val<a[p].val ? a[p].l : a[p].r;
    	}
    	return a[ans].val;
    }
    
    
    int main()
    {
    	Build();
    	scanf("%d",&n);
    	while(n--){
    		int opt,x;
    		scanf("%d%d",&opt,&x);
    		if(opt==1) Insert(root,x);
    		else if(opt==2) Remove(root,x);
    		else if(opt==3)	printf("%d
    ",GetRankByVal(root,x)-1);
    		else if(opt==4)	printf("%d
    ",GetvalByRank(root,x+1));
    		else if(opt==5)	printf("%d
    ",GetPre(x));
    		else if(opt==6)	printf("%d
    ",GetNext(x));
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Java接口的实现理解
    RDP |SSH |VNC简介
    关于彻底理解cookie,session,token的摘录,生动形象
    7.Reverse Integer&#160;&#160;
    1.Two Sum
    图形化编程娱乐于教,Kittenblock实例,播放与录制声音
    图形化编程娱乐于教,Kittenblock实例,一只思考的变色猫
    内存条性能参数查询(任务8)
    任务8选配内存,重点解读兼容与接口的搭配技术,解读选配内存的过程
    图形化编程娱乐于教,Kittenblock实例,键盘操控角色
  • 原文地址:https://www.cnblogs.com/A-sc/p/12244363.html
Copyright © 2011-2022 走看看