最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python實現(xiàn)堆和索引堆的代碼示例

 更新時間:2018年03月19日 14:39:23   作者:黃天浩  
本篇文章主要介紹了python實現(xiàn)堆和索引堆的代碼示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

堆是一棵完全二叉樹。堆分為大根堆和小根堆,大根堆是父節(jié)點大于左右子節(jié)點,并且左右子樹也滿足該性質(zhì)的完全二叉樹。小根堆相反??梢岳枚褋韺崿F(xiàn)優(yōu)先隊列。

由于是完全二叉樹,所以可以使用數(shù)組來表示堆,索引從0開始[0:length-1]。結(jié)點i的左右子節(jié)點分別為2i+1,2i+2。長度為length的樹的最后一個非葉子節(jié)點為length//2-1。當(dāng)前節(jié)點i的父節(jié)點為(i-1)//2。其中//表示向下取整。

以大根堆舉例。當(dāng)每次插入或者刪除的時候,為了保證堆的結(jié)構(gòu)特征不被破壞,需要進(jìn)行調(diào)整。調(diào)整分為兩種,一種是從上往下,將小的數(shù)下沉。一種是從下往上,令大的數(shù)上浮。

具體實現(xiàn)如下:

首先編寫幾個魔術(shù)方法。包括構(gòu)造函數(shù),可以直接調(diào)用len來返回data數(shù)組長度的函數(shù),一個打印data內(nèi)容的函數(shù)

 def __init__(self, data=[]):
    self.data = data
    self.construct_heap()

 def __len__(self):
    return len(self.data)

 def __str__(self):
    return str(self.data)

定義一個swap函數(shù),來方便的交換數(shù)組中兩個索引處的值。

  def swap(self, i, j):
    self.data[i], self.data[j] = self.data[j], self.data[i]

