鍍金池/ 教程/ Python/ 標(biāo)準(zhǔn)庫 (4)
標(biāo)準(zhǔn)庫 (4)
如何成為 Python 高手
標(biāo)準(zhǔn)庫 (6)
標(biāo)準(zhǔn)庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準(zhǔn)備
函數(shù)練習(xí)
標(biāo)準(zhǔn)庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標(biāo)準(zhǔn)庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標(biāo)準(zhǔn)庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準(zhǔn)備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標(biāo)準(zhǔn)庫 (2)
列表(2)
元組

標(biāo)準(zhǔn)庫 (4)

heapq

堆(heap),是一種數(shù)據(jù)結(jié)構(gòu)。用維基百科中的說明:

堆(英語:heap),是計算機科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱。堆通常是一個可以被看做一棵樹的數(shù)組對象。

對于這個新的概念,讀者不要感覺心慌意亂或者恐懼,因為它本質(zhì)上不是新東西,而是在我們已經(jīng)熟知的知識基礎(chǔ)上的擴展。

堆的實現(xiàn)是通過構(gòu)造二叉堆,也就是一種二叉樹。

基本知識

這是一顆在蘇州很常見的香樟樹,馬路兩邊、公園里隨處可見。

http://wiki.jikexueyuan.com/project/start-learning-python/images/22301.jpg" alt="" />

但是,在編程中,我們常說的樹通常不是上圖那樣的,而是這樣的:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22302.jpg" alt="" />

跟真實現(xiàn)實生活中看到的樹反過來,也就是“根”在上面。為什么這樣呢?我想主要是畫著更方便吧。但是,我覺這棵樹,是完全寫實的作品。我本人做為一名隱姓埋名多年的抽象派畫家,不喜歡這樣的樹,我畫出來的是這樣的:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22303.jpg" alt="" />

這棵樹有兩根枝杈,可不要小看這兩根枝杈哦,《道德經(jīng)》上不是說“一生二,二生三,三生萬物”。一就是下面那個干,二就是兩個枝杈,每個枝杈還可以看做下一個一,然后再有兩個枝杈,如此不斷重復(fù)(這簡直就是遞歸呀),就成為了一棵大樹。

我的確很佩服我自己的后現(xiàn)代抽象派的作品。但是,我更喜歡把這棵樹畫成這樣:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22304.jpg" alt="" />

并且給它一個正規(guī)的名字:二叉樹

http://wiki.jikexueyuan.com/project/start-learning-python/images/22305.jpg" alt="" />

這個也是二叉樹,完全脫胎于我所畫的后現(xiàn)代抽象主義作品。但是略有不同,這幅圖在各個枝杈上顯示的是數(shù)字。這種類型的“樹”就編程語言中所說的二叉樹,維基百科曰:

在計算機科學(xué)中,二叉樹(英語:Binary tree)是每個節(jié)點最多有兩個子樹的樹結(jié)構(gòu)。通常子樹被稱作「左子樹」(left subtree)和「右子樹」(right subtree)。二叉樹常被用於實現(xiàn)二叉查找樹和二叉堆。

在上圖的二叉樹中,最頂端的那個數(shù)字就相當(dāng)于樹根,也就稱作“根”。每個數(shù)字所在位置成為一個節(jié)點,每個節(jié)點向下分散出兩個“子節(jié)點”。就上圖的二叉樹,在最后一層,并不是所有節(jié)點都有兩個子節(jié)點,這類二叉樹又稱為完全二叉樹(Complete Binary Tree),也有的二叉樹,所有的節(jié)點都有兩個子節(jié)點,這類二叉樹稱作滿二叉樹(Full Binarry Tree),如下圖:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22306.jpg" alt="" />

下面討論的對象是實現(xiàn)二叉堆就是通過二叉樹實現(xiàn)的。其應(yīng)該具有如下特點:

  • 節(jié)點的值大于等于(或者小于等于)任何子節(jié)點的值。
  • 節(jié)點左子樹和右子樹是一個二叉堆。如果父節(jié)點的值總大于等于任何一個子節(jié)點的值,其為最大堆;若父節(jié)點值總小于等于子節(jié)點值,為最小堆。上面圖示中的完全二叉樹,就表示一個最小堆。

堆的類型還有別的,如斐波那契堆等,但很少用。所以,通常就將二叉堆也說成堆。下面所說的堆,就是二叉堆。而二叉堆又是用二叉樹實現(xiàn)的。

