堆排序法(Java & C/C++ 實現)
阿新 • • 發佈:2018-12-14
一、前言
堆排序是利用堆這種資料結構而設計的一種排序演算法。時間複雜度為 O(n * lg n)。
介紹堆排序前,我們先介紹一下堆的相關概念,如果你對堆的概念還不熟悉的話可以看看。
二、堆
1. 示意圖
2. 性質
除最底層外,該樹是完全充滿的,且是從左到右填充。
樹的根結點是 A[ 1 ],若某一結點下標為 i,則很容易得到它的父節點為 i/2,左子結點為 2i,右子結點為 2i + 1。
注意: 陣列的索引是 0 開始的,其左右子結點分別為 2i + 1 和 2i + 2。
3. 最大堆和最小堆
最大堆即父結點的值大於等於子結點的值;最小堆即父結點的值小於等於子結點的值。
4. 應用
堆是一個很有用的資料結構,它的一個常見應用即:優先佇列 。
三、程式碼實現
主要是 3 個方法:
heapSort() 方法進行堆排序,裡面會呼叫 buildHeap() 方法和 maxHeapify() 方法。
buildHeap() 方法將一個數組構建為一個最大堆。
maxHeapify() 方法調整堆,使得這個堆滿足最大堆的性質。
1. Java
public class Main { public static void main(String[] args) { int[] arr = new int[] { 16, 4, 10, 14, 7, 9, 3, 2, 8, 1 }; heapSort(arr); printArray(arr); } public static void heapSort(int[] arr) { buildHeap(arr); // 首先建立堆 int heapSize = arr.length; for (int i = arr.length - 1; i > 0; i--) { // 取出最大值放在陣列末端。由於堆的特性最大值總是在 a[0] 處 int max = arr[0]; arr[0] = arr[i]; arr[i] = max; // 重新調整堆 maxHeapify(arr, 0, --heapSize); } } public static void buildHeap(int[] arr) { // 堆的最後一個分支結點索引為 arr.length / 2 - 1 for (int i = arr.length / 2 - 1; i >= 0; i--) { maxHeapify(arr, i, arr.length); } } public static void maxHeapify(int[] arr, int index, int heapSize) { int leftIndex = index * 2 + 1; // 左子節點對應陣列中的索引 int rightIndex = index * 2 + 2; // 右子節點對應陣列中的索引 int maxIndex = index; // 如果左子結點較大,則將最大值索引設為左子節點 if (leftIndex < heapSize && arr[leftIndex] > arr[index]) { maxIndex = leftIndex; } // 如果右子結點比 max(this, left)還大,則將最大值索引設為右子節點 if (rightIndex < heapSize && arr[rightIndex] > arr[maxIndex]) { maxIndex = rightIndex; } // 如果當前結點的值不是最大的,則需要交換最大值,並繼續遍歷交換後的子結點 if (maxIndex != index) { int temp = arr[maxIndex]; arr[maxIndex] = arr[index]; arr[index] = temp; maxHeapify(arr, maxIndex, heapSize); // 結點的計數比索引值大1 } } public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
2. C/C++
程式碼實現基本沒什麼差異。
#include <stdio.h> void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } void maxHeapify(int *arr, int index, int heapSize) { int leftIndex = index * 2 + 1; // 左子節點對應陣列中的索引 int rightIndex = index * 2 + 2; // 右子節點對應陣列中的索引 int maxIndex = index; // 如果左子結點較大,則將最大值索引設為左子節點 if (leftIndex < heapSize && arr[leftIndex] > arr[index]) { maxIndex = leftIndex; } // 如果右子結點比 max(this, left)還大,則將最大值索引設為右子節點 if (rightIndex < heapSize && arr[rightIndex] > arr[maxIndex]) { maxIndex = rightIndex; } // 如果當前結點的值不是最大的,則需要交換最大值,並繼續遍歷交換後的子結點 if (maxIndex != index) { swap(&arr[index], &arr[maxIndex]); maxHeapify(arr, maxIndex, heapSize); // 結點的計數比索引值大1 } } void buildHeap(int *arr, int length) { // 堆的最後一個分支結點索引為 arr.length / 2 - 1 for (int i = length / 2 - 1; i >= 0; i--) { maxHeapify(arr, i, length); } } void heapSort(int *arr, int length) { buildHeap(arr, length); // 首先建立堆 int heapSize = length; for (int i = length - 1; i > 0; i--) { // 取出最大值放在陣列末端。由於堆的特性最大值總是在 a[0] 處 swap(&arr[0], &arr[i]); // 重新調整堆 maxHeapify(arr, 0, --heapSize); } } void printArray(int *array, int length) { for (int i = 0; i < length; i++) { printf("%d ", array[i]); } } int main() { int arr[] = { 16, 4, 10, 14, 7, 9, 3, 2, 8, 1 }; heapSort(arr, sizeof(arr) / sizeof(arr[0])); printArray(arr, sizeof(arr) / sizeof(arr[0])); return 0; }
執行結果
1 2 3 4 7 8 9 10 14 16