python 堆排序

堆排序之前一直感觉很难,因为在看堆排序的时候,都是讲的树结构,实际上在代码实现中,使用的是一维数组。仔细理解一下代码,发现其实没那么难。
图片名称


从上图可以得到
- 父节点 i 的左孩子索引: 2*i+1
- 父节点 i 的右孩子索引: 2*i+2
- 叶子结点 j 的父节点索引 floor((j-1)/2)

堆排序代码实现

class Solution:
    def heapsort(self,num):
        le = len(num)
        # 先建立大根堆,找到最大的元素置于堆顶,从下至上建堆
        for i in range((le-1)//2,-1,-1):
            self.fix(num,i,le-1)
        # 将堆顶元素取出与未排序的最后一个元素交换,实现整个数组的排序
        n = le-1
        while n >= 0:
            num[0],num[n] = num[n],num[0]
            self.fix(num,0,n-1)
            n -= 1
        return num
    #i 为需要调整元素的位置,e为结束位置
    def fix(self,num,i,e):
        while 2*i+1 <= e:
            j = 2*i+1 # j为i的左孩子
            # 如果右孩子存在且右孩子大于左孩子,则把值较大的索引赋给j
            # j表示左右孩子中值较大的索引
            if j < e and num[j]<num[j+1]:
                j = j+1
            # 如果父节点比较大的叶子结点值小,则交换叶子结点和父节点
            # 此时目标元素i的位置变成了j,若i存在孩子,继续调整i和其叶子结点的位置
            if num[i] < num[j]:
                num[i],num[j] = num[j],num[i]
                i = j
            else:
                break
if __name__== '__main__':
    p = Solution()
    num = [1,4,0,3,6,7,11,22,-1]
    print(p.heapsort(num))

相关题目:

输入n个整数,找出其中最小的K个数(或最大的K个数)

思路:

取数组的前k个数,建立大小为k的大根堆,遍历剩余的n-k个元素,若小于堆顶元素,则替换掉堆顶值,并调整大根堆。遍历完后,再对堆进行排序输出即可。

代码如下:

# 最小的k个数方法
class MinK:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        le = len(tinput)
        if k == 0:
            return []
        # 建立长度为k的大堆顶
        if k <= le:
            heap = tinput[:k]
        else:
            return []
        hl = len(heap)
        for i in range((hl-1)//2,-1,-1):
            self.fix(heap,i,hl-1)
        # 遍历剩余的元素
        for i in tinput[hl:]:
            if i < heap[0]:
                heap[0] = i
                self.fix(heap,0,hl-1)
        # 整个排序
        i = hl-1
        while i > 0:
            heap[0],heap[i] = heap[i],heap[0]
            self.fix(heap,0,i-1)
            i -= 1
        return heap

    def fix(self,num,i,e):
        while 2*i+1 <= e:
            j = 2*i+1 # j为i的左孩子
            # 如果右孩子存在且右孩子大于左孩子,则把值较大的索引赋给j
            # j表示左右孩子中值较大的索引
            if j < e and num[j]<num[j+1]:
                j = j+1
            # 如果父节点比较大的叶子结点值小,则交换叶子结点和父节点
            # 此时目标元素i的位置变成了j,若i存在孩子,继续调整i和其叶子结点的位置
            if num[i] < num[j]:
                num[i],num[j] = num[j],num[i]
                i = j
            else:
                break
# Top K 方法
class TopK:
    def GetTopNumbers_Solution(self, tinput, k):
        # write code here
        le = len(tinput)
        if k == 0:
            return []
        # 建立长度为k的小堆顶
        if k <= le:
            heap = tinput[:k]
        else:
            k = le
            heap = tinput
        hl = len(heap)
        for i in range((hl-1)//2,-1,-1):
            self.fix(heap,i,hl-1)
        # 遍历剩余的元素
        for i in tinput[hl:]:
            if i > heap[0]:
                heap[0] = i
                self.fix(heap,0,hl-1)
        # 整个排序
        i = hl-1
        while i > 0:
            heap[0],heap[i] = heap[i],heap[0]
            self.fix(heap,0,i-1)
            i -= 1
        return heap

    #i 为需要调整元素的位置,e为结束位置
    def fix(self,num,i,e):
        while 2*i+1 <= e:
            j = 2*i+1 # j为i的左孩子
            if j < e and num[j]>num[j+1]:
                j = j+1
            # 如果父节点比较大的叶子结点值大,则交换叶子结点和父节点
            # 此时目标元素i的位置变成了j,若i存在孩子,继续调整i和其叶子结点的位置
            if num[i] > num[j]:
                num[i],num[j] = num[j],num[i]
                i = j
            else:
                break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值