堆排序?qū)崿F(xiàn)方案_第1頁
堆排序?qū)崿F(xiàn)方案_第2頁
堆排序?qū)崿F(xiàn)方案_第3頁
堆排序?qū)崿F(xiàn)方案_第4頁
堆排序?qū)崿F(xiàn)方案_第5頁
已閱讀5頁,還剩25頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

堆排序?qū)崿F(xiàn)方案一、堆排序概述

堆排序是一種基于堆數(shù)據(jù)結(jié)構(gòu)的比較類排序算法,具有效率高、實現(xiàn)簡單等特點。堆排序的基本思想是將待排序序列構(gòu)造成一個大頂堆,此時,整個序列的最大值就是堆頂?shù)母?jié)點。然后將其與末尾元素進行交換,此時末尾就為最大值。之后將剩余n-1個元素重新構(gòu)造成一個堆,這樣會得到n個元素的次小值。如此反復(fù)執(zhí)行,便能得到一個有序序列。

堆排序主要包含兩大步驟:建堆和堆調(diào)整。下面將詳細介紹堆排序的具體實現(xiàn)方案。

二、堆排序?qū)崿F(xiàn)步驟

(一)建堆

1.初始化堆:將待排序序列視為一棵完全二叉樹,從最后一個非葉子節(jié)點開始,依次向前調(diào)整每個節(jié)點,使其滿足堆的性質(zhì)。

2.調(diào)整堆:對于每個節(jié)點,比較其與左右子節(jié)點的值,若不滿足大頂堆的性質(zhì)(即當前節(jié)點值小于左右子節(jié)點值),則與較大子節(jié)點交換,并繼續(xù)調(diào)整交換后的子節(jié)點,直到滿足堆的性質(zhì)。

(二)堆調(diào)整

1.交換堆頂與末尾元素:將當前堆頂元素與末尾元素交換,此時末尾元素為最大值。

2.縮小堆范圍:將堆的范圍縮小至前n-1個元素,并重新調(diào)整堆頂元素,使其滿足堆的性質(zhì)。

3.重復(fù)調(diào)整:重復(fù)步驟2,直到堆的范圍縮小為1,此時序列已有序。

三、堆排序算法實現(xiàn)

(一)Python實現(xiàn)

defheapify(arr,n,i):

largest=i

left=2i+1

right=2i+2

ifleft<nandarr[i]<arr[left]:

largest=left

ifright<nandarr[largest]<arr[right]:

largest=right

iflargest!=i:

arr[i],arr[largest]=arr[largest],arr[i]

heapify(arr,n,largest)

defheap_sort(arr):

n=len(arr)

