數據結構和算法-堆

堆的定義python

  • 必須是一個徹底二叉樹(除了最後一層, 每一個節點都有兩個子節點, 最後一層只能缺乏若干個右節點)
  • 堆中每個節點的值都必須>=(大頂堆)或<=(小頂堆)左右子樹節點的值

堆中節點的關係算法

  • 下標爲i節點的父節點序號是i/2
  • 下標爲i節點的左子樹節點時2*i, 右子樹節點是2*i+1

徹底二叉樹特色:
若是一個徹底二叉樹有n個節點, 那麼從n/2+1個節點開始到n都是葉子節點數組

構造最小二叉堆

# coding:utf-8

"""
空間複雜度: O(1)  原地排序
時間複雜度: O(nlogn)

不穩定, 由於最後一個節點跟堆頂節點互換可能致使相同元素的順序互換
"""


class MinHeap(object):
    """
    最小堆
    """

    def __init__(self, nums):
        self.heap_list = [0]  # 填充0位置
        self.size = 0
        self.init_heap(nums)

    def insert(self, data):
        """
        插入元素, 放到最尾部, 上浮
        :param num:
        :return:
        """
        self.heap_list.append(data)
        self.size += 1
        self._go_up(self.size)

    def _go_up(self, i: int):
        """
        末尾元素上浮
        :param i:
        :return:
        """
        while int(i / 2) > 0:
            parent = int(i / 2)
            if self.heap_list[i] < self.heap_list[parent]:
                self.heap_list[i], self.heap_list[parent] = self.heap_list[parent], self.heap_list[i]
            i = parent

    def pop_top(self):
        """
        刪除堆頂元素, 使用最後一個值移動到頂部, 再進行下浮
        :return:
        """
        if self.size >= 1:
            top_value = self.heap_list[1]
            self.heap_list[1] = self.heap_list[self.size]
            self.heap_list.pop()
            self.size -= 1
            self._go_down(1)
            return top_value
        else:
            raise Exception("Heap Empty")

    def _go_down(self, i: int):
        while (2 * i) <= self.size:
            min_child_pos = self._get_min_child(i)
            if self.heap_list[i] > self.heap_list[min_child_pos]:
                self.heap_list[i], self.heap_list[min_child_pos] = self.heap_list[min_child_pos], self.heap_list[i]
            i = min_child_pos

    def _get_min_child(self, i: int):
        """
        找出i節點左右子樹中較小的節點
        :param i:
        :return:
        """
        left = 2 * i
        right = 2 * i + 1
        if right > self.size:
            return left
        elif self.heap_list[left] < self.heap_list[right]:
            return left
        else:
            return right

    def init_heap(self, nums: list):
        """
        構造堆. 徹底二叉樹中從 n/2 開始都是葉子節點, 因此只須要讓非葉子節點下沉
        :param nums:
        :return:
        """
        start_pos = len(nums) // 2
        self.size = len(nums)

        self.heap_list.extend(nums)
        while start_pos > 0:
            self._go_down(start_pos)
            start_pos -= 1


if __name__ == "__main__":
    nums = [9, 4, 7, 1, 8, 20]

    mh = MinHeap(nums)
    mh.insert(2)
    mh.insert(17)

    res = [mh.pop_top() for _ in range(5)]
    assert res == [1, 2, 4, 7, 8]

應用

優先級隊列

  • 高性能定時器
    好比一個定時器中維護了不少的定時任務, 每一個任務都設定了一個觸發執行的時間點, 定時器每過一個很小的單位時間(好比0.1s), 就會掃描一遍任務, 若是有任務是當前時間, 就觸發執行.
    每過0.1s掃描所有任務效率會很低, 因此把全部任務放入一個最小堆中, 堆頂存儲的是最早執行任務. 定時器能夠根據堆頂任務的執行時間獲得一個時間間隔T, 能夠直接過T時間後再來檢查數據結構

  • 爬蟲任務的優先隊列
    二叉堆經常使用在爬蟲的優先級隊列中, 把任務按照優先級放入二叉堆, 調度器能夠拿堆頂元素, 保證拿到的是優先級最高的task.app

利用堆求Top K

  • 如何在一個包含n個元素的數組中找出前K大數據?
    構建一個K大小的小頂堆, 遍歷數組與堆頂元素比較, 若是比堆頂元素大就刪除堆頂數據, 把該數據插入堆中, 不然就比較下一個. 最後獲得的小頂堆內的K個元素就是前K大的元素.

資料

  • < <數據結構和算法-python> >
  • < <大話數據結構> >
  • < <數據結構和算法-王爭> >

相關文章
相關標籤/搜索