zoukankan      html  css  js  c++  java
  • 堆与左偏树/堆

    堆(heap):满足某结点,不大于或者小于其父节点的值。

    一般来说,常见的堆被默认为 二叉堆

    **堆的常见操作 **: STL : priority_queue p; //stl中的优先队列就是用的堆排

    (1)取得堆顶的元素)

    (2)移除堆顶元素)

    (3)插入堆中)

    假设以 p<<1 , p<<1|1 完全二叉树下标的性质来建堆, 那么堆顶即为 (heap[1])

    在堆中插入元素时,可以先插入到堆尾,然后再向父节点比较进行调整,调节到根结点结束或者符合条件时结束

    再移除堆顶对堆进行调整时,实际上就是一个 插入调整的逆向过程 , 将堆尾放置堆顶,然后向下调节

    Code : 以下是我写的一个小根堆 . 例题 P3378 【模板】堆

    struct Heap{
    	int heap[maxn];//结点	
    	int size = 0;//大小
    	/*
    		push
    		1.插入堆尾
    		2.从堆尾开始调整堆
    		3.根据大/小顶堆的方式调整
    	*/
    	void push(int x){
    		heap[++size] = x;
    		int p = size,q;
    		while(p > 1){
    			q = p >> 1;
    			if(heap[p] >= heap[q]) return ;
    			else swap(heap[p] , heap[q]);
    			p = q;
    		}
    	}
    	int top(){
    		return heap[1];//堆顶
    	}
    	/*
    		1.将堆尾的数值交给堆顶
    		2.从堆顶开始向下调整
    	*/
    	void pop(){
    		int p = 1,q;
    		heap[1] = heap[size--];
    		while(p*2 <= size){
    			q = p << 1;
    			if( q<size && heap[q+1] < heap[q] ) q++; //如果右子点还存在更小的则比较右子点
    			if(heap[p] <= heap[q]) return;
    			swap(heap[p],heap[q]);
    			p = q;
    		}
    	}
    }hp;
    

    我们明确了堆的操作之后,现在又遇到一个新的问题。如果要将 多个堆进行合并应该怎么操作呢?

    把所有的堆重新 放在一个新堆中,那不是(O(nlog^n)) ,这时候要降低合并的复杂度,就需要一种可并数据结构 : 左偏树 (leftist heap/tree)

    由于左偏树不是一个完全二叉树,所以要单独记录 左节点与右节点 下标信息

    (1.)左偏树具有 堆性质 ,即若其满足小根堆的性质,则对于每个结点 (x) ,有 $v_xle v_{lc},v_xle v_{rc} $

    (2.)左偏树具有 左偏性质 ,即对于每个结点 (x) ,有 $dist_{lc}ge dist_{rc} $

    (3.) 对于结点 (x) , (dist_x = dist_{rc}+1)

    操作: (以下操作默认为小根堆)

    (1.)merge 合并

    我们假设 (X) 的根节点小于等于 (Y) 的根节点(否则交换(X,Y)),把 (X) 的根节点作为新树 (Z) 的根节点,剩下的事就是合并 (X) 的右子树和 (Y)

    合并了 (X) 的右子树和 (Y) 之后,(X) 的右子树的距离可能会变大,当 (X) 的右子树 的距离大于 (X) 的左子树的距离时,性质二会被破坏。在这种情况下,我们只须要交换 (X) 的右子树和左子树。

    而且因为 (X) 的右子树的距离可能会变,所以要更新(dist_x = dist_{rc}+1) 这样就合并完了

    Code 例题 P3377 【模板】左偏树(可并堆)

    #include <cstdio>
    #include <iostream>
    const int maxn = 1e5+5;
    using namespace std ;
    #define ls s[x].lc
    #define rs s[x].rc
    struct leftist_tree{
    	int dis, val, lc, rc, rt ;
    }s[maxn] ;
    //路径压缩 
    int find(int x){
    	return s[x].rt == x?x:s[x].rt=find(s[x].rt);//对父结点路径压缩
    }
    int merge(int x,int y){
    	if(!x || !y) return x+y; 
    	if(s[x].val > s[y].val || (s[x].val == s[y].val && x > y)) swap(x,y);
    	rs = merge(rs,y);
    	if(s[ls].dis < s[rs].dis) swap(ls,rs);
    	s[ls].rt = s[rs].rt = s[x].rt = x;
    	s[x].dis = s[rs].dis + 1;
    	return x;
    }
    void pop(int x){
    	s[x].val = -1; 
    	s[ls].rt =  ls;
    	s[ls].rt =  ls;
    	s[x].rt = merge(ls,rs);
    }
    
    int main(){
    	int n,m;
    	cin>>n>>m;
    	s[0].dis = -1;//根的距离定为-1
    	for(int i=1;i<=n;i++){
    		s[i].rt = i, cin>>s[i].val;
    	}
    	for(int i=1;i<=m;i++){
    		int op;
    		cin>>op;
    		if(op==1){
    			int x,y;
    			cin>>x>>y;
    			if(s[x].val == -1 || s[y].val == -1) continue;//其中有不存在的部分
    			int fx = find(x),fy = find(y);
    			if(fx != fy) s[fx].rt = s[fy].rt = merge(fx,fy);
    		}else{
    			int x; 
    			cin>>x;
    			if(s[x].val == -1) cout<<-1<<endl;
    			else {
    				cout<<s[find(x)].val<<endl;
    				pop(find(x));
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    《SQL 基础教程》第五章:复杂查询
    Ruby on Rails Tutorial 第一章笔记
    《Practical Vim》第十章:复制和粘贴
    《Practical Vim》第五章:命令行模式
    《SQL 基础教程》第四章:数据更新
    用户的三次登录验证及进度条
    socket模块开启一个永久性服务
    TCP协议实现切换目录
    爬取好友微信信息
    TCP协议中传输系统命令及上传下载文件
  • 原文地址:https://www.cnblogs.com/Tianwell/p/12855943.html
Copyright © 2011-2022 走看看