foriinrange(n//2-1,-1,-1):

heapify(arr,n,i)

foriinrange(n-1,0,-1):

arr[i],arr[0]=arr[0],arr[i]

heapify(arr,i,0)

returnarr

(二)C++實現(xiàn)

include<vector>

include<algorithm>

voidheapify(std::vector<int>&arr,intn,inti){

intlargest=i;

intleft=2i+1;

intright=2i+2;

if(left<n&&arr[left]>arr[largest])

largest=left;

if(right<n&&arr[right]>arr[largest])

largest=right;

if(largest!=i){

std::swap(arr[i],arr[largest]);

heapify(arr,n,largest);

}

}

voidheap_sort(std::vector<int>&arr){

intn=arr.size();

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

heapify(arr,n,i);

for(inti=n-1;i>0;i--){

std::swap(arr[0],arr[i]);

heapify(arr,i,0);

}

}

四、堆排序性能分析

(一)時間復(fù)雜度

堆排序的時間復(fù)雜度為O(nlogn),其中n為待排序序列的長度。這是因為建堆的時間復(fù)雜度為O(n),每次堆調(diào)整的時間復(fù)雜度為O(logn),總共需要調(diào)整n-1次。

(二)空間復(fù)雜度

堆排序的空間復(fù)雜度為O(1),因為堆排序是原地排序算法,不需要額外的存儲空間。

五、總結(jié)

堆排序是一種高效的排序算法,具有時間復(fù)雜度穩(wěn)定、實現(xiàn)簡單等優(yōu)點。通過本文的介紹,相信讀者已經(jīng)掌握了堆排序的基本原理和實現(xiàn)方法。在實際應(yīng)用中,可以根據(jù)具體需求選擇合適的排序算法,以達到最佳的性能表現(xiàn)。

一、堆排序概述

堆排序是一種基于堆(Heap)數(shù)據(jù)結(jié)構(gòu)的比較類排序算法。堆是一種特殊的樹形數(shù)據(jù)結(jié)構(gòu),通常實現(xiàn)為數(shù)組,具有以下核心特征:

(一)堆的性質(zhì)

1.形狀性質(zhì):堆通常是一棵完全二叉樹。這意味著除了最底層之外,其他每一層都已經(jīng)被完全填滿,并且最底層的節(jié)點都集中在左側(cè)。

2.堆序性質(zhì)(HeapProperty):根據(jù)堆的類型,堆序性質(zhì)有所不同。

(1)大頂堆(Max-Heap):對于堆中的任意節(jié)點`i`,其值總是大于或等于其子節(jié)點的值。在最大堆中,樹的根節(jié)點擁有最大值。

(2)小頂堆(Min-Heap):對于堆中的任意節(jié)點`i`,其值總是小于或等于其子節(jié)點的值。在小頂堆中,樹的根節(jié)點擁有最小值。

堆排序通常使用大頂堆實現(xiàn)。

(二)堆排序的基本思想

堆排序的過程可以分為兩個主要階段:

1.建堆(BuildHeap):將一個無序的輸入數(shù)組重新組織成一個大頂堆。這一步確保了堆頂元素是當前未排序元素中的最大值。

2.排序(Sort):通過反復(fù)將堆頂元素(當前最大值)與堆的最后一個元素交換,并調(diào)整剩余元素構(gòu)成的子堆,使其重新滿足堆的性質(zhì),從而逐步構(gòu)建出有序序列。

(三)堆排序的特點

1.效率高:堆排序的最壞、平均和最好時間復(fù)雜度均為O(nlogn),在所有比較排序中具有較好的性能。

2.不穩(wěn)定性:堆排序是一種不穩(wěn)定的排序算法。這意味著相等元素的相對順序在排序后可能會改變。

3.原地排序:堆排序是原地排序算法,其空間復(fù)雜度為O(1),只需要常數(shù)級的額外空間(除了輸入數(shù)組本身)。這使得它在內(nèi)存使用上非常高效。

4.不是內(nèi)部排序:由于建堆過程可能需要O(n)的時間復(fù)雜度(特別是通過自底向上的方法),而交換和調(diào)整堆的過程需要O(logn)的時間復(fù)雜度,堆排序通常被認為是介于內(nèi)部排序和外部排序之間的一種算法,盡管其常用于內(nèi)存排序。

二、堆排序?qū)崿F(xiàn)步驟

堆排序的成功實現(xiàn)依賴于對堆結(jié)構(gòu)的深刻理解和準確的算法步驟。以下是詳細的實現(xiàn)步驟,主要圍繞大頂堆進行闡述。

(一)建堆(BuildHeap)

建堆的目的是將一個無序的數(shù)組轉(zhuǎn)換成一個大頂堆。這個過程需要從數(shù)組的最后一個非葉子節(jié)點開始,逐個向前調(diào)整節(jié)點,使其滿足大頂堆的性質(zhì)。

1.確定非葉子節(jié)點起點:

(1)在一個完全二叉樹中,最后一個非葉子節(jié)點的索引可以通過`n//2-1`計算得出,其中`n`是數(shù)組的長度。葉子節(jié)點位于索引`n//2`到`n-1`之間。

(2)從最后一個非葉子節(jié)點開始向前調(diào)整,是因為越靠近葉子節(jié)點的節(jié)點,其子節(jié)點越多,調(diào)整的必要性越大,離葉子節(jié)點越遠的節(jié)點(即靠近根節(jié)點的節(jié)點)調(diào)整時影響范圍更大,但子節(jié)點更少。

2.自底向上調(diào)整(SiftDown/Heapify):

(1)對于每一個從`n//2-1`到`0`的索引`i`,執(zhí)行調(diào)整操作。

(2)調(diào)整的目標是確保以`i`為根的子樹滿足大頂堆的性質(zhì)。

(3)對于當前節(jié)點`i`,首先確定其左右子節(jié)點的索引:左子節(jié)點索引為`2i+1`,右子節(jié)點索引為`2i+2`。

(4)比較當前節(jié)點`i`與其左右子節(jié)點的值。

(5)找出三個節(jié)點(節(jié)點`i`及其左右子節(jié)點)中的最大值。如果當前節(jié)點`i`的值不是最大值,則將其與最大值所在節(jié)點的值進行交換。

(6)關(guān)鍵點:交換后,雖然節(jié)點`i`的位置變了,但其子樹可能不再滿足堆的性質(zhì)。因此,必須繼續(xù)對交換后到達位置的新節(jié)點執(zhí)行同樣的調(diào)整過程。這個過程就像“沉降”一樣,將節(jié)點向下調(diào)整,直到其子樹滿足堆的性質(zhì)或到達葉子節(jié)點位置。

