首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

非遞歸堆排序算法:C 語言高性能實現與百萬級測試

     編輯:關於C

算法原理

堆排序利用**完全二叉樹**性質,將陣列視為堆結構。分兩階段:

  1. 建堆:自底向上調整,構建大根堆 (父節點 ≥ 子節點)。
  2. 排序:反覆交換堆頂 (最大值) 與堆尾元素,縮小堆範圍,向下調整新堆頂。

非遞歸實現避免函數調用開銷,適合嵌入式、高性能場景。

核心代碼實現


#include <windows.h> // DWORD 類型

// 參數: arr 陣列首址, number 元素個數
void HeapSort(int * const arr, const DWORD number)
{
    if (number < 2) return;

    int tmp;
    DWORD tmpIndex, indexUp, indexDown, num;

    // === 階段 1: 建堆 (自底向上) ===
    indexUp = number - 1; // 最後一個葉子節點索引

    // 處理最後一個非滿雙子節點的父節點 (若總數為偶數)
    if (indexUp % 2 != 0)
    {
        tmpIndex = (indexUp - 1) / 2; // 父節點
        if (arr[indexUp] > arr[tmpIndex]) { /* 交換 */ }
        indexUp--;
    }

    // 成對處理剩餘父節點 (每次處理一對兄弟節點)
    for (; indexUp > 0; indexUp -= 2)
    {
        tmpIndex = (indexUp / 2) - 1; // 父節點
        // 找較大的子節點
        if (arr[indexUp - 1] >= arr[indexUp])
        {
            if (arr[indexUp - 1] > arr[tmpIndex]) { indexDown = indexUp - 1; /* 交換父子 */ }
            else continue;
        }
        else
        {
            if (arr[indexUp] > arr[tmpIndex]) { indexDown = indexUp; /* 交換父子 */ }
            else continue;
        }

        // 向下篩選 (Sift Down)
        while ((2 * indexDown) < number)
        {
            tmpIndex = 2 * indexDown + 1; // 左子節點
            // 選較大的子節點 (注意右子節點邊界)
            if (arr[tmpIndex] >= arr[tmpIndex + 1] || (tmpIndex + 1 == number))
            {
                if (arr[tmpIndex] > arr[indexDown]) { /* 交換並下沉 */ indexDown = tmpIndex; }
                else break;
            }
            else
            {
                if (arr[tmpIndex + 1] > arr[indexDown]) { /* 交換並下沉 */ indexDown = tmpIndex + 1; }
                else break;
            }
        }
    }

    // === 階段 2: 排序 (反覆取最大) ===
    // 將堆頂 (最大) 與當前堆尾交換
    tmp = arr[0]; arr[0] = arr[number - 1]; arr[number - 1] = tmp;

    for (num = number - 1; num > 1; num--)
    {
        indexDown = 0;
        // 向下調整新堆頂
        while ((2 * indexDown + 1) < num)
        {
            tmpIndex = 2 * indexDown + 1;
            if (arr[tmpIndex] >= arr[tmpIndex + 1])
            {
                if (arr[tmpIndex] > arr[indexDown]) { /* 交換並下沉 */ indexDown = tmpIndex; }
                else break;
            }
            else
            {
                if ((arr[tmpIndex + 1] > arr[indexDown]) && (tmpIndex + 1 < num)) { /* 交換並下沉 */ indexDown = tmpIndex + 1; }
                else break;
            }
        }
        // 當前最大值歸位
        tmp = arr[0]; arr[0] = arr[num - 1]; arr[num - 1] = tmp;
    }
}

測試驗證與性能


#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void ExamineArr(const int * const arr, DWORD number)
{
    for (DWORD i = 0, j = 1; j < number; i++, j++)
        if (arr[i] > arr[j]) { printf("錯誤: 第 %u > 第 %u\n", i, j); return; }
    printf("排序驗證通過。\n");
}

int main()
{
    DWORD StartTime, EndTime, i;
    DWORD num = 50000000; // 5000 萬
    int *arr = (int *)malloc(num * sizeof(int));
    if (!arr) { printf("內存分配失敗\n"); return -1; }

    StartTime = GetTickCount();
    for (i = 0; i < num; i++) arr[i] = rand();
    EndTime = GetTickCount();
    printf("生成 %u 隨機數耗時: %u ms\n", num, EndTime - StartTime);

    StartTime = GetTickCount();
    HeapSort(arr, num);
    EndTime = GetTickCount();
    printf("堆排序耗時: %u ms\n", EndTime - StartTime);

    ExamineArr(arr, num);
    free(arr);
    return 0;
}

測試結果 (參考)

運行效果截圖

  • 5000 萬 int 元素 (約 200 MB) 排序在普通 PC 上可於秒級完成。
  • 空間複雜度 **O(1)**,原地排序,無額外內存分配 (除測試代碼外)。
  • 時間複雜度 **O(n log n)**,最壞/平均/最好情況一致,無快排最壞 O(n²) 風險。

優化建議

  1. 底層優化:使用位運算替代乘除 (index*2 -> index<<1)。
  2. 循環展開:手動展開內層 while 減少分支預測失敗。
  3. 預取指令:_mm_prefetch 提前載入緩存行。
  4. 並行化:建堆階段難並行,但可用「並行堆排序」變體或外部排序策略處理超大數據。

總結

非遞歸堆排序是**原地、穩定時間複雜度、無棧溢出風險**的排序利器。代碼雖較快排複雜,但在實時系統、內存受限環境、需保證最壞情況性能的場景極具價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved