數據結構堆(包含堆排序)有這麼簡單嗎? 其實是真的簡單!

前言

今天無意中翻了一下以前寫的堆排序文章,發現寫的也太Low了 . . .所以準備重新寫一篇文章來更詳細的講解堆排序,主要參考啊哈算法 和以前的一些資料,希望大家喜歡,今天也是放假的日子,但我還是選擇在學校留上幾天,希望大家喜歡這一篇文章 ^ _ ^ . . .

相關樹及C++堆排序一些文章:

1)《算法筆記》—— 堆排序算法( C++實現)
2) 二叉搜索樹 —— 查找與排序
3) 紅黑樹基本功能 —— C++實現
4) 二叉樹(鏈式存儲)—— C++實現
5) 二叉樹(順序存儲)—— C++實現

下面我將更詳細的講解堆排序 . . .
.


前面會講一些堆的一些特性 ~

文章目錄:

1)刪除頂元素並且添加新元素
2)增加新元素
3)創建一個堆(關鍵的部分,如何創建),堆排序


在這裏插入圖片描述

那麼什麼是堆排序呢? 讓我們看看度孃的解釋:

—— “堆排序(英語:Heapsort)是指利用堆這種數據結構所設計的一種 排序算法
堆是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即 子結點的鍵值或索引總是小於(或者大於)它的父節點。”

首先,它是一種排序算法,然後它擁有的一些特性 . . . 下面讓我們來了解一下這個特性是什麼樣子的,走你 ~

.

堆是一種特殊的完全二叉樹,就像下面這棵樹一樣:
在這裏插入圖片描述

這棵二叉樹有這麼一個特性:所有父結點都比子結點要小
那麼這個特性有什麼用呢?—— 下面我們將講解堆的一些優秀的特性 . . .
.

一)刪除頂元素並且添加新元素

我們現在需要刪除堆頂部的元素,然後將一個數23放到堆頂,我們將23調整到合適的位置。那們我們如何調整呢?當前結果爲如下的圖:
在這裏插入圖片描述

當然,聰明的你們肯定想到了, 向下調整!! 我們將 23 與 它的兩個兒子(2,5)進行比較,與較小的一個互換位置,結果如下:
在這裏插入圖片描述

同理,我們依次重複的比較,將 23調整到最終的位置,包含它的移動路線:
在這裏插入圖片描述

我們將這個 23進行調整,竟然只進行了 3次比較,是不是非常的快速呢? 現在最小的數在堆頂(2)。

效率提升的不是一星半點,當前我們肯定需要一個東西來進行比較,比如我們有 14個數字,分別是99、5、36、7、22、17、46、12、2、19、25、28 和 92,假如我們刪除最小的數後再添加一個新數,並且這樣的操作進行 14次,那麼它的時間複雜度將爲 O(14 ^2)即 O( N ^2) . . .

假如現在我們有 1Y 個數,對它進行刪除最小數並且 新增一個數的操作(進行1Y次),我們只需要 2.7秒,它的複雜度爲 O(logN) . . .

結點數據向下調整的代碼如下:
在這裏插入圖片描述
下面我會展示出完整的代碼示例,這個圖片樣式的僅供參考學習 . . .

.

二)增加新元素

如果我們想直接將新元素插入到堆之中,只需要將新元素放入到末尾,再將新元素向上調整,直至滿足堆的特性爲止 . . . 時間複雜度爲 O(logN),例如我們現在需要新增一個元素爲 3,它的運動軌跡如下所示:
在這裏插入圖片描述

最終效果如下所示:
在這裏插入圖片描述
結點數據向上調整的代碼如下:
在這裏插入圖片描述
.

三)創建一個堆(關鍵的部分,如何創建)

首先,我們將數據依次的放入到堆中,然後設計一個算法將其中的數據進行調整,以便其滿足堆的特性(最小堆或者最大堆) . . .

將數據依次放入到堆中:

n = 0;
for(i = 1; i <= m; i++)
{
    ++n;
    h[n] = a[i];
}

堆中數據進行調整:

for(i = n / 2; i >= 1; --i)
    siftdown(i);

這兩行代碼是什麼意思呢? 爲什麼 i 要從 n / 2 開始呢?
因爲我們調整堆中的數據滿足堆特性,只需要將每個根結點向下調整就行了,而最後葉子結點是沒有兒子結點的,所以我們只需要從倒數第二排開始即可,如下所示:
在這裏插入圖片描述

—— 堆排序
我們用這樣的思想就可以把一個堆調整好了,然後就可以使用我們的堆排序了,堆排序的時間複雜度是 O(NlogN),它與快速排序是一樣的實現堆排序我們只需要每次刪除頂部元素並將頂部元素輸出,然後用最後一個元素賦值給頂結點,並且將這個新的頂點進行向下調整即可 . . .

刪除最大的元素代碼如下所示(長度 - 1,假如是最小堆):

int deletemax()
{
    int t;
    t = h[1];		// 用於返回的值
    h[1] = h[n];	// 最後一個元素賦值給頂結點
    --n;			// 長度 - 1
    siftdown(1);	// 向下調整(調整最小值到上面去)
    return t;		// 返回的數據
}

建堆以及堆排序的完整代碼如下所示:
在這裏插入圖片描述在這裏插入圖片描述
當我們輸入下面的這些數據後:

14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

運行結果是:

1 2 5 7 12 17 19 22 25 28 36 46 92 99

.
當然我們還有更好的辦法,就是我們剛開始不是創建的最小堆,而是創建的是最大堆,最大堆創建好之後,最大的元素在 h[1],我們需要的是將數據從小到大進行排序,大的數據放到最後面。因此,我們每次將最大值放到最後面,並且長度 - 1,這樣獲得的最大值就不會被影響到,然後我們利用與最大值交換的那個最小值進行向下調整,重複如此直至n的值爲0 . . .

代碼設計如下所示:

// 堆排序
void heapsort(){
    while(n > 1){
        swap(1, n);
        --n;
        siftdown(1);
    }
}

這樣我們只需要在 main函數中調用這個方法之後,直接輸出堆中的數據即可:

// 堆排序
heapsort();

// 輸出
for(i = 1; i <= num; ++i)
    printf("%d ", h[i]);

結果與上面是一樣的 . . .
.
.
.


浪子花夢

一個有趣的程序員 ~

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