堆排序原理詳解和Java實現代碼

當面試的時候被問到,你對堆有沒有做過一些瞭解??

當時我我反問了一下:您是指堆數據結構還是JVM裏面存儲對象等信息的堆時??

當時感覺自己還挺高端的,居然可以反問,但是當面試官說是堆排序的時候我就傻眼了,

不會,只記得個大概,於是今天花了點時候把堆排序搞懂了!!

詳解如下:

對於一個數組:

int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };

他可以用來表示堆,堆是一顆完全二叉樹,分大值堆和小值堆,最後得出的分別是從大到小的排列順序和從小到達的排列順序,看你要排序的順序覺定大小堆!

                         0(1)

         4 (2)                       1(3)

      3(4)          2(5)          16(6)       9(7)

10(8)   14(9)    8(10)     7(11)

存在一個如上圖的關係,

left[i] = 2* i ;

right[i] = 2+i +1;

知道這個關係後,我們就可以開始建堆了:

從 a.length/2 到第一個元素 開始對每個元素進行建堆(遞歸),使每個元素下的樹都維持最小值堆的特性;

a.length/2+1 到 a.length 個元素都是葉子節點,所以不需要維持最小值堆操作,因爲已經是最小值堆了!!

代碼如下:

public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


上一步驟理解了,那麼之後的操作就很簡單了。

建好最小值堆後,每次取堆頂元素和數組最後一個元素(最後第n個元素,n爲交換次數)交換,剩下的元素採用維持堆性質的操作,

循環至最後堆只剩下一個元素即可,代碼如下:

public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}

時間複雜度O(N*logN)

至此,堆排序講解完畢!希望大家可以明白堆排序的原理了!

附上完整可運行代碼:


package ddl.com;


public class HeapSort {
public static int heap_size;


// 雙親編號
public static int parent(int i) {
return i / 2;
}


// 左孩子編號
public static int leftChild(int i) {
return 2 * i;
}


// 右孩子編號
public static int rightChild(int i) {
return 2 * i + 1;
}


/**
* 堆排序:首先使用建立最大堆的算法建立好最大堆,然後將堆頂元素(最大值)與最後一個值交換,同時使得堆的長度減小1
* ,調用保持最大堆性質的算法調整,使得堆頂元素成爲最大值,此時最後一個元素已被排除在外、
*/
public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}


/**
* 建立最大堆。在數據中,a.length/2+1一直到最後的元素都是葉子元素,也就是平凡最大堆,因此從其前一個元素開始,一直到
* 第一個元素,重複調用max_heapify函數,使其保持最大堆的性質

* @param a
*/
public static void build_max_heap(int[] a) {
for (int i = a.length / 2; i >= 1; i--) {
max_heapify(a, i);
}
}


/**
* 保持最大堆的性質

* @param a
*            ,堆中的數組元素
* @param i
*            ,對以該元素爲根元素的堆進行調整,假設前提:左右子樹都是最大堆

*            由於左右孩子都是最大堆,首先比較根元素與左右孩子,找出最大值,假如不是根元素,則調整兩個元素的值;
*            由於左孩子(右孩子)的值與根元素交換,有可能打破左子樹(右子樹)的最大堆性質,因此繼續調用,直至葉子元素。
*/
public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}


public static void main(String[] args) {
int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
heap_size = a.length;
heapSort(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + "  ");
}
}
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章