日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python堆堆乐教程_python堆排序,详细过程图和讲解,这样做小白都会

發布時間:2025/3/15 python 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python堆堆乐教程_python堆排序,详细过程图和讲解,这样做小白都会 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

### 正文前的扯淡

之前電話面試一個公司時,面試官讓寫一個堆排序,遺憾的是我忘了堆排序的思想了,所以直接說不會寫,這次電面也以失敗告終...知恥后勇,這幾天在網上找了很多寫堆排序的帖子,但是帖子質量不好,堆排序是什么不介紹,代碼也非常不詳細,看了半天沒整明白,不過好在今天找出了數據結構課的課本,系統復習后,嘗試用Python寫出了一個堆排序。

### 目錄

* 堆排序介紹

* 堆排序算法詳解+Python實現

### 堆排序涉及到的概念

* 堆排序是利用 **堆**進行排序的

* **堆**是一種完全二叉樹

* **堆**有兩種類型: **大根堆** **小根堆**

* 兩種類型的概念如下:

大根堆:每個結點的值都大于或等于左右孩子結點

小根堆:每個結點的值都小于或等于左右孩子結點

因為比較抽象,所以專門花了兩個圖表示

![image](https://img-blog.csdnimg.cn/img_convert/f7a894c339ed9348cf9a97906c9610a5.webp?x-oss-process=image/format,png)

![image](https://img-blog.csdnimg.cn/img_convert/ac71b6eda5f62a60620876ec93210111.webp?x-oss-process=image/format,png)

那么,什么是完全二叉樹呢?

**完全二叉樹** 是 一種除了最后一層之外的其他每一層都被完全填充,并且所有結點都保持向左對齊的樹,向左對齊指的是:

![image](https://img-blog.csdnimg.cn/img_convert/75abc3b514411ac39e031cedbc7c68d0.webp?x-oss-process=image/format,png)

像這樣的樹就不是完全二叉樹:

![image](https://img-blog.csdnimg.cn/img_convert/ac70745f035a922049eb317be3f9d3cc.webp?x-oss-process=image/format,png)

如果給上面的大小根堆的根節點從1開始編號,則滿足下面關系(下圖就滿足這個關系):

![image](https://img-blog.csdnimg.cn/img_convert/742a1e5609602ea5d8453f1956d285f4.webp?x-oss-process=image/format,png)

如果把這些數字放入數組中,則如下圖所示:其中,上面的數字是數組下標值,第一個元素占位用。

![image](https://img-blog.csdnimg.cn/img_convert/154eddedd06cad92c6cae1d93c4446fb.webp?x-oss-process=image/format,png)

### 堆排序算法詳解+Python實現

了解了堆。下面我們來看下堆排序的思想是怎樣的(以大根堆為例):

* 首先將待排序的數組構造出一個大根堆

* 取出這個大根堆的堆頂節點(最大值),與堆的最下最右的元素進行交換,然后把剩下的元素再構造出一個大根堆

* 重復第二步,直到這個大根堆的長度為1,此時完成排序。

### 下面通過圖片來看下,第二個步驟是如何進行的:

![image](https://img-blog.csdnimg.cn/img_convert/a6f1f637bd0e79f789dc854d5dc1954b.webp?x-oss-process=image/format,png)

![image](https://img-blog.csdnimg.cn/img_convert/ed049e92d121544f93ce438f20a33ad9.webp?x-oss-process=image/format,png)

![image](https://img-blog.csdnimg.cn/img_convert/ef8f183d934833cfdde9cee64e13999e.webp?x-oss-process=image/format,png)

這就是構建大根堆的思想,了解了之后就可以進行編碼,編碼主要解決兩個問題:

* 如何把一個序列構造出一個大根堆

* 輸出堆頂元素后,如何使剩下的元素構造出一個大根堆

根據問題進行編碼,由于數組下標是從0開始的,而樹的節點從1開始,我們還需要引入一個輔助位置,Python提供的原始數據類型list實際上是一個線性表(Array),由于我們需要在序列最左邊追加一個輔助位,線性表這樣做的話開銷很大,需要把數組整體向右移動,所以list類型沒有提供形如`appendleft`的函數,但是在一個鏈表里做這種操作就很簡單了,Python的`collections`庫里提供了鏈表結構`deque`,我們先使用它初始化一個無序序列:

```

from collections import deque

L = deque([50, 16, 30, 10, 60, 90, 2, 80, 70])

L.appendleft(0)

```

此時L如下:

```

In [2]: L

Out[2]: deque([0, 50, 16, 30, 10, 60, 90, 2, 80, 70])

```

根據我們上面找出的兩個難點,可以先編出`heap_sort`函數:

```

def heap_sort(L):

L_length = len(L) - 1

first_sort_count = L_length / 2

for i in range(first_sort_count):

heap_adjust(L, first_sort_count - i, L_length)

for i in range(L_length - 1):

L = swap_param(L, 1, L_length - i)

heap_adjust(L, 1, L_length - i - 1)

return [L[i] for i in range(1, len(L))]

```

講解:

* 因為引入了一個輔助空間,所以使`L_length = len(L) - 1`

* 第一個循環做的事情是把序列調整為一個大根堆(`heap_adjust`函數)

* 第二個循環是把堆頂元素和堆末尾的元素交換(`swap_param`函數),然后把剩下的元素調整為一個大根堆(`heap_adjust`函數)

我們要排序的序列為`deque([50, 16, 30, 10, 60, 90, 2, 80, 70])`,但是在第一個循環中,我們用了一個輔助變量`first_sort_count`,循環時,這個值變化的順序是**4->3->2->1**,這是為什么呢。實際上,這些數字代表的是有孩子的節點,從下圖可以看出,而我們所謂的調整大根堆,其實就是按照從右往左,從下到上的順序,把每顆小樹調整為一個大根堆。**4->3->2->1**的調整,其實就是**10->30->16->50**的調整。

![image](https://img-blog.csdnimg.cn/img_convert/0bf59643cbb516a39cbdb2bda531ade0.webp?x-oss-process=image/format,png)

**swap_param**函數很簡單,我們根據Python的特點,無需引入中間變量,直接交換堆頂元素和最后元素即可,代碼如下:

```

def swap_param(L, i, j):

L[i], L[j] = L[j], L[i]

return L

```

下面讓我們看下最關鍵的堆調整函數`heap_adjust`:

```

def heap_adjust(L, start, end):

temp = L[start]

i = start

j = 2 * i

while j <= end:

if (j < end) and (L[j] < L[j + 1]):

j += 1

if temp < L[j]:

L[i] = L[j]

i = j

j = 2 * i

else:

break

L[i] = temp

```

這段代碼比較抽象,我們結合實際例子把自己想象成一個解釋器來看一下:

![image](https://img-blog.csdnimg.cn/img_convert/80ce27261e252d5dbe518a8e4c022014.webp?x-oss-process=image/format,png)

* 第一個循環在第一個調用這個函數時,start=4, end=9,L=[0, 50, 16, 30, 10, 60, 90, 2, 80, 70],進行`temp = L[start]`,實際就是temp=L[4]=10,`i=start`, i此時為4,拿到我們要處理的樹節點,`j = 2*i`,j此時得到第四個節點的左子樹坐標,接著開始循環,循環條件`j <= end`代表在調整完整棵樹樹之前一直進行循環。第一個條件`if (j < end) and (L[j] < L[j + 1])`是要保證 **j** 取到較大子樹的坐標,由于左子樹大于右子樹,所以這個**if**表達式不進行。

![image](https://img-blog.csdnimg.cn/img_convert/bd6d35aab95ba3a40029721a78dbd569.webp?x-oss-process=image/format,png)

第二個**if** 表達式,要做的如果根節點小于子樹的值,就把根節點和較大的子樹的值進行交換,`temp

這個函數其實就是把每個子樹的根節點和較大的子節點進行值交換。而且如果在左子樹 依然是根節點的情況下繼續進行調整。 讀者可以自己照著圖調整幾次就可以很好的理解代碼的含義了。

這樣調整4次后,這棵樹就變成了一個大根堆,此時序列變成了這樣:

![image](https://img-blog.csdnimg.cn/img_convert/7d803f06998f1ae6e8f76c92e537e075.webp?x-oss-process=image/format,png)

接下來進行第二個循環。

```

for i in range(L_length - 1):

L = swap_param(L, 1, L_length - i)

heap_adjust(L, 1, L_length - i - 1)

```

首先`L = swap_param(L, 1, L_length - i)`交換第一個節點和最后一個節點的值(因為我們引入了一個輔助空間,所以序列長度減1),此時序列變成了**[16, 80, 50, 70, 60, 30, 2, 10, 90]** 接下來對**[16, 80, 50, 70, 60, 30, 2, 10]**進行調整,由于我們之前已經把序列調整為了大根堆,所以此時循環條件變為從堆頂進行小范圍調整就可以。

這次調整后,堆變為:

![image](https://img-blog.csdnimg.cn/img_convert/e4682af1f23d2aada8677a36cbc16d1b.webp?x-oss-process=image/format,png)

![image](https://img-blog.csdnimg.cn/img_convert/35a1767034acb3d4177e4d27a4cb747f.webp?x-oss-process=image/format,png)

然后繼續把10和80進行交換,繼續調整,直到遍歷完整個序列為止。

完整代碼如下:

```

from collections import deque

def swap_param(L, i, j):

L[i], L[j] = L[j], L[i]

return L

def heap_adjust(L, start, end):

temp = L[start]

i = start

j = 2 * i

while j <= end:

if (j < end) and (L[j] < L[j + 1]):

j += 1

if temp < L[j]:

L[i] = L[j]

i = j

j = 2 * i

else:

break

L[i] = temp

def heap_sort(L):

L_length = len(L) - 1

first_sort_count = L_length / 2

for i in range(first_sort_count):

heap_adjust(L, first_sort_count - i, L_length)

for i in range(L_length - 1):

L = swap_param(L, 1, L_length - i)

heap_adjust(L, 1, L_length - i - 1)

return [L[i] for i in range(1, len(L))]

def main():

L = deque([50, 16, 30, 10, 60, 90, 2, 80, 70])

L.appendleft(0)

print heap_sort(L)

if __name__ == '__main__':

main()

![QQ截圖20201210135040.png](https://upload-images.jianshu.io/upload_images/25205170-16fc095246556fda.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

```

運行結果如下:

```

python heap_sort2.py

[2, 10, 16, 30, 50, 60, 70, 80, 90]

```

推薦一下我建的python學習交流扣扣qun:850973621,群里有免費的視頻教程,開發工具、

電子書籍、項目源碼分享。一起交流學習,一起進步!

![QQ截圖20201205144328.png](https://img-blog.csdnimg.cn/img_convert/dc36d3b29bf986579276a7947e0a0b93.png)

一些小項目游戲

![QQ截圖20201210135040.png](https://img-blog.csdnimg.cn/img_convert/d6a14e104f6439ad58eeac348c9fb1ce.png)

電子書籍

![QQ截圖20201210135017.png](https://img-blog.csdnimg.cn/img_convert/4c660cdc9e3930c6f1a48984b2e875f9.png)

作者:一根薯條

鏈接:https://www.jianshu.com/p/d174f1862601

總結

以上是生活随笔為你收集整理的python堆堆乐教程_python堆排序,详细过程图和讲解,这样做小白都会的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。