1. 程式人生 > 其它 >堆建立和堆排序

堆建立和堆排序

一,堆建立:

比如說我現在要把這個完全二叉樹建立成堆,只要每一個子樹都是最小堆就好了,子樹也就是一個根節點加上兩個子節點。

首先從最後一行來看,最後一行是葉節點,葉節點是沒有相應的子節點的,所以所有以葉節點為根結點的子樹都是滿足最小堆的特性的,所以就直接從葉節點上面一層開始。

現在來看7號結點,發現這個不符合最小堆的特性,所以就要進行交換。後面的4 5 6 7號結點進行同理操作。

這一層交換完之後就會得到了下面這個二叉樹。

繼續對第二層的子樹進行同樣的操作

現在就發現已經滿足最小堆的特性了!

那麼其實做一個總結:就是從葉節點上一層(i = n\2)層 開始一直到第一次進行上面的操作就可以得到最小堆了!

for (i = n / 2; i >= 1; i++) {
    siftdown(i);
}

siftdown函式上一篇部落格寫了,其實程式碼很簡單,只要對這些位置的結點進行向上調整就好了。

二,堆排序:

堆排序的思路其實不會難,假設我們想進行從小到大的排序,可以先建立最小堆,再將頂部元素放入一個新的陣列中(頂部元素在堆裡面一定是最小的元素),直到堆是空的為止。下面就是怎麼刪除元素了

1 int deletmin() {
2     int  t;
3     t = h[1];//這個就是頂部元素了
4     h[1] = h[n];
5     n--;
6     siftdown(1
); 7 return t; 8 }

這個刪除函式的思路其實就是為了保證堆的結構,先用一個臨時變數記錄下第一個頂點的值,然後把最後一個元素的值賦給第一個元素,然後把第一個頂點中的元素向下調整,n的值減一就是把原來最後一個元素給排掉了,最後把一開始記錄的t給返回。

 1 #include <bits/stdc++.h>
 2 int h[101];
 3 int n;
 4 using namespace std;
 5 int h[100];
 6 void swap(int a, int b) {
 7     int t;
 8     t = h[a];
 9     h[a] = h[b];
10 h[b] = t; 11 return; 12 } 13 void siftup(int i) { 14 int flag = 0; 15 if (i == 1) { 16 return; 17 } 18 while (i != 1 && flag == 0) { 19 //判斷是否比父節點小 20 if (h[i] < h[i / 2]) 21 swap(i, i / 2); 22 else 23 flag = 1; 24 i = i / 2; 25 26 } 27 return; 28 } 29 30 void creat() { 31 int i; 32 for (i = n / 2; i >= 1; i++) { 33 siftdown(i); 34 } 35 } 36 37 38 //下面就是怎麼刪除堆中的元素 39 40 void siftdown(int i) { 41 int t, flag = 0; 42 while (i * 2 < n && flag == 0) { 43 if (h[i] > h[i * 2]) { 44 t = i * 2; 45 46 } 47 else { 48 t = i; 49 } 50 //上面其實就是有沒有和左結點交換 51 if (i * 2 + 1 <= n) {//也就是存在右結點 52 if (h[t] > h[2 * i + 1]) { 53 t = 2 * i + 1; 54 } 55 } 56 if (t != i) { 57 swap(t, i);//注意這個swap函式存入的是陣列的下標 58 i = t;//這個地方下標還是要變的 59 } 60 else { 61 flag = 1; 62 } 63 } 64 return; 65 } 66 67 68 69 int deletmin() { 70 int t; 71 t = h[1];//這個就是頂部元素了 72 h[1] = h[n]; 73 n--; 74 siftdown(1); 75 return t; 76 } 77 78 int main() { 79 int i,num; 80 scanf("%d", &num); 81 for (int i = 1; i <= num; i++) { 82 scanf("%d", &h[i]); 83 } 84 n = num; 85 //這個地方把n重新賦值了 86 creat(); 87 for (int i = 1; i <= num; i++) { 88 printf("%d", deletmin()); 89 90 } 91 92 return 0; 93 }

其實堆排序還有更簡單的方法:之前的方法是建立最小堆,把堆頂元素拿出來放到一個數組裡面,然後把最後一個結點中的元素放到堆頂,然後向下調整來保持堆的特性。而下面這種方法是要建立最大堆的,比如我現在想要從小到大排序,希望最大的放在後面,所以我就把第一個元素和最後一個元素交換。交換後向下調整依次來保證堆的特性。

1 void headsort() {
2     while (n > 1) {
3         swap(1, n);
4         n--;
5         siftdown();
6     }
7     return;
8 }

最後來進行一個總結:支援插入元素和尋找最大最小元素的資料結構被稱為優先佇列,如果普通佇列像要實現這樣的功能就要列舉整個佇列,這樣時間複雜度很高。

但是如果要學會用堆排序以及堆來解決問題對我來說還有很長的路要走。

參考《啊哈演算法》