码迷,mamicode.com
首页 > 其他好文 > 详细

左偏树(Leftist Heap/Tree)简介及代码

时间:2016-04-19 00:23:56      阅读:161      评论:0      收藏:0      [点我收藏+]

标签:

左偏树是一种常用的优先队列(堆)结构。与二叉堆相比,左偏树可以高效的实现两个堆的合并操作。

左偏树实现方便,编程复杂度低,而且有着不俗的效率表现。

它的一个常见应用就是与并查集结合使用。利用并查集确定两个元素是否在同一集合,利用左偏树确定某个集合中优先级最高的元素。

 

技术分享
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 template <class T>
  6 struct HeapNode
  7 {
  8     typedef HeapNode<T> Node;
  9     Node* lch;
 10     Node* rch;
 11     T val;
 12     int dist;
 13     
 14     HeapNode(const T& _val):lch(0),rch(0),val(_val),dist(0) {}
 15     
 16     void clear()
 17     {
 18         if(lch) lch->clear();
 19         if(rch) rch->clear();
 20         delete this;
 21     }
 22 };
 23 
 24 template <class T,class Comp>
 25 struct LeftistHeap
 26 {
 27     typedef HeapNode<T> Node;
 28     typedef LeftistHeap<T,Comp> Heap;
 29     
 30     Node* root;
 31     Comp cmp;
 32     
 33     LeftistHeap():root(0) {}
 34     ~LeftistHeap()
 35     {
 36         clear();
 37     }
 38     
 39     void clear()
 40     {
 41         if(root) root->clear();
 42         root=0;
 43     }
 44     
 45     Node* merge(Node* A,Node* B)
 46     {
 47         if(!A) return B;
 48         if(!B) return A;
 49         
 50         if(cmp(B->val,A->val)) std::swap(A,B);
 51         A->rch=merge(B,A->rch);
 52         
 53         if(!A->lch || A->rch->dist > A->lch->dist) 
 54             std::swap(A->lch,A->rch);
 55         A->dist = (A->rch) ? A->rch->dist + 1 : 0 ;
 56         
 57         return A;
 58     }
 59     
 60     void push(const T& _val)
 61     {
 62         Node* nNode=new Node(_val);
 63         root=merge(root,nNode);
 64     }
 65     
 66     Heap& operator << (const T& _val)
 67     {
 68         push(_val);
 69         return *this;
 70     }
 71     
 72     T top()
 73     {
 74         return root->val;
 75     }
 76     
 77     void pop()
 78     {
 79         Node* temp=root;
 80         root=merge(temp->lch,temp->rch);
 81         delete temp;
 82     }
 83     
 84     Heap& operator >> (T& _dest)
 85     {
 86         _dest=top();
 87         pop();
 88         return *this;
 89     }
 90     
 91     void merge(Heap& _other)
 92     {
 93         this->root=merge(this->root,_other.root);
 94         _other.root=0;
 95     }
 96     
 97     bool empty()
 98     {
 99         return root==0;
100     }
101 };
Leftist Heap

 

定义左偏树节点的“距离”(dist)为从其右子树开始,一直向右走的路径总长。特别地,若某个节点没有右孩子,其dist值为0。

树中的每个节点都必须满足左孩子的dist值不小于右孩子(如果有的话)的dist值。

和大多数可并堆一样,左偏树的核心操作就是合并(Merge)操作。

(以下伪代码以小根堆为例,节点的数据域记为val)

function merge(Node* A,Node* B)

if(A和B中某一个为空) return 另一个   //特判,同时也是递归终止的条件

交换A和B(如果需要的话),使得A的val小于B的val

A->rch = merge(B,A->rch)

if(A的左孩子的dist小于右孩子的dist或A的左孩子不存在) 交换A的左、右孩子

根据A的右孩子更新A的dist

return A

实现细节详见代码。

有了合并操作,其他的也就水到渠成了:

插入(push):建立一个新节点,然后把它视为一个左偏树,将其与已有的合并。

删除(pop):删除其根节点,合并原先根节点的左右孩子。

 

附一道左偏树+并查集的练习题:

