今天講的堆,簡單記錄一下:
堆,是一棵完全二叉樹,因此設當前節點編號為 i,則其父結點編號為 i/2,左兒子編號為 2*i,右兒子編號為 2i+1
堆有兩種比較特別:
一種是大根堆,一種是小根堆
由他們樸實的名字可知,大根堆就是每個節點都小於父親節點的堆,小根堆反之。
關於堆的操作有兩個比較常用的,一個是put(),另一個是get()。
put()演算法介紹:
put操作就是往堆裡插入一個元素,我們如果硬插的話會有悖於堆的性質,所以要用能夠維護堆的方法:
拿小根堆舉例:在堆的末尾加入元素,並將該節點當成當前節點。然後比較當前節點與他的父親的大小,如果當前節點小於其父節點。就將這兩個節點位置互換,並持續此操作,直到當前節點不小於其父節點(等於不等於的都可以),以維護小根堆的性質,大根堆亦然。
程式碼如下:
1 void put(int d)//展示的是小根堆的操作,大根堆亦然 2 { 3 int son,pa; 4 heap[++heap_size]=d; 5 son=heap_size; 6 while(son>1)//到頭了就不能繼續了 7 { 8 pa=son>>2; 9 if(heap[son]>=heap[pa])//如果兒子本來就比他爹大,滿足性質,直接break; 10 { 11 break; 12 } 13 swap(heap[son],heap[pa]);//將爹與兒子進行交換 14 son=pa;//讓兒子的編號變成他爹的,再進行下一步操作 15 } 16 }
當然,使用C++的標準模板庫STL也是可以的:
1 #include<algorithm>//別忘了加STL的標頭檔案 2 void put(int d) 3 { 4 heap[++heap_size]=d; 5 push_heap(heap+1,heap+heap_size+1,greater<int>());//小根堆 6 push_heap(heap+1,heap+heap_size+1);//大根堆 7 }
get()演算法介紹:
put就是從堆中取出並刪除元素的操作:
拿小根堆舉例:
先將堆的根節點與堆尾元素互換,接著將對堆中元素的個數減1.
接著將當前的根節點視作pa比較他的兩個兒子的大小(如果他有兩個兒子)並選出較小的那個與父親互換,一直持續此操作,直到又變成一個新的小根堆。大根堆亦然。
程式碼如下:
1 int get()//This is 小根堆 2 { 3 int pa,son,res; 4 res=heap[1];//堆頂馬上就要離開了,趕緊記錄一下 5 heap[1]=heap[heap_size--];//先將堆頂的元素與堆尾的元素進行交換,再將堆中元素個數減一 6 pa=1; 7 while(pa*2<=heap_size)//父親是不能變成葉節點的歐 8 { 9 son=pa*2; 10 if(son<heap_size && heap[son+1]<heap[son])//為了取最小的孩子 11 { 12 son++; 13 } 14 if(heap[pa]<=heap[son]) break; 15 swap(heap[pa],heap[son]); 16 pa=son; 17 } 18 return res; 19 }
還有就是C++標準模板庫的STL:
1 #include<algorithm> 2 int get() 3 { 4 pop_heap(heap+1,heap+heap_size+1,greater<int>());//小根堆 5 pop_heap(heap+1,heap+heap_size+1);//大根堆 6 return heap[heap_size--]; 7 }
我們由此可以將其運用起來:
1、堆排序:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 using namespace std; 5 int heap_size,n; 6 int heap[100001]; 7 void swap(int &a,int &b) 8 { 9 int t=a; 10 a=b; 11 b=t; 12 } 13 void put(int d) 14 { 15 heap[++heap_size]=d; 16 push_heap(heap+1,heap+heap_size+1,greater<int>()); 17 } 18 int get() 19 { 20 pop_heap(heap+1,heap+heap_size+1,greater<int>());//小根堆 21 return heap[heap_size--]; 22 } 23 int ans; 24 void work() 25 { 26 int x,y; 27 cin>>n; 28 for(int i=1;i<=n;i++) 29 { 30 cin>>x; 31 put(x); 32 } 33 for(int i=1;i<=n;i++) 34 { 35 cout<<get()<<' '; 36 } 37 } 38 39 int main() 40 { 41 work(); 42 return 0; 43 }
2、合併果子
其實就是將堆進行排序,將頭兩個元素相加成一個元素並放在堆尾,ans+=兩數之和:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 using namespace std; 5 int heap_size,n; 6 int heap[100001]; 7 void swap(int &a,int &b) 8 { 9 int t=a; 10 a=b; 11 b=t; 12 } 13 void put(int d) 14 { 15 heap[++heap_size]=d; 16 push_heap(heap+1,heap+heap_size+1,greater<int>()); 17 } 18 int get() 19 { 20 pop_heap(heap+1,heap+heap_size+1,greater<int>());//小根堆 21 return heap[heap_size--]; 22 } 23 int ans; 24 void work() 25 { 26 int x,y; 27 cin>>n; 28 for(int i=1;i<=n;i++) 29 { 30 cin>>x; 31 put(x); 32 } 33 for(int i=1;i<n;i++) 34 { 35 x=get(); 36 y=get(); 37 ans+=x+y; 38 put(x+y); 39 } 40 cout<<ans; 41 } 42 43 int main() 44 { 45 ios::sync_with_stdio(false); 46 work(); 47 return 0; 48 }
就先到這吧,再見啦!