(7)重復(fù)步驟(4)到(6),直到節(jié)點`i`的子樹滿足堆的性質(zhì),或者`i`已經(jīng)是葉子節(jié)點。

3.建堆完成標志:當從索引`0`(根節(jié)點)開始,對所有`i`在`0`到`n//2-1`范圍內(nèi)的節(jié)點都執(zhí)行了上述調(diào)整后,整個數(shù)組就構(gòu)成了一棵大頂堆。

(二)堆調(diào)整(Heapify/SiftDown)

堆調(diào)整是堆排序的核心操作,用于在每次將堆頂元素取出后,重新調(diào)整剩余元素,使其重新滿足堆的性質(zhì)。這個操作通常在“排序”階段反復(fù)調(diào)用。

1.交換堆頂與末尾元素:

(1)堆頂元素(索引`0`)是當前未排序部分的最大值。

(2)將其與當前未排序部分的最后一個元素(索引`n-1`,其中`n`是當前待排序元素的數(shù)量)進行交換。

(3)交換后,末尾元素`arr[n-1]`獲得了當前未排序部分的最大值。此時,這部分元素(索引`0`到`n-2`)可能不再滿足堆的性質(zhì)。

2.縮小堆范圍并調(diào)整:

(1)將待排序元素的數(shù)量(即需要保持堆性質(zhì)的元素范圍)從`n`減少到`n-1`。

(2)對新的堆頂元素(原數(shù)組末尾元素,現(xiàn)在位于索引`0`)執(zhí)行“自底向上調(diào)整”(SiftDown)操作。

(3)調(diào)整過程與建堆中的自底向上調(diào)整相同:比較當前節(jié)點與其子節(jié)點,若不滿足大頂堆性質(zhì)則交換,并繼續(xù)對交換后的節(jié)點進行遞歸調(diào)整,直到滿足堆的性質(zhì)或到達葉子節(jié)點。

3.重復(fù)調(diào)整直至完成:

(1)重復(fù)步驟(1)和(2),每次將堆頂(當前最大值)與末尾元素交換,并對新的堆頂進行堆調(diào)整。

(2)這個過程會不斷減少需要保持堆性質(zhì)的元素范圍(`n`逐漸減小到`1`)。

(3)當范圍縮小為`1`時,即所有元素都已按從大到小排列在數(shù)組的末尾,排序過程完成。

三、堆排序算法實現(xiàn)

(一)Python實現(xiàn)

以下是使用Python實現(xiàn)堆排序的代碼示例,包含了`heapify`(用于調(diào)整堆)和`heap_sort`(主排序函數(shù))兩個核心函數(shù)。

```python

defheapify(arr,n,i):

"""調(diào)整以索引i為根的子樹,使其滿足大頂堆性質(zhì)。

arr:待排序的數(shù)組

n:堆的有效大?。创{(diào)整的元素范圍)

i:當前子樹的根節(jié)點索引

"""

largest=i初始化最大值索引為根節(jié)點

left=2i+1左子節(jié)點索引

right=2i+2右子節(jié)點索引

如果左子節(jié)點存在且大于當前最大值,則更新最大值索引

ifleft<nandarr[i]<arr[left]:

largest=left

如果右子節(jié)點存在且大于當前最大值,則更新最大值索引

ifright<nandarr[largest]<arr[right]:

largest=right

如果最大值索引不是當前根節(jié)點索引,則需要交換

iflargest!=i:

arr[i],arr[largest]=arr[largest],arr[i]交換根節(jié)點與最大值子節(jié)點

交換后,子樹可能不再滿足堆的性質(zhì),因此遞歸調(diào)用heapify

遞歸調(diào)整的是交換后位于原最大值位置的子樹

heapify(arr,n,largest)

defheap_sort(arr):

"""對數(shù)組arr進行堆排序。

arr:待排序的數(shù)組

"""

n=len(arr)

1.建堆:自底向上調(diào)整所有非葉子節(jié)點

從最后一個非葉子節(jié)點開始,到根節(jié)點為止

foriinrange(n//2-1,-1,-1):

heapify(arr,n,i)

2.排序:重復(fù)取出堆頂元素并調(diào)整堆

foriinrange(n-1,0,-1):

將當前未排序部分的堆頂(最大值)與末尾元素交換

arr[i],arr[0]=arr[0],arr[i]

調(diào)整剩余n-i個元素(索引0到i-1)為大頂堆

注意:此時堆的有效大小為n-i

heapify(arr,i,0)

排序完成,arr已經(jīng)是有序數(shù)組

returnarr

```

(二)C++實現(xiàn)

