树-堆heap, since 2022-05-03

(2020.12.21)
堆: 用树形结构实现的优先队列,完全二叉树。

任意节点保存的数据,在优先级上优于或等于其子节点的数据。从根到任意叶的路径,优先级(非严格)递减。堆中最优先数据在根节点,检索复杂度O(1)。

插入元素:向堆(完全二叉树)的结尾加入一个元素,此时二叉树不是堆。新加入元素和父节点做对比,如果新的优先级高就与父节点交换,并重复该过程;否则结束插入元素的过程。复杂度O(log2(n)).该过程称为向上筛选

弹出元素:弹出优先级最高的元素,即堆顶的元素。弹出后堆顶空缺,从堆的尾部取出最后一个元素放在堆顶,并比较堆顶和两个子节点,将优先级最高的放在顶点,此时原来处在堆尾的点就下移,重复这个过程直到堆形成。称为向下筛选。复杂度O(log2(n)).

(2022.05.03 Tue)

实现

本实现案例使用Python的list保存heap数据。用list元素的值作为优先级,将最小的元素放在堆顶,以此类推。

先实现HeapBase基类,其中定义了若干基本动作,包括

  • 判断有无左/右子节点
  • 找出左/右子节点
  • 子父节点交换
  • 从heap尾部插入数据并upheap到符合heap标准的位置
  • 调整非尾部数据到符合heap标准的位置

heap类继承了HeapBase基类,还加入了若干动作,包括

  • 向heap加入新元素
  • 找出heap的最小值
  • 移除heap的最小值
class HeapBase:
    def __init__(self, data=[]):
        self._data = data
    def __len__(self):
        return len(self._data)
    def min(self):
        return self._data[0]
    def _parent(self, j):
        return (j-1)//2
    def _left(self, j):
        left = j*2 + 1
        return left
    def _right(self, j):
        right = j*2 + 2
        return right
    def _has_left(self, j):
        return len(self._data) > self._left(j)
    def _has_right(self, j):
        return len(self._data) > self._right(j)
    def _swap(self, i, j):
        self._data[i], self._data[j] = self._data[j], self._data[i]
    def _upheap(self, j):
        parent = self._parent(j)
        if parent >= 0 and self._data[j] < self._data[parent]:
            self._swap(parent, j)
            self._upheap(parent)
        else:
            pass
    def _downheap(self, j):
        if self._has_left(j):
            left = self._left(j)
            smaller = left
            if self._has_right(j):
                right = self._right(j)
                if self._data[right] < self._data[left]:
                    smaller = right
            if self._data[smaller] < self._data[j]:
                self._swap(smaller, j)
                self._downheap(smaller)
    
class heap(HeapBase):
    def __init__(self, data=[]):
        self._data = data
    def __len__(self):
        return len(self._data)
    def add(self, nod):
        self._data.append(nod)
        if len(self._data) == 1:
            pass
        else:
            super()._upheap(len(self._data)-1)
    def min(self):
        if len(self._data) == 0:
            raise 'Empty heap'
        return self._data[0]
    def remove_min(self):
        if len(self._data) == 0:
            raise 'Empty heap'
        super()._swap(0, len(self._data)-1)
        res = self._data.pop()
        super()._downheap(0)
        return res

下面将一个list推入heap并排序输出

>>> a = heap()
>>> tmp = [9, 85, 96, 93, 37, 81, 30, 50, 31, 54]
>>> for i in tmp:
...     a.add(i)
...
>>> for i in range(len(tmp)):
...     a.remove_min()
... 
9
30
31
37
50
54
81
85
93
96

复杂度分析

  • 添加新元素:和list的append复杂度相同,amortised O(1),以及upheap的复杂度O(logn),总复杂度O(logn)
  • 找到最小元素:O(1)
  • 去除最小元素:交换O(1),删除O(1),downheap O(logn),总复杂度O(logn)

应用 - 排序

对长度为n的list排序,先将其添加到heap中,在以此去除最小元素,总计算量是n*O(logn)+n*O(logn),总复杂度O(nlogn)

Reference

1 Goodrich and etc., Data Structures and Algorithms in Python

你可能感兴趣的:(树-堆heap, since 2022-05-03)