堆的存儲

堆用列表(有的語言中成為數(shù)組)來表示。如下圖所示:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22307.jpg" alt="" />

從圖示中可以看出,將邏輯結(jié)構(gòu)中的樹的節(jié)點數(shù)字依次填入到存儲結(jié)構(gòu)中。看這個圖,似乎是列表中按照順序進行排列似的。但是,這僅僅由于那個樹的特點造成的,如果是下面的樹:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22308.jpg" alt="" />

如果將上面的邏輯結(jié)構(gòu)轉(zhuǎn)換為存儲結(jié)構(gòu),讀者就能看出來了,不再是按照順序排列的了。

關(guān)于堆的各種,如插入、刪除、排序等,本節(jié)不會專門講授編碼方法,讀者可以參與有關(guān)資料。但是,下面要介紹如何用 Python 中的模塊 heapq 來實現(xiàn)這些操作。

heapq 模塊

heapq 中的 heap 是堆,q 就是 queue(隊列)的縮寫。此模塊包括:

>>> import heapq
>>> heapq.__all__
['heappush', 'heappop', 'heapify', 'heapreplace', 'merge', 'nlargest', 'nsmallest', 'heappushpop']

依次查看這些函數(shù)的使用方法。

heappush(heap, x):將 x 壓入對 heap(這是一個列表)

Help on built-in function heappush in module _heapq:

heappush(...)
    heappush(heap, item) -> None. Push item onto heap, maintaining the heap invariant.

>>> import heapq
>>> heap = []    
>>> heapq.heappush(heap, 3)
>>> heapq.heappush(heap, 9)
>>> heapq.heappush(heap, 2)
>>> heapq.heappush(heap, 4)
>>> heapq.heappush(heap, 0)
>>> heapq.heappush(heap, 8)
>>> heap
[0, 2, 3, 9, 4, 8]

請讀者注意我上面的操作,在向堆增加數(shù)值的時候,我并沒有嚴(yán)格按照什么順序,是隨意的。但是,當(dāng)我查看堆的數(shù)據(jù)時,顯示給我的是一個有一定順序的數(shù)據(jù)結(jié)構(gòu)。這種順序不是按照從小到大,而是按照前面所說的完全二叉樹的方式排列。顯示的是存儲結(jié)構(gòu),可以把它還原為邏輯結(jié)構(gòu),看看是不是一顆二叉樹。

http://wiki.jikexueyuan.com/project/start-learning-python/images/22309.jpg" alt="" />

由此可知,利用 heappush() 函數(shù)將數(shù)據(jù)放到堆里面之后,會自動按照二叉樹的結(jié)構(gòu)進行存儲。

heappop(heap):刪除最小元素

承接上面的操作:

>>> heapq.heappop(heap)
0
>>> heap
[2, 4, 3, 9, 8]

heappop() 函數(shù),從 heap 堆中刪除了一個最小元素,并且返回該值。但是,這時候的 heap 顯示順序,并非簡單地將 0 去除,而是按照完全二叉樹的規(guī)范重新進行排列。

heapify():將列表轉(zhuǎn)換為堆

如果已經(jīng)建立了一個列表,利用 heapify() 可以將列表直接轉(zhuǎn)化為堆。

>>> hl = [2, 4, 6, 8, 9, 0, 1, 5, 3]
>>> heapq.heapify(hl)
>>> hl
[0, 3, 1, 4, 9, 6, 2, 5, 8]

經(jīng)過這樣的操作,列表 hl 就變成了堆(注意觀察堆的順序,和列表不同),可以對 hl(堆)使用 heappop() 或者 heappush() 等函數(shù)了。否則,不可。

>>> heapq.heappop(hl)
0
>>> heapq.heappop(hl)
1
>>> hl
[2, 3, 5, 4, 9, 6, 8]
>>> heapq.heappush(hl, 9)
>>> hl
[2, 3, 5, 4, 9, 6, 8, 9]

不要認(rèn)為堆里面只能放數(shù)字,之所以用數(shù)字,是因為對它的邏輯結(jié)構(gòu)比較好理解。

>>> heapq.heappush(hl, "q")
>>> hl
[2, 3, 5, 4, 9, 6, 8, 9, 'q']
>>> heapq.heappush(hl, "w")
>>> hl
[2, 3, 5, 4, 9, 6, 8, 9, 'q', 'w']