以下是使用C++實現(xiàn)堆排序的代碼示例,同樣包含了`heapify`和`heap_sort`函數(shù)。注意C++中使用`std::vector`來表示數(shù)組。

```cpp

include<vector>

include<algorithm>//用于std::swap

voidheapify(std::vector<int>&arr,intn,inti){

intlargest=i;//初始化最大值索引為根節(jié)點

intleft=2i+1;//左子節(jié)點索引

intright=2i+2;//右子節(jié)點索引

//如果左子節(jié)點存在且大于當前最大值,則更新最大值索引

if(left<n&&arr[largest]<arr[left]){

largest=left;

}

//如果右子節(jié)點存在且大于當前最大值,則更新最大值索引

if(right<n&&arr[largest]<arr[right]){

largest=right;

}

//如果最大值索引不是當前根節(jié)點索引,則需要交換

if(largest!=i){

std::swap(arr[i],arr[largest]);//交換根節(jié)點與最大值子節(jié)點

//交換后,子樹可能不再滿足堆的性質(zhì),因此遞歸調(diào)用heapify

//遞歸調(diào)整的是交換后位于原最大值位置的子樹

heapify(arr,n,largest);

}

}

voidheap_sort(std::vector<int>&arr){

intn=arr.size();

//1.建堆:自底向上調(diào)整所有非葉子節(jié)點

//從最后一個非葉子節(jié)點開始,到根節(jié)點為止

for(inti=n/2-1;i>=0;i--){

heapify(arr,n,i);

}

//2.排序:重復(fù)取出堆頂元素并調(diào)整堆

for(inti=n-1;i>0;i--){

//將當前未排序部分的堆頂(最大值)與末尾元素交換

std::swap(arr[0],arr[i]);

//調(diào)整剩余i個元素(索引0到i-1)為大頂堆

//注意:此時堆的有效大小為i

heapify(arr,i,0);

}

//排序完成,arr已經(jīng)是有序數(shù)組

}

```

四、堆排序性能分析

(一)時間復(fù)雜度

堆排序的時間復(fù)雜度是其性能評估的關(guān)鍵指標。分析如下:

1.建堆階段:

(1)建堆過程需要調(diào)整從最后一個非葉子節(jié)點到根節(jié)點的所有節(jié)點。假設(shè)數(shù)組長度為`n`,最后一個非葉子節(jié)點的索引為`n//2-1`。

(2)對于每個需要調(diào)整的節(jié)點`i`,執(zhí)行`heapify`操作。`heapify`操作在最壞情況下需要O(logi)的時間復(fù)雜度(即節(jié)點深度)。

(3)由于建堆過程是從深度較小的節(jié)點向深度較大的節(jié)點進行的,可以證明建堆的總時間復(fù)雜度為O(n)。雖然單個`heapify`是O(logn),但建堆時節(jié)點調(diào)整的“成本”隨著深度增加而遞減,累加總成本為O(n)。

2.排序階段:

(1)排序過程需要執(zhí)行`n-1`次交換操作(每次將最大值移到末尾)。

(2)每次交換后,需要執(zhí)行一次`heapify`操作來調(diào)整剩余`n-i`個元素,使其重新成為大頂堆。這部分`heapify`操作的時間復(fù)雜度為O(log(n-i))。

(3)因此,排序階段的總時間復(fù)雜度為`O((n-1)log(n-i))`。通過變換和積分計算,可以證明這部分的總時間復(fù)雜度也是O(nlogn)。

3.綜合時間復(fù)雜度:將建堆和排序階段的時間復(fù)雜度相加,堆排序的總時間復(fù)雜度為O(n)+O(nlogn)=O(nlogn)。

4.不同情況:無論是最好情況、平均情況還是最壞情況,堆排序的時間復(fù)雜度都保持為O(nlogn),這使得它成為一種非常穩(wěn)定的排序性能算法。與其他O(nlogn)排序算法(如快速排序的平均情況)相比,堆排序的最壞情況性能更優(yōu)。

(二)空間復(fù)雜度

堆排序的空間復(fù)雜度衡量了算法執(zhí)行過程中所需的額外存儲空間。

1.原地排序:堆排序是原地排序算法。它主要通過在輸入數(shù)組內(nèi)部進行元素的交換和下濾(下沉)操作來實現(xiàn)排序,不需要分配額外的數(shù)組或數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)。

2.遞歸??臻g(可能):在某些實現(xiàn)中(如Python的遞歸`heapify`或C++的遞歸`heapify`),如果數(shù)組很大,遞歸調(diào)用`heapify`可能會消耗與堆的高度(約為O(logn))相關(guān)的??臻g。然而,這通常被認為是技術(shù)上的O(logn)空間復(fù)雜度,而不是算法本身的O(1)原地排序空間。