定義float_up方法,使堆中大的數(shù)能浮上來。當(dāng)前節(jié)點不為根節(jié)點,并且當(dāng)前節(jié)點數(shù)據(jù)大小大于父節(jié)點時,上浮。

 def float_up(self, i):
    while i > 0 and self.data[i] > self.data[(i - 1) // 2]:
      self.swap(i, (i - 1) // 2)
      i = (i - 1) // 2

定義sink_down方法,使堆中小的數(shù)沉下去。當(dāng)前節(jié)點不為葉子節(jié)點時,如果小于左孩子或右孩子的數(shù)據(jù),則和左右孩子中較大的換一下位置。

def sink_down(self, i):
    while i < len(self) // 2:
      l, r = 2 * i + 1, 2 * i + 2
      if r < len(self) and self.data[l] < self.data[r]:
        l = r
      if self.data[i] < self.data[l]:
        self.swap(i, l)

      i = l

實現(xiàn)append方法,能夠動態(tài)地添加數(shù)據(jù)。在數(shù)據(jù)數(shù)組尾部添加數(shù)據(jù),然后將數(shù)據(jù)上浮。

  def append(self, data):
    self.data.append(data)
    self.float_up(len(self) - 1)

實現(xiàn)pop_left方法,取堆中最大元素,即優(yōu)先隊列中第一個元素。將數(shù)組中第一個元素與最后一個元素?fù)Q位置,刪除最后一個元素,然后將第一個元素下沉到合適的位置。

  def pop_left(self):
    self.swap(0, len(self) - 1)
    r = self.data.pop()
    self.sink_down(0)
    return r

如果想在初始化堆的時候,向構(gòu)造函數(shù)中傳入數(shù)據(jù)參數(shù),則需要一次性將整個堆構(gòu)建完畢,而不能一個一個加入。實現(xiàn)也很簡單,從最后一個非葉節(jié)點開始,逐個執(zhí)行sink_down操作。

  def construct_heap(self):
    for i in range(len(self) // 2 - 1, -1, -1):
      self.sink_down(i)

這樣一個基本的堆的代碼就編寫完畢了。

但是如果我們想要動態(tài)的改變數(shù)據(jù),當(dāng)前的堆就不能滿足我們的需求了,因為索引不能總是標(biāo)識同一個數(shù)據(jù),因為堆的結(jié)構(gòu)是不斷調(diào)整的。我們需要使用索引堆。

在索引堆中,我們不在堆中直接保存數(shù)據(jù),而是用在堆中存放數(shù)據(jù)的索引。

如果我們輸入的數(shù)據(jù)arr是 45 20 12 5 35。則arr[0]一直指向45,arr[1]一直指向20,因為我們在調(diào)整堆結(jié)構(gòu)中實際調(diào)整的是索引數(shù)組,而不會改變真實存放數(shù)據(jù)的數(shù)組。

因此我們的代碼需要調(diào)整,首先在構(gòu)造函數(shù)中加入一個索引數(shù)組。下標(biāo)從0開始,與存放數(shù)據(jù)的數(shù)組的下標(biāo)相對應(yīng)。

  def __init__(self, data=[]):
    self.data = data
    self.index_arr = list(range(len(self.data)))
    self.construct_heap()

然后將返回堆長度的魔術(shù)函數(shù)也修改一下。

  def __len__(self):
    return len(self.index_arr)

調(diào)整一下之前定義的swap方法,原來是直接交換數(shù)據(jù),現(xiàn)在交換索引。

 def swap(self, i, j):
    self.index_arr[i], self.index_arr[j] = self.index_arr[j], self.index_arr[i]

調(diào)整float_up以及sink_down中的相應(yīng)位置

  def float_up(self, i):
    while i > 0 and self.data[self.index_arr[i]] > self.data[self.index_arr[(i - 1) // 2]]:
      self.swap(i, (i - 1) // 2)
      i = (i - 1) // 2
      
  def sink_down(self, i):
    while i < len(self) // 2:
      l, r = 2 * i + 1, 2 * i + 2
      if r < len(self) and self.data[self.index_arr[l]] < self.data[self.index_arr[r]]:
        l = r
      if self.data[self.index_arr[i]] < self.data[self.index_arr[l]]:
        self.swap(i, l)

      i = l

當(dāng)append數(shù)據(jù)的時候,要相應(yīng)的更新index_arr

  def append(self, data):
    self.data.append(data)
    self.index_arr.append(len(self))
    self.float_up(len(self) - 1)

當(dāng)移出數(shù)據(jù)的時候,之前已經(jīng)提到過存放數(shù)據(jù)的數(shù)組,是按照append的順序進(jìn)行存儲的,平時操作只是對index_arr的順序進(jìn)行調(diào)整。

如果data_arr為 42 30 74 60 相應(yīng)的index_arr應(yīng)該為2 3 0 1

這時,當(dāng)我們popleft出最大元素時,data_arr中的74被移出后變成了42 30 60,數(shù)組中最大索引由3變成了2,如果索引數(shù)組中仍然用3這個索引來索引30會造成index溢出。74的索引為2,需要我們將索引數(shù)在2之后的都減1。

綜上,在刪除元素時,我們原先是將data_arr中的首尾元素互換,再刪除尾部元素,再對頭部元素進(jìn)行sink_down操作?,F(xiàn)在我們先換索引數(shù)組中首尾元素,再刪除索引數(shù)組尾部元素,此時尚未操作存放data的data_arr,因此索引數(shù)組剩余元素與data_arr的元素仍是一一對應(yīng)的。進(jìn)行sink_down操作,操作完成之后再刪除data_arr相應(yīng)位置元素。最后將index_arr中值大于原index_arr頭部元素值的減一。

  def pop_left(self):
    self.swap(0, len(self) - 1)
    r = self.index_arr.pop()
    self.sink_down(0)
    self.data.pop(r)

    for i, index in enumerate(self.index_arr):
      if index > r:
        self.index_arr[i] -= 1

    return r

索引堆增加了一個更新操作,可以隨時更新索引堆中的數(shù)據(jù)。更新時,先直接更新data_arr中相應(yīng)索引處的數(shù)據(jù),然后在index_arr中,找到存放了data_arr中,剛被更新的數(shù)據(jù)的索引的索引位置,與刪除時一樣需要進(jìn)行一次遍歷。找到這個位置之后,由于無法確定與前后元素的大小關(guān)系,因此需要進(jìn)行一次float_up操作再進(jìn)行一次sink_down操作。

  def update(self, i, data):
    self.data[i] = data
    for index_index, index in enumerate(self.index_arr):
      if index == i:
        target = index_index

    self.float_up(target)
    self.sink_down(target)

可以很明顯看出,這個索引堆在插入元素時是比較快的,但是在刪除元素和更新元素時,為了查找相應(yīng)位置索引,都進(jìn)行了一次遍歷,這是很耗時的操作。為了能更快的找到index_arr中值為要更新的data_arr的相應(yīng)索引值得索引位置,我們再次開辟一個新的數(shù)組to_index,來對index_arr進(jìn)行索引。

例如對于數(shù)組75 54 65 90

此時它的index_arr為3 0 2 1。當(dāng)要更新data[3],即90這個元素時,現(xiàn)在要遍歷一遍index_arr來找到3這個位置,這個位置是0。我們要建立一個to_index,to_index[3]中存放的元素為0。

index_arr存放的元素分別為: 1 3 2 0。

先改變swap數(shù)組,在交換index_arr中元素時,也交換存放在to_index中的index_arr的索引。

 def swap(self, i, j):
    self.index_arr[i], self.index_arr[j] = self.index_arr[j], self.index_arr[i]
    self.to_index[self.index_arr[i]], self.to_index[self.index_arr[j]] = self.to_index[self.index_arr[j]], \
                                       self.to_index[self.index_arr[i]]

然后在update中,當(dāng)要更新位置為i的元素時,我們就不需要通過一次遍歷才能找到index_arr中該元素的索引,而是直接通過訪問index_arr[i]即可訪問index_arr中相應(yīng)索引

  def update(self, i, data):
    self.data[i] = data
    target = self.to_index[i]
    self.float_up(target)
    self.sink_down(target)

最后改變pop_left中相應(yīng)代碼,這時我們需要維護(hù)三個數(shù)組,data_arr,index_arr以及to_index。

仍然是首先將index_arr首位元素交換,并pop出尾部元素存放到i中。然后將頭部元素sink_down到相應(yīng)位置,然后將pop出data_arr索引i處的元素。然后pop出to_index中索引為i的元素,再將index_arr中索引溢出的元素進(jìn)行調(diào)整。

  def pop_left(self):
    self.swap(0, len(self) - 1)
    r = self.index_arr.pop()

    self.sink_down(0)
    self.data.pop(r)

    self.to_index.pop(r)
    for i in range(r, len(self)):
      self.index_arr[self.to_index[i]] -= 1

    return r

以上就是python實現(xiàn)對和索引堆的具體方式。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python刪除windows垃圾文件的方法

    Python刪除windows垃圾文件的方法

    這篇文章主要介紹了Python刪除windows垃圾文件的方法,涉及Python針對系統(tǒng)垃圾文件的查找與清理技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • Python中查看文件名和文件路徑

    Python中查看文件名和文件路徑

    本篇文章主要介紹了Python中查看文件名和文件路徑的方法。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-03-03
  • Python不要再使用while死循環(huán),定時器代替效果更佳

    Python不要再使用while死循環(huán),定時器代替效果更佳

    在python開發(fā)的過程中,經(jīng)常見到小伙伴直接使用while True的死循環(huán)+sleep的方式來保存程序的一直運行。這種方式雖然能達(dá)到效果,但是說不定什么時候就直接崩潰了,其實使用定時器效果也不錯哦
    2023-03-03
  • 使用python實現(xiàn)深度優(yōu)先遍歷搜索(DFS)的示例代碼

    使用python實現(xiàn)深度優(yōu)先遍歷搜索(DFS)的示例代碼

    深度優(yōu)先搜索算法(Depth-First-Search,DFS)是一種用于遍歷或搜索樹或圖的算法,沿著樹的深度遍歷樹的節(jié)點,盡可能深的搜索樹的分支,本文給大家介紹了如何基于python實現(xiàn)深度優(yōu)先遍歷搜索(DFS),需要的朋友可以參考下
    2024-01-01
  • Python中 map()函數(shù)的用法詳解

    Python中 map()函數(shù)的用法詳解

    map( )函數(shù)在算法題目里面經(jīng)常出現(xiàn),map( )會根據(jù)提供的函數(shù)對指定序列做映射,在寫返回值等需要轉(zhuǎn)換的時候比較常用。這篇文章主要介紹了Python中 map()的用法,需要的朋友可以參考下
    2018-07-07
  • Python?hashlib模塊與subprocess模塊使用詳細(xì)介紹

    Python?hashlib模塊與subprocess模塊使用詳細(xì)介紹

    這篇文章主要介紹了Python?hashlib模塊與subprocess模塊使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • anaconda3安裝及jupyter環(huán)境配置全教程

    anaconda3安裝及jupyter環(huán)境配置全教程

    這篇文章主要介紹了anaconda3安裝及jupyter環(huán)境配置全教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • 詳解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和區(qū)別

    詳解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和區(qū)別

    這篇文章主要介紹了詳解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • python列表刪除元素的三種實現(xiàn)方法

    python列表刪除元素的三種實現(xiàn)方法

    本文主要介紹了python列表刪除元素的三種實現(xiàn)方法,主要包括pop方法,remove方法,del方法這三種,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Python繪制并保存指定大小圖像的方法

    Python繪制并保存指定大小圖像的方法

    今天小編就為大家分享一篇Python繪制并保存指定大小圖像的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-01-01

最新評論

五常市| 河池市| 乐平市| 红桥区| 万州区| 肥城市| 赤峰市| 廊坊市| 鄂州市| 汝城县| 溆浦县| 扬州市| 扎兰屯市| 伊宁市| 炉霍县| 炉霍县| 兴业县| 石城县| 宜宾县| 长阳| 方城县| 乌拉特前旗| 临颍县| 宜宾市| 隆安县| 霍林郭勒市| 科技| 龙山县| 湛江市| 江陵县| 通榆县| 三都| 宁海县| 大丰市| 石首市| 孝昌县| 万年县| 友谊县| 富民县| 长兴县| 宜宾县|