堆排序-python

复习之前学过的堆排序,发现掌握的不是特别牢固,又仔细阅读了几篇博文,整理出来这篇记录。

1 堆排序介绍

1.1 与堆有关的概念

二叉树:每个节点最多有两个子节点的树。
完全二叉树:除了最后一层之外,每一层都被完全填充并且最后一层左对齐的二叉树。
最大堆:每个父节点都大于孩子节点的完全二叉树。
最小堆:每个父节点都小于孩子节点的完全二叉树。

这里我们讨论最大堆,在最大堆中,如果节点标号从 1 开始,满足如下条件:

  • 父节点标号为 i ,左孩子节点标号为 i*2, 右孩子节点标号为 i*2 + 1
  • 最后一个节点标号为 j ,则它的父节点的标号为 j // 2,也是最后一个有子节点。

1.2 堆排序

给定一个列表[90,50,80,16,30,60,70,10,2],在上面讲到,节点标号从1开始满足某些性质方便表示,为了和下标索引对应起来,在列表最前端添加一个占位元素0,此时列表变为[0,90,50,80,16,30,60,70,10,2]
堆排序主要步骤如下:

  • 首先我们将列表调整成一个最大堆。
  • 此时堆首的元素最大,将它与堆最有一个元素交换。
  • 将剩下的元素调整成一个最大堆。
  • 循环执行,直到需要调整的堆中只有一个元素。

2堆排序步骤详解

  • 将列表调整为一个最大堆,得到结果如下:


    初始的最大堆
  • 将最大的堆顶元素与最后一个元素交换,即902交换,得到如下结果:

    将90与2交换后的堆

  • 此时除了90剩下的元素构成的堆已经不是最大堆,调整剩下的元素,使它成为最大堆


    调整元素,使它成为最大堆
  • 调整结果如下,然后继续重复上述步骤,直到堆中只有一个元素。


    剩下的元素变为最大堆

3 代码

整体的排序分为 2 块:

  • 初始化最大堆
  • 交换堆顶与堆尾元素
    注:
    1.在初始化最大堆的过程中,从最后一个有子节点开始,也就是从最后一个极小堆开始
    2.在队首加入占位元素,如果直接用python内置的list,用.insert(0)方法时间复杂度为O(n),而用单链表时间复杂度仅为O(1)。文件头需要加入from collections import deque
def heap_sort(l):
    # 加入占位元素
    L = deque(l)
    L.appendleft(0)
    
    Length = len(L) - 1 #最大索引下标
    first_sort_count = Length // 2 # 最后一个有子节点索引下标
    # 初始化最大堆, 从最后一个有子节点开始
    for i in range(first_sort_count):
        heap_adjust(L, first_sort_count - i, Length)
        
    # 开始排序
    for i in range(Length-1):
        swap_param(L, 1, Length-i)
        heap_adjust(L, 1, Length-i-1)
        
    return [L[i] for i in range(1, len(L))]
def heap_adjust(L, start, end):
    tmp = L[start]
    i = start
    j = i*2
    
    while j <= end:
        if (j<end) and (L[j]<L[j+1]):
            j += 1
        if tmp < L[j]:
            L[i] = L[j]
            i = j
            j = i*2
        else:
            break
    
    L[i] = tmp
def swap_param(L, i, j):
    L[i], L[j] = L[j], L[i]

4 时间和空间复杂度

空间复杂度:O(1)。就地排序,全程并没有使用额外的空间。

时间复杂度:O(nlogn)
分为初始化堆过程和每次选取最大数后重新建堆的过程。
1.初始化建堆过程时间:O(n)

首先要理解怎么计算这个堆化
假设高度为k,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较然后交换(如果顺序是对的就不用交换);倒数第三层呢,则会选择其子节点进行比较和交换,如果没交换就可以不用再执行下去了。如果交换了,那么又要选择一支子树进行比较和交换;
那么第 i 层的时间计算为:s = 2^( i - 1 ) * ( k - i );其中 i 表示第几层,2^( i - 1) 表示该层上有多少个元素,( k - i) 表示子树上要比较的次数,如果在最差的条件下,就是比较次数后还要交换;因为这个是常数,所以提出来后可以忽略;

S = 2^(k-2) * 1 + 2^(k-3)2+.....+2(k-2) + 2^(0)*(k-1) ===> 因为叶子层不用交换,所以i从 k-1 开始到 1;
这个等式求解:等式左右乘上2,然后和原来的等式相减,就变成了:
S = 2^(k - 1) + 2^(k - 2) + 2^(k - 3) ..... + 2 - (k-1)
除最后一项外,就是一个等比数列了,直接用求和公式:S = {a1[ 1- (q^n) ] } / (1-q)

S = 2^k -k -1;又因为k为完全二叉树的深度,所以 (2^k) <= n < (2^k -1 ),总之可以认为:k = logn
综上所述得到:S = n - longn -1,所以时间复杂度为:O(n)。

2.重构最大堆过程
在取出堆顶点放到对应位置并把原堆的最后一个节点填充到堆顶点之后,需要对堆进行重建,只需要对堆的顶点调用heap_adjust()函数。
  每次重建意味着有一个节点出堆,所以需要将堆的容量减一。heap_adjust()函数的时间复杂度k=log(n),k为堆的层数。所以在每次重建时,随着堆的容量的减小,层数会下降,函数时间复杂度会变化。重建堆一共需要n-1次循环,每次循环的比较次数为log(i),则相加为:log2+log3+…+log(n-1)+log(n)≈log(n!)。可以证明log(n!)和nlog(n)是同阶函数,所以时间复杂度为O(nlogn)。

最终的时间复杂度为O(nlogn)

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

推荐阅读更多精彩内容

  • 选择排序 每次在n个记录中选择一个最小的需要比较n-1次,但是这样的操作并没有把每一趟的比较结果保存下来,在后一趟...
    wlj1107阅读 1,700评论 0 2
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,333评论 0 2
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
    依依玖玥阅读 1,243评论 0 2
  • 旅行的第三天,早早的就是睡不着了。 今天是要离开的日子,有人说旅行越到终点的时候越会百感交集。一个美好的地方常常带...
    羊的三脚猫阅读 258评论 0 0
  • 禁烟签名现场 合影留念 根据世界卫生组织统计,目前大学生吸烟人数正逐年增长,为了让更多的人了解吸烟的危害,让同学们...
    兰州理工大学管理员阅读 229评论 0 0