技术分享
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 
  5 template <class T>
  6 struct HeapNode
  7 {
  8     typedef HeapNode<T> Node;
  9     Node* lch;
 10     Node* rch;
 11     T val;
 12     int dist;
 13     
 14     HeapNode(const T& _val):lch(0),rch(0),val(_val),dist(0) {}
 15     
 16     void clear()
 17     {
 18         if(lch) lch->clear();
 19         if(rch) rch->clear();
 20         delete this;
 21     }
 22 };
 23 
 24 template <class T,class Comp>
 25 struct LeftistHeap
 26 {
 27     typedef HeapNode<T> Node;
 28     typedef LeftistHeap<T,Comp> Heap;
 29     
 30     Node* root;
 31     Comp cmp;
 32     
 33     LeftistHeap():root(0) {}
 34     ~LeftistHeap()
 35     {
 36         clear();
 37     }
 38     
 39     void clear()
 40     {
 41         if(root) root->clear();
 42         root=0;
 43     }
 44     
 45     Node* merge(Node* A,Node* B)
 46     {
 47         if(!A) return B;
 48         if(!B) return A;
 49         
 50         if(cmp(B->val,A->val)) std::swap(A,B);
 51         A->rch=merge(B,A->rch);
 52         
 53         if(!A->lch || A->rch->dist > A->lch->dist) 
 54             std::swap(A->lch,A->rch);
 55         A->dist = (A->rch) ? A->rch->dist + 1 : 0 ;
 56         
 57         return A;
 58     }
 59     
 60     void push(const T& _val)
 61     {
 62         Node* nNode=new Node(_val);
 63         root=merge(root,nNode);
 64     }
 65     
 66     Heap& operator << (const T& _val)
 67     {
 68         push(_val);
 69         return *this;
 70     }
 71     
 72     T top()
 73     {
 74         return root->val;
 75     }
 76     
 77     void pop()
 78     {
 79         Node* temp=root;
 80         root=merge(temp->lch,temp->rch);
 81         delete temp;
 82     }
 83     
 84     Heap& operator >> (T& _dest)
 85     {
 86         _dest=top();
 87         pop();
 88         return *this;
 89     }
 90     
 91     void merge(Heap& _other)
 92     {
 93         this->root=merge(this->root,_other.root);
 94         _other.root=0;
 95     }
 96     
 97     bool empty()
 98     {
 99         return root==0;
100     }
101 };
102 
103 #include <functional>
104 
105 const int maxN=100005;
106 
107 int N,M;
108 int idx[maxN];
109 
110 int father(int x)
111 {
112     return idx[x]==x ? x : idx[x]=father(idx[x]) ;
113 }
114 
115 LeftistHeap<int,std::greater<int> > heap[maxN];
116 
117 void init()
118 {
119     for(int i=0;i<maxN;i++) heap[i].clear();
120     for(int i=0;i<maxN;i++) idx[i]=i;
121 }
122 
123 bool solve()
124 {
125     init();
126     
127     if(scanf("%d",&N)==EOF) return false;
128     for(int i=1;i<=N;i++)
129     {
130         int s; scanf("%d",&s);
131         heap[i].push(s);
132     }
133     
134     scanf("%d\n",&M);
135     while(M--)
136     {
137         int mk1,mk2;
138         scanf("%d%d",&mk1,&mk2);
139         
140         int f1=father(mk1);
141         int f2=father(mk2);
142         if(f1==f2) 
143         {
144             printf("-1\n"); 
145             continue;
146         }
147 
148         int s1,s2;
149         heap[f1]>>s1;
150         heap[f2]>>s2;
151         
152         if(f1<f2)
153         {
154             idx[f2]=f1;
155             heap[f1].merge(heap[f2]);
156             if(heap[f1].empty()) printf("%d\n",std::max(s1,s2)>>1);
157             else printf("%d\n",std::max(heap[f1].top(),std::max(s1,s2)>>1));
158             heap[f1] << (s1>>1) << (s2>>1);
159         }
160         else
161         {
162             idx[f1]=f2;
163             heap[f2].merge(heap[f1]);
164             if(heap[f2].empty()) printf("%d\n",std::max(s1,s2)>>1);
165             else printf("%d\n",std::max(heap[f2].top(),std::max(s1,s2)>>1));
166             heap[f2] << (s1>>1) << (s2>>1);
167         }
168     }
169     
170     return true;
171 }
172 
173 int main()
174 {
175     while(solve());
176     return 0;
177 }
Problem:ZOJ P2334

 

左偏树(Leftist Heap/Tree)简介及代码

标签:

原文地址:http://www.cnblogs.com/Onlynagesha/p/5406403.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!