国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

堆和索引堆的python實現

寵來也 / 3148人閱讀

摘要:堆分為大根堆和小根堆,大根堆是父節點大于左右子節點,并且左右子樹也滿足該性質的完全二叉樹。其中表示向下取整。則一直指向,一直指向,因為我們在調整堆結構中實際調整的是索引數組,而不會改變真實存放數據的數組。以上就是實現對和索引堆的具體方式。

堆是一棵完全二叉樹。堆分為大根堆和小根堆,大根堆是父節點大于左右子節點,并且左右子樹也滿足該性質的完全二叉樹。小根堆相反。可以利用堆來實現優先隊列。

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

以大根堆舉例。當每次插入或者刪除的時候,為了保證堆的結構特征不被破壞,需要進行調整。調整分為兩種,一種是從上往下,將小的數下沉。一種是從下往上,令大的數上浮。

具體實現如下:

首先編寫幾個魔術方法。包括構造函數,可以直接調用len來返回data數組長度的函數,一個打印data內容的函數

 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函數,來方便的交換數組中兩個索引處的值。

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

定義float_up方法,使堆中大的數能浮上來。當前節點不為根節點,并且當前節點數據大小大于父節點時,上浮。

 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方法,使堆中小的數沉下去。當前節點不為葉子節點時,如果小于左孩子或右孩子的數據,則和左右孩子中較大的換一下位置。

 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

實現append方法,能夠動態地添加數據。在數據數組尾部添加數據,然后將數據上浮。

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

實現pop_left方法,取堆中最大元素,即優先隊列中第一個元素。將數組中第一個元素與最后一個元素換位置,刪除最后一個元素,然后將第一個元素下沉到合適的位置。

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

如果想在初始化堆的時候,向構造函數中傳入數據參數,則需要一次性將整個堆構建完畢,而不能一個一個加入。實現也很簡單,從最后一個非葉節點開始,逐個執行sink_down操作。

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

這樣一個基本的堆的代碼就編寫完畢了。
但是如果我們想要動態的改變數據,當前的堆就不能滿足我們的需求了,因為索引不能總是標識同一個數據,因為堆的結構是不斷調整的。我們需要使用索引堆。

在索引堆中,我們不在堆中直接保存數據,而是用在堆中存放數據的索引。
如果我們輸入的數據arr是 45 20 12 5 35。則arr[0]一直指向45,arr[1]一直指向20,因為我們在調整堆結構中實際調整的是索引數組,而不會改變真實存放數據的數組。

因此我們的代碼需要調整,首先在構造函數中加入一個索引數組。下標從0開始,與存放數據的數組的下標相對應。

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

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

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

調整一下之前定義的swap方法,原來是直接交換數據,現在交換索引。

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

調整float_up以及sink_down中的相應位置

    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

當append數據的時候,要相應的更新index_arr

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

當移出數據的時候,之前已經提到過存放數據的數組,是按照append的順序進行存儲的,平時操作只是對index_arr的順序進行調整。
如果data_arr為 42 30 74 60 相應的index_arr應該為2 3 0 1
這時,當我們popleft出最大元素時,data_arr中的74被移出后變成了42 30 60,數組中最大索引由3變成了2,如果索引數組中仍然用3這個索引來索引30會造成index溢出。74的索引為2,需要我們將索引數在2之后的都減1。
綜上,在刪除元素時,我們原先是將data_arr中的首尾元素互換,再刪除尾部元素,再對頭部元素進行sink_down操作。現在我們先換索引數組中首尾元素,再刪除索引數組尾部元素,此時尚未操作存放data的data_arr,因此索引數組剩余元素與data_arr的元素仍是一一對應的。進行sink_down操作,操作完成之后再刪除data_arr相應位置元素。最后將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

索引堆增加了一個更新操作,可以隨時更新索引堆中的數據。更新時,先直接更新data_arr中相應索引處的數據,然后在index_arr中,找到存放了data_arr中,剛被更新的數據的索引的索引位置,與刪除時一樣需要進行一次遍歷。找到這個位置之后,由于無法確定與前后元素的大小關系,因此需要進行一次float_up操作再進行一次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)

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