3.主要空間復(fù)雜度:從主要的空間消耗來看,堆排序只需要常數(shù)個變量來存儲索引和臨時值,因此其輔助空間復(fù)雜度(或稱空間復(fù)雜度)為O(1)。這意味著它的內(nèi)存占用非常小,不受輸入數(shù)據(jù)規(guī)模的影響。

五、總結(jié)

堆排序是一種基于堆數(shù)據(jù)結(jié)構(gòu)的、效率高且穩(wěn)定的排序算法。通過將數(shù)組先構(gòu)建成大頂堆,再反復(fù)取出堆頂元素并調(diào)整剩余部分為堆,最終實現(xiàn)排序。

(一)主要優(yōu)勢:

1.時間效率高:具有O(nlogn)的穩(wěn)定時間復(fù)雜度,適用于需要可預(yù)測性能的場景。

2.原地排序:空間復(fù)雜度為O(1),內(nèi)存占用低,適合內(nèi)存資源受限的環(huán)境。

3.實現(xiàn)相對簡單:核心操作是`heapify`,邏輯清晰。

(二)主要劣勢:

1.不穩(wěn)定性:相等元素的相對順序可能會改變,不適合需要穩(wěn)定排序的應(yīng)用。

2.建堆過程相對復(fù)雜:需要理解完全二叉樹的結(jié)構(gòu)和自底向上的調(diào)整過程。

3.非適應(yīng)性:其時間復(fù)雜度不隨輸入數(shù)據(jù)的初始順序改變而改變,對于已經(jīng)有序或部分有序的數(shù)據(jù),性能不會得到顯著提升(仍然為O(nlogn))。

(三)適用場景:

1.大規(guī)模數(shù)據(jù)排序:當數(shù)據(jù)量較大,且對排序穩(wěn)定性要求不高時,堆排序是一個不錯的選擇。

2.內(nèi)存受限環(huán)境:由于其O(1)的空間復(fù)雜度,非常適合內(nèi)存資源有限的應(yīng)用。

3.需要穩(wěn)定O(nlogn)性能:在需要保證排序時間復(fù)雜度不會因數(shù)據(jù)特性變差的情況下,堆排序優(yōu)于快速排序等依賴數(shù)據(jù)特性的算法。

總體而言,堆排序是一種實用且高效的排序算法,掌握其原理和實現(xiàn)對于理解高級排序技術(shù)具有重要意義。在實際應(yīng)用中,應(yīng)根據(jù)具體需求(如數(shù)據(jù)規(guī)模、穩(wěn)定性要求、內(nèi)存限制等)選擇合適的排序算法。

一、堆排序概述

堆排序是一種基于堆數(shù)據(jù)結(jié)構(gòu)的比較類排序算法,具有效率高、實現(xiàn)簡單等特點。堆排序的基本思想是將待排序序列構(gòu)造成一個大頂堆,此時,整個序列的最大值就是堆頂?shù)母?jié)點。然后將其與末尾元素進行交換,此時末尾就為最大值。之后將剩余n-1個元素重新構(gòu)造成一個堆,這樣會得到n個元素的次小值。如此反復(fù)執(zhí)行,便能得到一個有序序列。

堆排序主要包含兩大步驟:建堆和堆調(diào)整。下面將詳細介紹堆排序的具體實現(xiàn)方案。

二、堆排序?qū)崿F(xiàn)步驟

(一)建堆

1.初始化堆:將待排序序列視為一棵完全二叉樹,從最后一個非葉子節(jié)點開始,依次向前調(diào)整每個節(jié)點,使其滿足堆的性質(zhì)。

2.調(diào)整堆:對于每個節(jié)點,比較其與左右子節(jié)點的值,若不滿足大頂堆的性質(zhì)(即當前節(jié)點值小于左右子節(jié)點值),則與較大子節(jié)點交換,并繼續(xù)調(diào)整交換后的子節(jié)點,直到滿足堆的性質(zhì)。

(二)堆調(diào)整

1.交換堆頂與末尾元素:將當前堆頂元素與末尾元素交換,此時末尾元素為最大值。

2.縮小堆范圍:將堆的范圍縮小至前n-1個元素,并重新調(diào)整堆頂元素,使其滿足堆的性質(zhì)。

3.重復(fù)調(diào)整:重復(fù)步驟2,直到堆的范圍縮小為1,此時序列已有序。

三、堆排序算法實現(xiàn)

(一)Python實現(xiàn)

defheapify(arr,n,i):

largest=i

left=2i+1

right=2i+2

ifleft<nandarr[i]<arr[left]:

largest=left

ifright<nandarr[largest]<arr[right]:

largest=right

iflargest!=i:

arr[i],arr[largest]=arr[largest],arr[i]

heapify(arr,n,largest)

defheap_sort(arr):

n=len(arr)

foriinrange(n//2-1,-1,-1):

heapify(arr,n,i)

foriinrange(n-1,0,-1):

arr[i],arr[0]=arr[0],arr[i]

heapify(arr,i,0)

returnarr

(二)C++實現(xiàn)

include<vector>

include<algorithm>

voidheapify(std::vector<int>&arr,intn,inti){

intlargest=i;

intleft=2i+1;

intright=2i+2;

if(left<n&&arr[left]>arr[largest])

largest=left;

if(right<n&&arr[right]>arr[largest])

largest=right;

if(largest!=i){

std::swap(arr[i],arr[largest]);

heapify(arr,n,largest);

}

}

voidheap_sort(std::vector<int>&arr){

intn=arr.size();

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

heapify(arr,n,i);

for(inti=n-1;i>0;i--){

std::swap(arr[0],arr[i]);

heapify(arr,i,0);

}

}

四、堆排序性能分析

(一)時間復(fù)雜度

堆排序的時間復(fù)雜度為O(nlogn),其中n為待排序序列的長度。這是因為建堆的時間復(fù)雜度為O(n),每次堆調(diào)整的時間復(fù)雜度為O(logn),總共需要調(diào)整n-1次。

(二)空間復(fù)雜度

堆排序的空間復(fù)雜度為O(1),因為堆排序是原地排序算法,不需要額外的存儲空間。

五、總結(jié)

堆排序是一種高效的排序算法,具有時間復(fù)雜度穩(wěn)定、實現(xiàn)簡單等優(yōu)點。通過本文的介紹,相信讀者已經(jīng)掌握了堆排序的基本原理和實現(xiàn)方法。在實際應(yīng)用中,可以根據(jù)具體需求選擇合適的排序算法,以達到最佳的性能表現(xiàn)。

一、堆排序概述

堆排序是一種基于堆(Heap)數(shù)據(jù)結(jié)構(gòu)的比較類排序算法。堆是一種特殊的樹形數(shù)據(jù)結(jié)構(gòu),通常實現(xiàn)為數(shù)組,具有以下核心特征:

(一)堆的性質(zhì)

1.形狀性質(zhì):堆通常是一棵完全二叉樹。這意味著除了最底層之外,其他每一層都已經(jīng)被完全填滿,并且最底層的節(jié)點都集中在左側(cè)。

2.堆序性質(zhì)(HeapProperty):根據(jù)堆的類型,堆序性質(zhì)有所不同。

(1)大頂堆(Max-Heap):對于堆中的任意節(jié)點`i`,其值總是大于或等于其子節(jié)點的值。在最大堆中,樹的根節(jié)點擁有最大值。

(2)小頂堆(Min-Heap):對于堆中的任意節(jié)點`i`,其值總是小于或等于其子節(jié)點的值。在小頂堆中,樹的根節(jié)點擁有最小值。

堆排序通常使用大頂堆實現(xiàn)。

(二)堆排序的基本思想

堆排序的過程可以分為兩個主要階段:

1.建堆(BuildHeap):將一個無序的輸入數(shù)組重新組織成一個大頂堆。這一步確保了堆頂元素是當前未排序元素中的最大值。

2.排序(Sort):通過反復(fù)將堆頂元素(當前最大值)與堆的最后一個元素交換,并調(diào)整剩余元素構(gòu)成的子堆,使其重新滿足堆的性質(zhì),從而逐步構(gòu)建出有序序列。

(三)堆排序的特點

1.效率高:堆排序的最壞、平均和最好時間復(fù)雜度均為O(nlogn),在所有比較排序中具有較好的性能。

2.不穩(wěn)定性:堆排序是一種不穩(wěn)定的排序算法。這意味著相等元素的相對順序在排序后可能會改變。

3.原地排序:堆排序是原地排序算法,其空間復(fù)雜度為O(1),只需要常數(shù)級的額外空間(除了輸入數(shù)組本身)。這使得它在內(nèi)存使用上非常高效。

4.不是內(nèi)部排序:由于建堆過程可能需要O(n)的時間復(fù)雜度(特別是通過自底向上的方法),而交換和調(diào)整堆的過程需要O(logn)的時間復(fù)雜度,堆排序通常被認為是介于內(nèi)部排序和外部排序之間的一種算法,盡管其常用于內(nèi)存排序。

二、堆排序?qū)崿F(xiàn)步驟

堆排序的成功實現(xiàn)依賴于對堆結(jié)構(gòu)的深刻理解和準確的算法步驟。以下是詳細的實現(xiàn)步驟,主要圍繞大頂堆進行闡述。

