演算法-一步步教你如何用c語言實現堆排序(非遞迴)

陳佳樂發表於2019-07-26

看了左神的堆排序,覺得思路很清晰,比常見的遞迴的堆排序要更容易理解,所以自己整理了一下筆記,帶大家一步步實現堆排序演算法

首先介紹什麼是大根堆:每一個子樹的最大值都是子樹的頭結點,即根結點是所有結點的最大值

堆排序是基於陣列和二叉樹思想實現的(二叉樹是腦補結構,實際是陣列)

堆排序過程

1、陣列建成大根堆,首先,遍歷所有結點,當前結點index和父結點(index-1)/ 2 比較 (當前陣列的下標是index,此結點的父結點的下標就是(index-1)/ 2  ),如果比父結點大,交換,變成父結點的位置再和上一層的父結點比較,直到滿足大根堆條件

int swap(int source[], int a, int b)
{
    int temp = source[a];
    source[a] = source[b];
    source[b] = temp;
}
int heapsort(int source[], int len)
{
    for (int i = 0; i <len; i++)
    {
        heapInsert(source, i);
    }
}
int heapInsert(int source[], int index)
{
    while (source[index] > source[(index - 1) / 2])
    {
        swap(source, index, (index - 1) / 2);
        index = (index - 1) / 2;
    }
}

2、讓根結點和最後一個結點交換位置,也就是陣列的第一個數和最後一個數交換位置,接下來最後一個數不用考慮了,比如一個陣列有5個數,定義一個變數size=5,大根堆的根結點放到最後一個數後,--size

int size = len;
swap(source, 0, --size);

3、讓交換後的頭結點經歷一個向下的調整,讓結點和自己的兩個孩子比較,如果孩子的值比頭結點大,交換,交換到孩子結點位置,繼續和下面的兩個孩子比較,直到滿足大根堆條件

int heapify(int source[], int index, int size)//index表示要和它兩個孩子比較的結點
{
    int left = index * 2 + 1;  //找到index左孩子結點的陣列下標
    while (left < size)
    {
        int largest = left + 1 < size && source[left + 1] > source[left] ? source[left + 1] : source[left];//如果有右孩子且右孩子比左孩子大,令largest=右孩子的值,也就是把兩個孩子中最大的一個數賦給largest
        if (source[index] < source[largest])
        {
            swap(source, index, largest);
            index = largest;
            left = index * 2 + 1;
        }
        else break;
    }

}

4、重複第2步,第3步,直到size = 0 ,整個陣列排序過程結束

while (size > 0)
    {
        swap(source, 0, --size);
        heapify(source, 0, size);
    }

原始碼如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int swap(int source[], int a, int b)
{
    int temp = source[a];
    source[a] = source[b];
    source[b] = temp;
}
int heapsort(int source[], int len)
{
    for (int i = 0; i < len; i++)
    {
        heapInsert(source, i);
    }
    int size = len;
    while (size > 0)
    {
        swap(source, 0, --size);
        heapify(source, 0, size);
    }
}
int heapInsert(int source[], int index)
{
    while (source[index] > source[(index - 1) / 2])
    {
        swap(source, index, (index - 1) / 2);
        index = (index - 1) / 2;
    }
}
int heapify(int source[], int index, int size)//index表示要和它兩個孩子比較的結點
{
    int left = index * 2 + 1;  //找到index左孩子結點的陣列下標
    while (left < size)
    {
        int largest = left + 1 < size && source[left + 1] > source[left] ? left + 1 : left;//如果有右孩子且右孩子比左孩子大,令largest=右孩子的值,也就是把兩個孩子中最大的一個數賦給largest
        if (source[index] < source[largest])
        {
            swap(source, index, largest);
            index = largest;
            left = index * 2 + 1;
        }
        else break;
    }

}
int main()
{
    int source[] = { 10,16,123,8,79,6,54,65,48,6,54,536,654,64,8,9,25,17 };
    int len;
    len = sizeof(source) / sizeof(int);
    heapsort(source, len);
    for (int i = 0; i < len; i++)
    {
        printf("%d ", source[i]);
    }
    
}

輸出結果

以上就是堆排序的所有細節,這個版本很優良,堆排序的額外空間複雜度是O(1),如果用遞迴的話,遞迴有遞迴棧,額外空間複雜度不就上去了嗎,設計成這種迭代的可以省空間,時間複雜度為O(n log n)

轉載請註明出處、作者  謝謝

 

相關文章