排序 9

来源:互联网 发布:淘宝新店搜索软件 编辑:程序博客网 时间:2024/05/16 15:05

参考:

排序 0 - 前言

百度百科:堆排序

【Python排序搜索基本算法】之堆排序

【坐在马桶上看算法】算法11:堆——神奇的优先队列(上)

【坐在马桶上看算法】算法12:堆——神奇的优先队列(下)

python数据结构之二叉树的实现


堆排序(heap sort)

关键名词

参考:

二叉树

完全二叉树

  • 二叉树:每个节点最多有两个子树的树结构

  • 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树。设树层数为 h,则 1-(h-1) 层的节点数均达到了最大值,而第 h 层的节点均集中在最左边

    • 将完全二叉树的节点值排成列表形式,则第 i 个节点的左右子树为 2i2i+1

堆分为 大根堆小根堆

  • 大根堆:根节点(或称为 堆顶)的值 大于等于 左右子树的完全二叉树

  • 小根堆:根节点(或称为 堆顶)的值 小于等于 左右子树的完全二叉树

工作原理

根据堆的特性,其位于堆顶的值为最大值或者最小值,所以每次取堆顶节点值,删除堆顶节点,重新构建堆,直到堆为空为止,排序完成

算法思想

难点一:如何构建堆

堆既然是完全二叉树,所以可以用序列表示,每次新增加一个节点,将其加入到序列末尾,设为 n,其根节点位置即为 n // 2,比较两者的大小,进行替换,依次类推,调整堆的结构

为方便计算,设序列为 [-1, x1, x2, x3, ...],其第一个元素 -1 无效,从下标 1 开始计算

难点二:如何删除堆顶后,重新构建堆

删除堆顶后,取序列末尾元素,放在堆顶位置,重新计算堆的结构

对于大根堆来说,每次向下调整时,如果需要交换根和左右子树的值,和左右子树中较大值进行交换

对于小根堆来说,与之相反

思路

获得一个无序序列后,先将其构建成堆结构,然后每次取堆顶元素,重新构建堆,直到堆为空为止


Python 算法实现

实现一:使用一个辅助序列

# -*- coding: utf-8 -*-"""堆排序实现"""import randomimport math__author__ = 'zj'def create_data(leng, min, max):    """    创建待排序序列    :param leng: 序列长度    :param min: 最小值    :param max: 最大值    :return: 列表    """    li = range(min, max)    return random.sample(li, leng)def heap_sort_add(heap, v):    """    在堆上加入一个元素    :param heap: 大根堆    :param v: 插入数据    :return:    """    heap.append(v)    idx = len(heap) - 1    r = idx // 2    while r >= 1:        if heap[r] < heap[idx]:            heap[r], heap[idx] = heap[idx], heap[r]            idx = r            r = idx // 2        else:            break    return heapdef heap_sort_delete(heap):    """    删除堆顶元素    :param heap: 大根堆    :return:    """    if len(heap) == 1:        return heap    elif len(heap) == 2:        del heap[0]        return heap    heap[1] = heap[-1]    del heap[-1]    n = len(heap) - 1    idx = 1    n = len(heap)    while idx < n:        lchild = 2 * idx        rchild = 2 * idx + 1        if lchild >= n:            break        elif lchild < n and rchild >= n:            if heap[idx] < heap[lchild]:                heap[idx], heap[lchild] = heap[lchild], heap[idx]                idx = lchild            else:                break        elif lchild < n and rchild < n:            if heap[lchild] > heap[rchild]:                if heap[idx] < heap[lchild]:                    heap[idx], heap[lchild] = heap[lchild], heap[idx]                    idx = lchild                else:                    break            else:                if heap[idx] < heap[rchild]:                    heap[idx], heap[rchild] = heap[rchild], heap[idx]                    idx = rchild                else:                    break    return heapdef heap_sort(li, reverse=False):    """    堆排序实现    :param li: 待排序列表    :param reverse: 是否从大到小排序,默认为False    :return: 已排序列表    """    src = [0]    for item in li:        heap_sort_add(src, item)    n = len(li)    for i in xrange(n):        if reverse:            li[n - i - 1] = src[1]        else:            li[i] = src[1]        heap_sort_delete(src)    return liif __name__ == '__main__':    da = create_data(10, 30, 60)    print da    heap_sort(da)    print da

实现二:使用自身序列,实现从小到大排序

# -*- coding: utf-8 -*-"""堆排序实现"""import randomimport math__author__ = 'zj'def create_data(leng, min, max):    """    创建待排序序列    :param leng: 序列长度    :param min: 最小值    :param max: 最大值    :return: 列表    """    li = range(min, max)    return random.sample(li, leng)def heap_sort_add_v2(heap, n):    """    堆中加入新数据,下标为 n    :param heap: 堆和待排序序列    :param n: 前 n-1 个元素为堆,后面元素为待排序序列    :return:    """    idx = n    r = idx // 2    while r >= 1:        if heap[r] < heap[idx]:            heap[r], heap[idx] = heap[idx], heap[r]            idx = r            r = idx // 2        else:            break    return heapdef heap_sort_delete_v2(heap, n):    """    删除堆顶元素    :param heap: 大根堆    :return:    """    heap[1], heap[n] = heap[n], heap[1]    idx = 1    while idx < n:        lchild = 2 * idx        rchild = 2 * idx + 1        if lchild >= n:            break        elif lchild < n and rchild >= n:            if heap[idx] < heap[lchild]:                heap[idx], heap[lchild] = heap[lchild], heap[idx]                idx = lchild            else:                break        elif lchild < n and rchild < n:            if heap[lchild] > heap[rchild]:                if heap[idx] < heap[lchild]:                    heap[idx], heap[lchild] = heap[lchild], heap[idx]                    idx = lchild                else:                    break            else:                if heap[idx] < heap[rchild]:                    heap[idx], heap[rchild] = heap[rchild], heap[idx]                    idx = rchild                else:                    break    return heapdef heap_sort_v2(li):    """    从小到大排序    :param li:    :return:    """    li.insert(0, -1)    for i in xrange(2, len(li)):        heap_sort_add_v2(li, i)    for i in range(2, len(li))[::-1]:        heap_sort_delete_v2(li, i)    del li[0]    return liif __name__ == '__main__':    da = create_data(10, 30, 60)    print da    heap_sort_v2(da)    print da

性能分析

稳定性

堆排序是不稳定排序算法

在删除堆顶,重新构建堆的过程中,需要将末尾节点元素放置在堆顶,重新构建,这样会破坏相对位置关系

时间复杂度

其时间复杂度为 O(N*logN)

空间复杂度

空间复杂度为 O(1)

原创粉丝点击