heapreplace()

是 heappop() 和 heappush() 的聯(lián)合,也就是刪除一個,同時加入一個。例如:

>>> heap
[2, 4, 3, 9, 8]
>>> heapq.heapreplace(heap, 3.14)
2
>>> heap
[3, 4, 3.14, 9, 8]

先簡單羅列關(guān)于對的幾個常用函數(shù)。那么堆在編程實踐中的用途在哪方面呢?主要在排序上。一提到排序,讀者肯定想到的是 sorted() 或者列表中的 sort(),不錯,這兩個都是常用的函數(shù),而且在一般情況下已經(jīng)足夠使用了。如果再使用堆排序,相對上述方法應(yīng)該有優(yōu)勢。

堆排序的優(yōu)勢不僅更快,更重要的是有效地使用內(nèi)存,當(dāng)然,另外一個也不同忽視,就是簡單易用。比如前面操作的,刪除數(shù)列中最小的值,就是在排序基礎(chǔ)上進行的操作。

deque 模塊

有這樣一個問題:一個列表,比如是[1,2,3],我打算在最右邊增加一個數(shù)字。

這也太簡單了,不就是用 append() 這個內(nèi)建函數(shù),追加一個嗎?

這是簡單,我要得寸進尺,能不能在最左邊增加一個數(shù)字呢?

這個嘛,應(yīng)該有辦法。不過得想想了。讀者在向下閱讀的時候,能不能想出一個方法來?

>>> lst = [1, 2, 3]
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
>>> nl = [7]
>>> nl.extend(lst)
>>> nl
[7, 1, 2, 3, 4]

你或許還有別的方法。但是,Python 為我們提供了一個更簡單的模塊,來解決這個問題。

>>> from collections import deque

這次用這種引用方法,因為 collections 模塊中東西很多,我們只用到 deque。

>>> lst
[1, 2, 3, 4]

還是這個列表。試試分別從右邊和左邊增加數(shù)

>>> qlst = deque(lst)

這是必須的,將列表轉(zhuǎn)化為 deque。deque 在漢語中有一個名字,叫做“雙端隊列”(double-ended queue)。

>>> qlst.append(5)        #從右邊增加
>>> qlst
deque([1, 2, 3, 4, 5])
>>> qlst.appendleft(7)    #從左邊增加
>>> qlst
deque([7, 1, 2, 3, 4, 5])

這樣操作多么容易呀。繼續(xù)看刪除:

>>> qlst.pop()
5
>>> qlst
deque([7, 1, 2, 3, 4])
>>> qlst.popleft()
7
>>> qlst
deque([1, 2, 3, 4])

刪除也分左右。下面這個,請讀者仔細觀察,更有點意思。

>>> qlst.rotate(3)
>>> qlst
deque([2, 3, 4, 1])

rotate() 的功能是將[1, 2, 3, 4]的首位連起來,你就想象一個圓環(huán),在上面有 1,2,3,4 幾個數(shù)字。如果一開始正對著你的是 1,依順時針方向排列,就是從 1 開始的數(shù)列,如下圖所示:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22310.jpg" alt="" />

經(jīng)過 rotate(),這個環(huán)就發(fā)生旋轉(zhuǎn)了,如果是 rotate(3),表示每個數(shù)字按照順時針方向前進三個位置,于是變成了:

http://wiki.jikexueyuan.com/project/start-learning-python/images/22311.jpg" alt="" />

請原諒我的后現(xiàn)代注意超級抽象派作圖方式。從圖中可以看出,數(shù)列變成了[2, 3, 4, 1]。rotate() 作用就好像在撥轉(zhuǎn)這個圓環(huán)。

>>> qlst
deque([3, 4, 1, 2])
>>> qlst.rotate(-1)
>>> qlst
deque([4, 1, 2, 3])

如果參數(shù)是復(fù)數(shù),那么就逆時針轉(zhuǎn)。

在 deque 中,還有 extend 和 extendleft 方法。讀者可自己調(diào)試。


總目錄   |   上節(jié):標(biāo)準(zhǔn)庫(3)   |   下節(jié):標(biāo)準(zhǔn)庫(5)

如果你認(rèn)為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。

上一篇:語句(5)下一篇:標(biāo)準(zhǔn)庫 (2)