(一)建堆(BuildHeap)

建堆的目的是將一個無序的數(shù)組轉(zhuǎn)換成一個大頂堆。這個過程需要從數(shù)組的最后一個非葉子節(jié)點開始,逐個向前調(diào)整節(jié)點,使其滿足大頂堆的性質(zhì)。

1.確定非葉子節(jié)點起點:

(1)在一個完全二叉樹中,最后一個非葉子節(jié)點的索引可以通過`n//2-1`計算得出,其中`n`是數(shù)組的長度。葉子節(jié)點位于索引`n//2`到`n-1`之間。

(2)從最后一個非葉子節(jié)點開始向前調(diào)整,是因為越靠近葉子節(jié)點的節(jié)點,其子節(jié)點越多,調(diào)整的必要性越大,離葉子節(jié)點越遠的節(jié)點(即靠近根節(jié)點的節(jié)點)調(diào)整時影響范圍更大,但子節(jié)點更少。

2.自底向上調(diào)整(SiftDown/Heapify):

(1)對于每一個從`n//2-1`到`0`的索引`i`,執(zhí)行調(diào)整操作。

(2)調(diào)整的目標是確保以`i`為根的子樹滿足大頂堆的性質(zhì)。

(3)對于當前節(jié)點`i`,首先確定其左右子節(jié)點的索引:左子節(jié)點索引為`2i+1`,右子節(jié)點索引為`2i+2`。

(4)比較當前節(jié)點`i`與其左右子節(jié)點的值。

(5)找出三個節(jié)點(節(jié)點`i`及其左右子節(jié)點)中的最大值。如果當前節(jié)點`i`的值不是最大值,則將其與最大值所在節(jié)點的值進行交換。

(6)關(guān)鍵點:交換后,雖然節(jié)點`i`的位置變了,但其子樹可能不再滿足堆的性質(zhì)。因此,必須繼續(xù)對交換后到達位置的新節(jié)點執(zhí)行同樣的調(diào)整過程。這個過程就像“沉降”一樣,將節(jié)點向下調(diào)整,直到其子樹滿足堆的性質(zhì)或到達葉子節(jié)點位置。

(7)重復(fù)步驟(4)到(6),直到節(jié)點`i`的子樹滿足堆的性質(zhì),或者`i`已經(jīng)是葉子節(jié)點。

3.建堆完成標志:當從索引`0`(根節(jié)點)開始,對所有`i`在`0`到`n//2-1`范圍內(nèi)的節(jié)點都執(zhí)行了上述調(diào)整后,整個數(shù)組就構(gòu)成了一棵大頂堆。

(二)堆調(diào)整(Heapify/SiftDown)

堆調(diào)整是堆排序的核心操作,用于在每次將堆頂元素取出后,重新調(diào)整剩余元素,使其重新滿足堆的性質(zhì)。這個操作通常在“排序”階段反復(fù)調(diào)用。

1.交換堆頂與末尾元素:

(1)堆頂元素(索引`0`)是當前未排序部分的最大值。

(2)將其與當前未排序部分的最后一個元素(索引`n-1`,其中`n`是當前待排序元素的數(shù)量)進行交換。

(3)交換后,末尾元素`arr[n-1]`獲得了當前未排序部分的最大值。此時,這部分元素(索引`0`到`n-2`)可能不再滿足堆的性質(zhì)。

2.縮小堆范圍并調(diào)整:

(1)將待排序元素的數(shù)量(即需要保持堆性質(zhì)的元素范圍)從`n`減少到`n-1`。

(2)對新的堆頂元素(原數(shù)組末尾元素,現(xiàn)在位于索引`0`)執(zhí)行“自底向上調(diào)整”(SiftDown)操作。

(3)調(diào)整過程與建堆中的自底向上調(diào)整相同:比較當前節(jié)點與其子節(jié)點,若不滿足大頂堆性質(zhì)則交換,并繼續(xù)對交換后的節(jié)點進行遞歸調(diào)整,直到滿足堆的性質(zhì)或到達葉子節(jié)點。

3.重復(fù)調(diào)整直至完成:

(1)重復(fù)步驟(1)和(2),每次將堆頂(當前最大值)與末尾元素交換,并對新的堆頂進行堆調(diào)整。

(2)這個過程會不斷減少需要保持堆性質(zhì)的元素范圍(`n`逐漸減小到`1`)。

(3)當范圍縮小為`1`時,即所有元素都已按從大到小排列在數(shù)組的末尾,排序過程完成。

三、堆排序算法實現(xiàn)

(一)Python實現(xiàn)

