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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python最大堆_用Python实现最大堆

發(fā)布時(shí)間:2025/4/5 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python最大堆_用Python实现最大堆 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文的內(nèi)容是如何通過二叉樹實(shí)現(xiàn)一個(gè)最大堆, 實(shí)現(xiàn)原理方面參考了Python的heap模塊. 此外, 在正式項(xiàng)目上, 我還是建議你使用python自帶的heap完成, 它只提供最小堆, 但是可以通過對所有元素取反或者重寫__lt__方法實(shí)現(xiàn)最大堆.

一. 堆的數(shù)據(jù)結(jié)構(gòu)

1. 數(shù)據(jù)結(jié)構(gòu)分析

堆的本質(zhì)就是一顆二叉樹, 這顆二叉樹必須具備以下兩個(gè)性質(zhì):

1). 對于最大堆來說, 二叉樹根節(jié)點(diǎn)的值不小于任何子節(jié)點(diǎn), 其所有子樹也符合這一特征, 最小堆則相反;

2). 堆是一顆完全二叉樹, 除了底層外, 所有層都盡可能地填滿, 底層元素從左到右排列.

上圖就是一個(gè)最大堆的二叉樹, 基于特性1我們可以得知, 這顆二叉樹從任意葉子節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑一定是一個(gè)遞增序列, 最大值為根節(jié)點(diǎn). 因此, 當(dāng)我們需要最大值時(shí), 取出根節(jié)點(diǎn)的值就行了. 當(dāng)我們新添加了一個(gè)葉子節(jié)點(diǎn)之后, 為了維護(hù)二叉樹的有序性, 我們可以讓這個(gè)葉子節(jié)點(diǎn)向頂端移動(dòng), 如下圖所示:

->

->

我們插入節(jié)點(diǎn)16后, 將這個(gè)節(jié)點(diǎn)的值與其父節(jié)點(diǎn)進(jìn)行比較, 大于父節(jié)點(diǎn)則二者交換, 持續(xù)這個(gè)操作直到不大于父節(jié)點(diǎn)或沒有父節(jié)點(diǎn)為止, 這樣, 我們就在插入元素之后, 仍然保持了二叉樹的有序性. 彈出節(jié)點(diǎn)同理, 將底層最后一個(gè)葉子節(jié)點(diǎn)取出填入空缺, 然后根據(jù)值的大小讓這個(gè)節(jié)點(diǎn)往下移動(dòng)就行.

因此, 堆在保證內(nèi)部有序性的前提下, 可以做到在O(k)的時(shí)間內(nèi)插入和彈出元素, k為二叉樹的高度. 這也就是為什么堆的二叉樹必須是完全二叉樹: 在這種情況下k最小, 為log n. 因此, 堆的插入和彈出都只需要O(log n)的時(shí)間復(fù)雜度, 可以高效地獲取最大值/最小值.

2. 通過列表實(shí)現(xiàn)二叉樹

由于堆是一顆完全二叉樹, 因此我們可以用一個(gè)列表來儲(chǔ)存這顆二叉樹的值:

如上圖所示, 我們用列表從上到下, 從左到右記錄了二叉樹的所有節(jié)點(diǎn). 二叉樹節(jié)點(diǎn)右邊的藍(lán)色數(shù)字是它在列表中的索引. 因此我們可以得知, 對于一個(gè)在列表中索引為n的節(jié)點(diǎn), 它的父節(jié)點(diǎn)索引為(n-1)//2, 它的左右子節(jié)點(diǎn)索引為n*2+1和n*2+2, 如果索引值溢出, 說明沒有對應(yīng)的父節(jié)點(diǎn)或子節(jié)點(diǎn). 這樣, 我們就通過列表儲(chǔ)存了這顆完全二叉樹的信息.

基于以上的分析, 我們先定義一個(gè)Heap類:

classHeap:def __init__(self, nums: [int] = None) ->None:

self.cache= nums or[]

self._heapify()def __len__(self) ->int:returnlen(self.cache)def __bool__(self) ->bool:return len(self) >0def __repr__(self) ->str:return f'heap({self.cache})'@propertydef largest(self) ->int:if notself.cache:raise Exception('Empty heap')returnself.cache[0]def show(self) ->None:#調(diào)用這個(gè)函數(shù)繪制一顆二叉樹出來,DEBUG用

