树-堆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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容