堆排序(python)

前两天面试被问到堆,于是学习了一下堆排序

堆相关的二叉树基础

堆实际上是一个完全二叉树,所以先了解一下二叉树。二叉树(binary tree)是指树中节点的度不大于2的有序树。划重点,度不大于2,有序。

一些重要性质:

  1. 在二叉树的第层 i 最多有 2^(i-1) 个结点。
  2. 深度为k(k>=0)的二叉树最少有k个结点,最多有 2^k-1 个结点。
  3. 对于任一棵非空二叉树,若其叶结点数为n0,度为2的非叶结点数为n2,则n0=n2+1。
  4. 具有n个结点的完全二叉树的深度为比 log2(n+1) 大的第一个整数。
  5. 如果将一棵有n个结点的完全二叉树自顶向下,同一层自左向右连续给结点编号1,2,3,4…n,然后按此结点编号将树中各结点顺序的存放于一个一维数组,并简称编号为i的结点为结点i (i>=1&&i<=n), 则有以下关系:
    (1)若 i = 1,则结点i为根,无父结点;若 i>1,则结点 i 的父结点为结点int_DOWN(i / 2);
    (2)若 2 * i<=n,则结点i的左子女为结点2 * i (从父亲找左孩子) ; 结点i的右子女为结点2 * i+1(从父亲找右孩子)
    (3)结点 i 所在的层次为 int_DOWN(log2(i)+1)。

完全二叉树:

满二叉树:每一层结点都达到了最大个数,除最底层结点的度为0外,其他各层结点的度都为2。
完全二叉树:上面从第1层到第k-1层的所有各层的结点数都是满的,仅最下面的第k层是满的,或从右往左连续缺少若干结点。也就是从树最底层从右到左有缺失的二叉树,记住得是一层一层的来。
在这里插入图片描述

大根堆:所有父节点都比其子节点大。
小根堆:所有父节点都比其子节点小。
二叉树可以通过向下调整变成一个大根堆。从根节点开始,使得每一个小三角都变成一个大根堆,简单的理解就是大的带小的玩,如果不够资格就一直往下找有资格的。
B站讲解,特别清楚

构建堆

构建最小的堆然后一层一层扩充,直到整个树构成一个堆。

def sift(lis, root, last):
    '''
    完成一个节点的向下调整,也就是该节点比他的左孩子右孩子都大即可
    root: 根节点的位置
    last: 最后一个叶子节点的位置
    '''
    ii=root
    ii_l=2*ii+1
    ii_r=ii_l+1
    while ii_l<=last:
        if ii_r>last:
            if lis[ii]<lis[ii_l]:
                lis[ii],lis[ii_l]=lis[ii_l],lis[ii]
            break
        if lis[ii]>lis[ii_l] and lis[ii]>lis[ii_r]:
            break
        if lis[ii_l]>lis[ii_r]:
            lis[ii],lis[ii_l]=lis[ii_l],lis[ii]
            ii=ii_l
        else:
            lis[ii],lis[ii_r]=lis[ii_r],lis[ii]
            ii=ii_r
        ii_l=2*ii+1
        ii_r=ii_l+1
    return lis
    

上面我们完成的是一个当父亲时找到自己合理的位置的过程,对所有的节点做同样的事情就能构建堆,问题是如果从上往下有可能根节点没办法领导整棵树(比下面的某个非子节点还小),所以采用的思路是从下往上,把最小的二叉树调整成一个大根堆,然后再调整稍微大一点的树,一直到调整整棵树。

def heap_build(lis):
    n=len(lis)
    for ii in range((n-1)//2,-1,-1):
        lis=sift(lis,ii,n-1)
    return lis

堆排序

得先构建堆才能给堆排序。构建了堆以后,每次的根节点就是整个树最大的数字,放到新的列表里面去,然后把最后一个元素放到顶点上去重新向下调整,在拿走根节点,最后元素放上去,以此类推。

def heap_sort(lis):
    lis=heap_build(lis)
    res=[]
    while len(lis)!=0:
        res.append(lis[0])
        lis[0]=lis[-1]
        lis.pop()
        lis=sift(lis,0,len(lis)-1)
    return res

但是这个方法多占一个内存,简化一下

def heap_sort(lis):
    lis=heap_build(lis)
    n=len(lis)-1
    for ii in range(n,-1,-1):
        lis[0],lis[ii]=lis[ii],lis[0]
        lis=sift(lis[:ii],0,ii-1)+lis[ii:]
    return lis

python内置堆包heapq

常用函数

import heapq
lis=[3,4,2,6,7,1,9,8]
heapq.heapify(lis) # 构建堆
heapq.heappush(lis,20) # 给堆里面添加元素
lis1=lis[:]
lis_sort=[heapq.heappop(lis1) for ii in range(len(lis1))] # 堆排序输出

topK问题

从海量数字中寻找最大的 k 个.

  1. 思路1:先排序后切片取前k个复杂度是O(nlog(n))
  2. 思路2:冒泡排序,选择排序,插入排序,时间复杂度O(kn)
  3. 思路3:堆。先取前k个元素建立一个最小堆,然后遍历后面的元素维护这个最小堆,如果新元素比根节点小则忽略否则向下调整一次堆(O(logk))。整个时间复杂度是O(nlogk),比前两种思路都好。
    实现代码:
import heapq
def topk(nums):
	n=len(nums)
	heap=nums[:k]
	heapq.heapify(heap)
	for ii in range(100,n):
		if nums[ii]<heap[0]:
			continue
		else:
			heap[0]=nums[ii]
			heapq.heapify(heap)
	return heap		

leetcode: 前k个高频词

前k个高频词

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值