height = int(math.log2(len(self))) + 1

for i inrange(height):

width= 2 ** (height - i) - 2

print(' ' * width, end='')

blank= ' ' * (width * 2 + 2)print(

blank.join(['{: >2d}'.format(num) for num in self.cache[2 ** i - 1:min(2 ** (i + 1) - 1, len(self))]]))print()def _swap(self, i: int, j: int) ->None:#這個(gè)方法交換二叉樹的兩個(gè)節(jié)點(diǎn)

self.cache[i], self.cache[j] = self.cache[j], self.cache[i]

二. 插入元素

這部分好像太簡單了, 我實(shí)在講不出來什么:

def push(self, num: int) ->None:

self.cache.append(num)

self._siftup(self.size- 1)def _siftup(self, i: int) ->None:while i >0:

parent= (i - 1) >> 1

if self.cache[i] <=self.cache[parent]:breakself._swap(i, parent)

i= parent

說白了, 當(dāng)我們push一個(gè)元素時(shí), 首先把這個(gè)元素放到列表的末端, 這相當(dāng)于在完全二叉樹上新建了一個(gè)葉子節(jié)點(diǎn). 然后, 調(diào)用siftup方法讓這個(gè)節(jié)點(diǎn)一直和父節(jié)點(diǎn)比較, 大于父節(jié)點(diǎn)就上浮, 直到它到達(dá)合適的位置. 這樣就維護(hù)了二叉樹的有序性.

三. 彈出元素

彈出元素的原理和插入元素大同小異: 我們將根節(jié)點(diǎn)的元素彈出后, 取出最后一個(gè)葉子節(jié)點(diǎn)作為根節(jié)點(diǎn)(避免破壞完全二叉樹的結(jié)構(gòu)), 然后讓這個(gè)節(jié)點(diǎn)與子節(jié)點(diǎn)比較, 下沉到合適的位置就行. 有兩點(diǎn)需要注意一下: 首先, 最大元素處在列表的頭部, 彈出的時(shí)間復(fù)雜度是O(n), 因此我們可以把頭部元素和尾部元素交換后, 刪除尾部元素. 然后, 大部分節(jié)點(diǎn)都有兩個(gè)子節(jié)點(diǎn), 我們應(yīng)該讓更大的那個(gè)節(jié)點(diǎn)上浮, 這樣才能保證二叉樹的有序性.

基于以上兩點(diǎn), 彈出元素的代碼如下:

def pop(self) ->int:

largest=self.largest

self._swap(0, len(self)- 1)

self.cache.pop()

self._siftdown(0)returnlargestdef _siftdown(self, i: int) ->None:while i * 2 + 1

smaller=iif self.cache[i * 2 + 1] >self.cache[smaller]:

smaller= i * 2 + 1

if i * 2 + 2 < len(self) and self.cache[i * 2 + 2] >self.cache[smaller]:

smaller= i * 2 + 2

if smaller ==i:returnself._swap(i, smaller)

i= smaller

四. 列表的堆化

我們在創(chuàng)建Heap對象時(shí)傳入了一個(gè)列表作為堆的原始數(shù)據(jù), 但是, 這個(gè)列表并不一定是顆有序的二叉樹, 因此我們需要將其堆化.

最容易想到的方式是, 首先創(chuàng)建一個(gè)空堆, 然后將列表的所有元素依次推入堆中, 通過_siftup方法保持有序:

如上圖所示, 如果我們通過_siftup來堆化所有元素, 則時(shí)間復(fù)雜度為O(n/2*log n+n/4*log n/2+...+1*1)=O(nlog n), 這和排序的時(shí)間復(fù)雜度差不多, 因此不是很理想.

另外一種方案是, 首先按照列表的原有順序構(gòu)建二叉樹, 然后從二叉樹的倒數(shù)第二層開始, 依次通過_siftdown下沉, 這樣依次為k-1層, k-2層直到頂層排序:

這種堆化方式的時(shí)間復(fù)雜度為O(n), 計(jì)算過程如下:

T(n)=O(n/4)+O(n/8*2)+(n/16*3)+O(log n)2*T(n)=O(n/2)+O(n/4*2)+(n/8*3)+O(2*log n)2*T(n)-T(n)=O(n/2)+O(n/4)+O(n/8)+...+O(log n)=O(n)

因此, 我們的堆化方法可以這么寫:

def _heapify(self) ->None:for i in reversed(range(len(self) // 2)):

self._siftdown(i)

五. 總結(jié)

簡單對我們創(chuàng)建的Heap類進(jìn)行測試:

nums = list(range(14))

random.shuffle(nums)

heap=Heap(nums[:])

heap.show()

heap.push(100)print('插入100')

heap.show()

heap.pop()print('彈出堆頂元素')

heap.show()for _ in range(100):

num= random.randrange(100)

nums.append(num)

heap.push(num)assert max(nums) ==heap.largest

nums.remove(heap.pop())print('所有測試通過!!!')

結(jié)果如下:

總結(jié)

以上是生活随笔為你收集整理的python最大堆_用Python实现最大堆的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 色骚网 | 波多野结衣高清视频 | www五月天com | 又色又爽又高潮免费视频国产 | 色综合天天色综合 | www.日韩视频| youjizz少妇| 黄色片免费 | 天天撸天天射 | 无毒黄色网址 | 国产一区二区三区成人 | 欧美日韩在线视频一区 | 成年人国产 | 香蕉视频二区 | 国产成人av网站 | 自拍偷拍亚洲区 | xxxxwww国产 | 精品午夜福利在线观看 | 高潮一区二区 | 97人人爽人人爽人人爽 | 色亭亭| 国产福利观看 | 国产一区二区三区麻豆 | 热播网 | 国产精品无码专区av免费播放 | 成人av免费看 | 国产色网站 | 青青草自拍视频 | 日韩av中文字幕在线免费观看 | 麻豆av一区二区三区在线观看 | 免费一级a毛片夜夜看 | 亚洲一级一区 | 成人综合网站 | 强行挺进白丝老师翘臀网站 | 最新久久久 | 中国黄色录像 | 午夜久久久久 | 亚洲精品偷拍视频 | 男人天堂成人 | 天堂男人在线 | 亚洲啪啪免费视频 | 国产精品无码久久久久一区二区 | 男女扒开双腿猛进入爽爽免费 | 三级麻豆 | 日本精品999| 欧美性教育视频 | 娇小6一8小毛片 | 在线观看日韩精品 | 日韩欧美国产一区二区三区 | 领导揉我胸亲奶揉下面 | 一级黄色免费观看 | 精品欧美久久久 | 国产女人高潮时对白 | 国产女同视频 | 午夜精品免费 | 97caoporn| 久久色网站 | 亚洲aⅴ | 法国伦理少妇愉情 | 久久精品欧美一区二区三区不卡 | 亚洲国产成人精品激情在线 | 日本三级视频在线观看 | 亚洲欧美视频在线观看 | 黄页嫩草 | 国产视频欧美视频 | 黄色污在线观看 | 亚洲成人系列 | 无套内谢的新婚少妇国语播放 | 亚洲欧美激情另类校园 | 激情偷乱人成视频在线观看 | 精品产国自在拍 | 超碰超碰超碰超碰 | 韩国黄色网| aaa色| 亚洲视频欧洲视频 | 欧美久久精品一级黑人c片 1000部多毛熟女毛茸茸 | a黄视频| 一级艳片新婚之夜 | 野外吮她的花蒂高h在线观看 | 成人免费国产 | 性猛交╳xxx乱大交 偷偷操不一样的久久 | 二色av | 天天舔天天操天天干 | 性欧美久久久 | 日日日人人人 | 18禁裸乳无遮挡啪啪无码免费 | 一二三av| 在线免费中文字幕 | 天天操天天舔天天干 | 亚洲精品一卡二卡 | 黄色小视频免费 | 国产真实乱在线更新 | 97超碰超碰| 亚洲一级网 | 色综合久久久久无码专区 | 欧美国产日韩一区二区三区 | 黄色小说图片视频 | 久久男人网| 久久这里只精品 |