例如對于數組75 54 65 90
此時它的index_arr為3 0 2 1。當要更新data[3],即90這個元素時,現在要遍歷一遍index_arr來找到3這個位置,這個位置是0。我們要建立一個to_index,to_index[3]中存放的元素為0。
index_arr存放的元素分別為: 1 3 2 0。

先改變swap數組,在交換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中,當要更新位置為i的元素時,我們就不需要通過一次遍歷才能找到index_arr中該元素的索引,而是直接通過訪問index_arr[i]即可訪問index_arr中相應索引

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

最后改變pop_left中相應代碼,這時我們需要維護三個數組,data_arr,index_arr以及to_index。
仍然是首先將index_arr首位元素交換,并pop出尾部元素存放到i中。然后將頭部元素sink_down到相應位置,然后將pop出data_arr索引i處的元素。然后pop出to_index中索引為i的元素,再將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)

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

        return r

以上就是python實現對和索引堆的具體方式。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/44615.html

相關文章

  • 我理解的數據結構(七)—— 堆和優先隊列(Heap And PriorityQueue)

    摘要:我理解的數據結構七堆和優先隊列一堆堆的基礎堆也是一顆樹堆最為主流的一種實現方式二叉堆二叉堆是一顆完全二叉樹完全二叉樹完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。 我理解的數據結構(七)—— 堆和優先隊列(Heap And PriorityQueue) 一、堆 1.堆的基礎 堆也是一顆樹 堆最為主流的一種實現方式:二叉堆 二叉堆是一顆完全二叉樹 2.完全二叉樹 ...

    Simon 評論0 收藏0
  • 我理解的數據結構(七)—— 堆和優先隊列(Heap And PriorityQueue)

    摘要:我理解的數據結構七堆和優先隊列一堆堆的基礎堆也是一顆樹堆最為主流的一種實現方式二叉堆二叉堆是一顆完全二叉樹完全二叉樹完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。 我理解的數據結構(七)—— 堆和優先隊列(Heap And PriorityQueue) 一、堆 1.堆的基礎 堆也是一顆樹 堆最為主流的一種實現方式:二叉堆 二叉堆是一顆完全二叉樹 2.完全二叉樹 ...

    I_Am 評論0 收藏0
  • PHP面試:說下什么是堆和堆排序?

    摘要:一個常見的例子就是優先隊列,還有排序算法之一的堆排序。另外我們還將學習堆排序,并將使用實現堆。堆排序在堆排序中,我們需要用給定的值構建一個一個堆。偽代碼如下從上面的偽代碼可以看到,堆排序的第一步就是構建一個堆。 堆是什么? 堆是基于樹抽象數據類型的一種特殊的數據結構,用于許多算法和數據結構中。一個常見的例子就是優先隊列,還有排序算法之一的堆排序。這篇文章我們將討論堆的屬性、不同類型的堆...

    twohappy 評論0 收藏0
  • 八大排序算法的Python實現

    摘要:是穩定的排序方法。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。算法實現選擇排序堆排序描述堆排序是指利用堆積樹堆這種數據結構所設計的一種排序算法,它是選擇排序的一種。 1、插入排序 描述 插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用于少量數據的排序,時間復雜度為O(n^2)。是穩定...

    princekin 評論0 收藏0
  • 數據結構與算法——堆

    摘要:堆排序的時間復雜度非常的穩定,是,并且是原地排序算法,具體是怎么實現的呢我們一般把堆排序分為兩個步驟建堆和排序。 1. 什么是堆 堆(Heap),其實是一種特殊的二叉樹,主要滿足了二叉樹的兩個條件: 堆是一種完全二叉樹,還記得完全二叉樹的定義嗎?葉節點都在最底下兩層,最后一層的節點都靠左排列,并且除了最后一層,其他層的節點個數都要達到最大,這種樹叫做完全二叉樹。 堆中的每個節點的值都...

    hankkin 評論0 收藏0

發表評論

0條評論

寵來也

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<