Python中堆模块heapq

对于排序算法有很多种,其中包括常见的:

冒泡排序,选择排序, 插入排序, 希尔排序, 快速排序, 归并排序, 堆排序

这里主要讲一下堆排序, 可以采用自己的方式实现, 也可以采用Python内置模块heapq实现, 现在分别从这两种方法实现一下.

(1) 自己实现

import math
from collections import deque

def print_tree(array): #打印堆排序使用
    '''
    深度 前空格 元素间空格
    1     7       0
    2     3       7
    3     1       3
    4     0       1
    '''
    # first=[0]
    # first.extend(array)
    # array=first
    index = 1
    depth = math.ceil(math.log2(len(array))) # 因为补0了,不然应该是math.ceil(math.log2(len(array)+1))
    sep = '  '
    for i in range(depth):
        offset = 2 ** i
        print(sep * (2 ** (depth - i - 1) - 1), end='')
        line = array[index:index + offset]
        for j, x in enumerate(line):
            print("{:>{}}".format(x, len(sep)), end='')
            interval = 0 if i == 0 else 2 ** (depth - i) - 1
            if j < len(line) - 1:
                print(sep * interval, end='')
        index += offset
        print()

def swap_param(L, i, j):
    L[i], L[j] = L[j], L[i]
    return L

def heap_adjust(L, start, end):
    # 调整大顶堆
    temp = L[start]
    
    i = start
    j = 2 * i  # 左孩子节点索引2*i, 右孩子节点索引为2*i+1
    
    while j <= end:
        if (j < end) and (L[j] < L[j+1]): # 左右孩子节点比较
            j += 1       
        if temp < L[j]:  # 先比较右孩子节点,选出大的和父节点比较
            L[i] = L[j]  # 和父节点交换
            i = j
            j = 2 * i
        else:
            break
    L[i] = temp
    
def heap_sort(L):
    '''
    堆排序,采用大根堆 升序
    '''
    L_length = len(L) - 1
    
    # 得到初始最大根堆
    first_sort_count = L_length // 2  # 最后一个非叶子节点
    for i in range(first_sort_count): # 遍历每个非叶子节点
        heap_adjust(L, first_sort_count - i, L_length)
        #print(L)
        
    for i in range(L_length - 1):  # 大根堆最大值和最右下节点交换
        L = swap_param(L, 1, L_length - i)
        heap_adjust(L, 1, L_length - i - 1) # 重新构造大根堆
    
    return [L[i] for i in range(1, len(L))]
    
    
if __name__ == '__main__':
    
    #alist = [0, 6, 8, 2, 3, 9, 7, 4, 1, 5, 10]
    alist = deque([16, 7, 3, 20, 17, 8])
    alist.appendleft(0)
    print(heap_sort(alist))

(2) 采用Python,模块实现

对于heapq中的函数有

                                                              函 数                                                           描 述
                                                      heappush(heap, x)                                        将x压入堆中
                                                        heappop(heap)                                      从堆中弹出最小的元素
                                                        heapify(heap)                                           让列表具备堆特征
                                                  heapreplace(heap, x)                            弹出最小的元素,并将x压入堆中
                                                      nlargest(n, iter)                                       返回iter中n个最大的元素
                                                        nsmallest(n, iter)                                   返回iter中n个最小的元素
函数heappush用于在堆中添加一个元素。请注意,不能将它用于普通列表,而只能用于使用各种堆函数创建的列表。原因是元素的顺序很重要(虽然元素的排列顺序看起来有点随意,并没有严格地排序)。

import heapq as hq
from random import shuffle

data = list(range(10))
shuffle(data)
heap = []
for i in data:
    hq.heappush(heap, i)
print(heap)

hq.heappush(heap, 0.5)
print(heap)

hq.heappop(heap)
print(heap)

元素的排列顺序并不像看起来那么随意。它们虽然不是严格排序的,但必须保证一点:位置i处的元素总是大于位置i // 2处的元素(反过来说就是小于位置2 * i和2 * i + 1处的元素)。这是底层堆算法的基础,称为堆特征(heap property)。

因为是最小堆,因此heappop弹出最小的元素(总是位于索引0处),并确保剩余元素中最小的那个位于索引0处(保持堆特征)

Python中堆模块heapq_第1张图片

函数heapify通过执行尽可能少的移位操作将列表变成合法的堆(即具备堆特征)。如果你的堆并不是使用heappush创建的,应在使用heappush和heappop之前使用这个函数。

Python中堆模块heapq_第2张图片

nlargest(n, iter)和nsmallest(n, iter):分别用于找出可迭代对象iter中最大和最小的n个元素。这种任务也可通过先排序(如使用函数sorted)再切片来完成,但堆算法的速度更快,使用的内存更少(而且使用起来也更容易)。

Python中堆模块heapq_第3张图片

hq.heapreplace()函数将最小的数弹出之后,再将x压入堆中

除了上述几种常见的函数,还有merge()函数,该函数的迭代性质意味着它对所有提供的序列都不会做一次性读取,因此可以处理非常长的序列,而开销却非常小.此外,它要求所有的输入序列都是有序的,它只是简单地检查每个输入序列中的第一个元素,将最小的那个发送出去,然后重复执行此步骤,直到所有的输入序列都耗尽为止.

Python中堆模块heapq_第4张图片

内容参考自 https://blog.csdn.net/jamfiy/article/details/88185512

你可能感兴趣的:(python学习)