堆排序

hello,你好,欢迎来到堆排序!
堆排序是典型的数据结构和算法的结合,首先使用数据结构记录了必要的信息,然后算法通过查找数据结构的信息,来决定算法的下一步该怎么进行。以后我们会遇到很多的数据结构,他们通常会在某些特定的算法中起到一种特殊的作用。了解数据结构的特性,也可以给你设计算法的时候提供必要的模型。从某种意义上说,数据结构是算法的一种补充。从另一种意义上说,数据结构是我们认识世界的一个模型特写。简单的说,算法才是编码的重点,但是构造算法的过程,如果你有某种数据结构作为模型,那么算法的就会很容易构造出来,这是一种相互作用的过程,这种思维方式不同与分治法思想,不过这也是一种重要的思想,而且这种思想,更需要你去训练,了解某种数据结构的使用场景,了解该种数据结构的特性,了解一切和该种数据结构相关的内容,这是一个复杂的训练过程,这也是我学习算法的时候最耗时的过程,这个过程没有捷径,不可能像归并排序,我三句话就能说明白的。
数据结构是也可以对现实世界进行建模的,这也是工作种经常要做的事情,相反算法虽然往往比较复杂,不过都可以固化到算法库里面,你只要学会怎么使用就行了,这也是很多人不愿意学习算法的原因,我想说的是,学习算法的过程,是为了巩固你对数据结构的理解,这才可能是为什么程序员必须学习算法,通过学习算法进一步提高你对数据结构的认识!!!
好了,说了这么多,其实我想向你展现的第一个数据结构“堆”。

为了模拟升序排序,这里我使用“大顶堆”,简单的定义就是,堆是一棵满二叉树,每个堆的左子树和右子树都是堆,大顶堆指的是它的根的值不小于他的所有的孩子的值。(啊,太烦了,为了描述它,我脑细胞又死了一堆,这是我对堆的理解,太绕口了,这是一个多么自然的数据结构,我生生的描述成了一个不能理解的数据结构)

二叉树的实现又多种方式,这里由于是满二叉树,所以采用数组法,所谓的数组法就是采用连续的内存空间来存储堆。(我擦, 又是一套专业术语,有时候我都怀疑人生, 你还能理解吗?)

OK,我们还得继续,这里有个问题,如何构建堆?当你学习某种数据结构的时候,这个问题是我们必须要解决的问题。另外一个问题,堆数据结构有哪些基本的操作?这个问题是我们能够使用该种数据结构进行建模的一个本质问题,如果该种数据结构的操作,或者操作的性能不能满足实际问题的要求,那么该种数据结构就不适合这种实际问题的建模,或者说,如果你强行使用不适合的数据结构对实际问题建模,那么你会遇到一切你想不到的麻烦!!!这里,有种特殊情况,就是,我们可以对数据结构进行变体,这样,让变体的模型适应实际问题,堆数据结构就有个变体,优先级队列,这个以后有时间会更新一篇文章。

堆能提供的操作:

  1. build构建
  2. push一个新元素
  3. top最小的元素
  4. pop一个最小的元素

如果你熟悉堆这个数据结构,那么你一定会知道,这些基本的堆操作都会涉及到如何保持一个堆的性质,简单的说就是维护堆固有的定义。所以我们必须解决这个问题,否则,堆的操作就无法进行。对于复杂的数据结构,维护该数据结构的性质,是操作数据结构的一个基础,比如平衡树。这些复杂的数据结构的性质的维护代码的多少,从某种意义上也体现了这个数据结构的复杂程度,有些特殊的特性,需要特殊的维护代码,更艰难的是,通常这种代码很少能简单的找到共性,简单的说,你很难通过一个有效的公式就能拼凑这种维护数据结构特性的代码。那么如何提高自己对数据结构性质的理解呢?多多的编码和感悟,这是唯一的有效途径!(没有其他更有效的方法,除非你有这方面的天赋,我等鼠辈只能按部就班)所以,那些熟悉各种算法和数据结构的人,值得你去尊重,因为,虽然很多东西都是浅显易懂的,但是不坚持一点一滴的积累,是很难有质的突破。(我好像说了很多的废话。。。废话比你看我的技术文章跟重要,因为,我不是专门研究技术的。。。嘿嘿!!!)

好了,现在来说说,如何确保一个堆的性质,这里我们提供一个方法heapify。(我也不知道为什么叫这个名字,我英语不好,别笑话)
代码1如下:

def heapify(H, HSize, index):
    # H: it's the heap
    # HSize: heap's size
    # index: the element we have to adjust, 
    #   then the heap can keep the heap properties.
    def left_child(index):
        return (index << 1) + 1
    def right_child(index):
        return (index << 1) + 2

    l_index = left_child(index)
    r_index = right_child(index)
    max_index = index
    if l_index < HSize and H[max_index] < H[l_index]:
        max_index = l_index
    if r_index < HSize and H[max_index] < H[r_index]:
        max_index = r_index

    if max_index == index:
        return

    H[index], H[max_index] = H[max_index], H[index]
    heapify(H, HSize, max_index)

呃,如果你是算法的初学者,那么代码1的内容你可能不能够立即明白,里面有几个技巧,比如left_child和right_child的定义,还有就是这是一个递归调整,直到调整到叶子节点,如果index的初值是0,那么表示从堆的根开始调整,调整的之前,左右子树都是堆。这些技巧,我一开始也不能理解,甚至有段时间我产生了放弃的念头,直到有天我遇到一个真的会写这段代码的人,我才明白,这玩意真的可以自己写,而且不用看书写,哦,对了,初学者都喜欢看着书去敲代码,俗语说,先模仿后学习,不过我的建议是模仿可以,学习也是很重要的。我现在可以很自信的告诉你,这段代码是我的第一反应, 堆的这种保持堆性质的操作,我是烂熟于心,我了解堆的性质,我理解它,我了解如何递归的调整,我了解它,我了解为什么使用数组构建堆的基本形态,我了解它,我的这种了解,不是形式化上的了解,不是背书,而是,如果是我来设计,我也只能这样设计,这就是自然化的过程,我相信,你通过努力也可以达到这种状态,一种很自然的状态,训练是必须的。!!!

废话不多说,如果有了这个基本操作heapify,那么其他的操作就会简单的很多。

从一个简单的数组构建堆,代码2如下:

def heap_build(L):
    i = len(L) >> 1
    while True:
        if i < 0:
            break
        
        heapify(L, len(L), i)
        
        i -= 1
        
    return L

top操作,代码3如下:

def heap_top(H):
    return H[0] if H else None

pop操作,代码4如下:

def heap_pop(H):
    if not H:
        return

    H[0], H[len(H) - 1] = H[len(H) - 1], H[0]
    H.pop()
    heapify(H, len(H), 0)

push操作,要复杂一点,但是也是可以理解的,代码5如下:

def heap_push(H, ele):
    def parent(index):
        return (index - 1) >> 1

    H.append(ele)
    index = len(H) - 1
    # find max parent index
    while True:
        if index <= 0:
            break

        if H[parent(index)] >= H[index]:
            break

        index = parent(index)

有了上述的堆的基本操作,堆排序是所有排序算法中最容易的理解的,从这点可以看出,如果理解某种数据结构,那么和这个数据结构相关的模型算法,也会容易理解的多,所以,我才会关注一个程序员知道多少种数据结构,这个可以从一个侧面看出这个程序员的建模能力。

堆排序算法,代码6如下:

def heap_sort(L):
    heap_build(L)

    i = len(L) - 1
    while True:
        if i <= 0:
            break

        L[0], L[i] = L[i], L[0]
        heapify(L, i, 0)

        i -= 1
    return L

好了,堆排序算法就到这里了,你会发现,堆排序的设计也是很自然的,你可以体会一下heapify在这里作用。再次说明一下,以上的所有代码我都是一气呵成的,所以,有些编码你会觉得有点奇怪,不过通过重构,总可以达到你想要的那种状态。好了,这篇文章就到这里了,有什么不明白的,可以留言,如果我能解释,我一定会回复你的!!!

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

推荐阅读更多精彩内容

  • 更详细的讲解和代码调试演示过程,请点击链接 做过系统编程的人都知道,几乎任何系统都会提供一种时钟机制,也就是Set...
    望月从良阅读 725评论 0 1
  • 堆排序可以做什么 首先应该弄清楚堆排序可以解决什么问题,答案是显而易见的:排序。说得通俗点儿就是对一组无序的数字进...
    Springlord888阅读 30,266评论 11 52
  • 基础概念#### 堆排序是比较基础的排序算法,也是我认为比较难的一种算法,因为它的流程比较多,理解起来不会像冒泡排...
    一只小哈阅读 21,593评论 9 32
  • 上次写到了快排,接着往下讲。O(nlogn)级别的算法除了上次说的快排,归并,还有推排序。今天从先推排序开始写。堆...
    锅与盆阅读 1,961评论 0 2
  • 昨晚和儿子因为学习发生了不愉快,一觉醒来生活还得继续,还要越来越好;今天是儿子11岁的生日,对于我自己来说生日只是...
    邹小兔阅读 242评论 1 1