堆排序与python实现 2023-09-19(未允禁转)

调用关系:

                       heap_sort
                 /                  |
build_max_heap_by_down_adjust       |
              \                     |
                maxheap_down_adjust              

堆排序python实现:

def heap_sort(array: list[int]) -> None:
    """
    原地堆排序 (大顶堆实现的从小到大排序)
    """

    # egde case
    if len(array) <= 1:
        return

    # 1. 建堆. 建堆有2种思路:down_adjust 和 up_adjust。详见下文说明
    build_max_heap_by_down_adjust(array)
    # build_max_heap_by_up_adjust(array)

    # 2. loop to sort: 将array首尾换位置,最大值到最后,然后再maxheap_down_adjust
    for i in range(len(array) - 1, 0, -1):
        array[i], array[0] = array[0], array[i]
        maxheap_down_adjust(array, 0, i - 1)

    return

def build_max_heap_by_down_adjust(array: list[int]) -> None:
    # 构建大顶堆 by_down_adjust
    # down_adjust从最后一个非叶子结点起(也可以从叶子结点起,但没必要,因为叶子本身已经是合法的堆结构),
    # 倒序地通过maxheap_down_adjust构建每个子大顶堆
    lenth = len(array)
    for i in range(lenth // 2 - 1, -1, -1):
        # i as maxheap root_index
        maxheap_down_adjust(array, i, lenth - 1)

    print(f"heap built as: {array}")
    return

def maxheap_down_adjust(array: list[int], root_index: int, end_index: int) -> None:
    """
    maxheap_down_adjust 以root_index为根的一个大顶堆的向下调整。
    【前提条件是root的左右结点已经是大顶堆根,这样才能做到root只需要关注自己的左右结点】

    :param array: 待排序的数组,可通过下标推断parent node和child node
    :param root_index: 大顶堆根的index
    :param end_index: 推断parent node和child node时,最后一个合法index
    :return: None
    """

    # down_adjust过程:
    # 1. if 树根(array[root_index])的左子结点存在,且左子结点值 > 树根,将树根与左子结点进行值交换, and build_max_heap(array, left_child_index)
    # 2. if 树根(array[root_index])的右子结点存在,且右子结点值 > 树根,将树根与右子结点进行值交换, and build_max_heap(array, right_child_index)
    if root_index > end_index:
        raise ValueError(f"err: root_index({root_index}) > end_index({end_index})")

    left_child_index, right_child_index = 2 * root_index + 1, 2 * root_index + 2

    if left_child_index <= end_index:
        left_child_value = array[left_child_index]
        if left_child_value > array[root_index]:
            # swap value of root and left_child
            array[left_child_index], array[root_index] = array[root_index], array[left_child_index]
            # maxheap_down_adjust left_child recursively
            maxheap_down_adjust(array, left_child_index, end_index)

    if right_child_index <= end_index:
        right_child_value = array[right_child_index]
        if right_child_value > array[root_index]:
            # swap value of root and right_child
            array[root_index], array[right_child_index] = array[right_child_index], array[root_index]
            # maxheap_down_adjust right_child recursively
            maxheap_down_adjust(array, right_child_index, end_index)

    return

def build_max_heap_by_up_adjust(array: list[int]) -> None:
    # 构建大顶堆 by_up_adjust
    # up_adjust从首个结点起,
    # 顺序地对每个节点使用maxheap_up_adjust进行堆调整
    lenth = len(array)
    for i in range(lenth):
        maxheap_up_adjust(array, i)
    print(f"heap built as: {array}")
    return

def maxheap_up_adjust(array: list[int], bottom_index: int) -> None:
    child_index = bottom_index
    parent_index = (child_index - 1) // 2

    if parent_index >= 0 and array[child_index] > array[parent_index]:
        # swap parent and child
        array[child_index], array[parent_index] = array[parent_index], array[child_index]
        # 继续以 parent_index 为 bottom 节点进行向上调整
        maxheap_up_adjust(array, parent_index)

test_list = [134, 5, 565, 35, 676, 3, 2, 1, 4, 5]
heap_sort(test_list)
print(test_list)

down_adjust和up_adjust

down_adjust(向下调整):

down_adjust将调整起点视为堆顶元素,从堆顶开始向下调整堆结构,以维护堆的性质(大顶堆或小顶堆)。这个操作通常在堆顶元素被移除或替换时使用,例如在堆排序算法中,将堆顶元素与最后一个元素交换后,需要通过down_adjust重新调整堆结构。

down_adjust的具体步骤如下:

  • 从堆顶元素开始,找到其左右孩子中较大(大顶堆)或较小(小顶堆)的一个;
  • 如果当前节点比较大(大顶堆)或较小(小顶堆)的孩子还要大(大顶堆)或小(小顶堆),则不需要调整,结束;
  • 否则,将当前节点与较大(大顶堆)或较小(小顶堆)的孩子交换;继续向下调整,直到叶子节点或不需要交换为止。

up_adjust(向上调整):

up_adjust将调整起点视为堆底元素,从堆底开始向上调整堆结构,以维护堆的性质(大顶堆或小顶堆)。这个操作通常在向堆中插入新元素时使用,例如在构建堆或实现优先级队列时,需要通过up_adjust调整堆结构。

up_adjust的具体步骤如下:

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

推荐阅读更多精彩内容