以下是使用Python實現(xiàn)堆排序的代碼示例,包含了`heapify`(用于調(diào)整堆)和`heap_sort`(主排序函數(shù))兩個核心函數(shù)。

```python

defheapify(arr,n,i):

"""調(diào)整以索引i為根的子樹,使其滿足大頂堆性質(zhì)。

arr:待排序的數(shù)組

n:堆的有效大小(即待調(diào)整的元素范圍)

i:當前子樹的根節(jié)點索引

"""

largest=i初始化最大值索引為根節(jié)點

left=2i+1左子節(jié)點索引

right=2i+2右子節(jié)點索引

如果左子節(jié)點存在且大于當前最大值,則更新最大值索引

ifleft<nandarr[i]<arr[left]:

largest=left

如果右子節(jié)點存在且大于當前最大值,則更新最大值索引

ifright<nandarr[largest]<arr[right]:

largest=right

如果最大值索引不是當前根節(jié)點索引,則需要交換

iflargest!=i:

arr[i],arr[largest]=arr[largest],arr[i]交換根節(jié)點與最大值子節(jié)點

交換后,子樹可能不再滿足堆的性質(zhì),因此遞歸調(diào)用heapify

遞歸調(diào)整的是交換后位于原最大值位置的子樹

heapify(arr,n,largest)

defheap_sort(arr):

"""對數(shù)組arr進行堆排序。

arr:待排序的數(shù)組

"""

n=len(arr)

1.建堆:自底向上調(diào)整所有非葉子節(jié)點

從最后一個非葉子節(jié)點開始,到根節(jié)點為止

foriinrange(n//2-1,-1,-1):

heapify(arr,n,i)

2.排序:重復(fù)取出堆頂元素并調(diào)整堆

foriinrange(n-1,0,-1):

將當前未排序部分的堆頂(最大值)與末尾元素交換

arr[i],arr[0]=arr[0],arr[i]

調(diào)整剩余n-i個元素(索引0到i-1)為大頂堆

注意:此時堆的有效大小為n-i

heapify(arr,i,0)

排序完成,arr已經(jīng)是有序數(shù)組

returnarr

```

(二)C++實現(xiàn)

以下是使用C++實現(xiàn)堆排序的代碼示例,同樣包含了`heapify`和`heap_sort`函數(shù)。注意C++中使用`std::vector`來表示數(shù)組。

```cpp

include<vector>

include<algorithm>//用于std::swap

voidheapify(std::vector<int>&arr,intn,inti){

intlargest=i;//初始化最大值索引為根節(jié)點

intleft=2i+1;//左子節(jié)點索引

intright=2i+2;//右子節(jié)點索引

//如果左子節(jié)點存在且大于當前最大值,則更新最大值索引

if(left<n&&arr[largest]<arr[left]){

largest=left;

}

//如果右子節(jié)點存在且大于當前最大值,則更新最大值索引

if(right<n&&arr[largest]<arr[right]){

largest=right;

}

//如果最大值索引不是當前根節(jié)點索引,則需要交換

if(largest!=i){

std::swap(arr[i],arr[largest]);//交換根節(jié)點與最大值子節(jié)點

//交換后,子樹可能不再滿足堆的性質(zhì),因此遞歸調(diào)用heapify

//遞歸調(diào)整的是交換后位于原最大值位置的子樹

heapify(arr,n,largest);

}

}

voidheap_sort(std::vector<int>&arr){

intn=arr.size();

//1.建堆:自底向上調(diào)整所有非葉子節(jié)點

//從最后一個非葉子節(jié)點開始,到根節(jié)點為止

for(inti=n/2-1;i>=0;i--){

heapify(arr,n,i);

}

//2.排序:重復(fù)取出堆頂元素并調(diào)整堆

for(inti=n-1;i>0;i--){

//將當前未排序部分的堆頂(最大值)與末尾元素交換

std::swap(arr[0],arr[i]);

//調(diào)整剩余i個元素(索引0到i-1)為大頂堆

//注意:此時堆的有效大小為i

heapify(arr,i,0);

}

//排序完成,arr已經(jīng)是有序數(shù)組

}

```

四、堆排序性能分析

(一)時間復(fù)雜度

堆排序的時間復(fù)雜度是其性能評估的關(guān)鍵指標。分析如下:

1.建堆階段:

(1)建堆過程需要調(diào)整從最后一個非葉子節(jié)點到根節(jié)點的所有節(jié)點。假設(shè)數(shù)組長度為`n`,最后一個非葉子節(jié)點的索引為`n//2-1`。

(2)對于每個需要調(diào)整的節(jié)點`i`,執(zhí)行`heapify`

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論