1. 程式人生 > >堆排序法(Java & C/C++ 實現)

堆排序法(Java & C/C++ 實現)

一、前言

堆排序是利用堆這種資料結構而設計的一種排序演算法。時間複雜度為 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