最小的K个数

题目描述

给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组

输入输出

输入:[4,5,1,6,2,7,3,8],4
返回值:[1,2,3,4]

解题思路

最大堆的特性是根节点的值比左右节点的值大,并且是完全二叉树的结构。我们可以用序列中的数来创建最大容量为k的最大堆,每次新的插入都保证k的容量,并且满足最大堆中k个元素是最小的。

  • 首先,要完成最大堆的创建;
    最大堆创建的过程中,每次插入的值都是放在堆的最后一个位置,每次的插入都需要去比较插入的数与其父节点的数的大小,如果插入的值比其父节点大的话,需要两项做交换,这样比较变换直到当前的索引指向最大堆的根节点。
  • 然后需要定义最大堆的调整,因为当创建的最大堆容量到达k时,每次存入都要去调整使得每次的堆都是一个最大堆的形式,这样后续插入的数与根节点的值进行比较;
    调整最大堆的过程是,非常插入的数与最大堆的根节点值做比较,如果插入的数的值小于根节点的值就进行两项的交换;然后再判断此时根节点的值与其左右子节点的值的大小,如果比左右子节点的值小的话,则需要与其中最大的数进行交换,依次进行直到指向堆的最后一个节点。
  • 定义了创建和调整最大堆后,在找出最小k个数中调用;
  • 如果此时的堆的容量不等于k,则直接将序列中的数插入堆中,直到成为容量为k的堆;
  • 这是再往堆中插入元素时,每次的插入都要调整堆,使其成为最大堆
  • 序列遍历完成后,容量为k,节点数为最小的最大堆也完成,需要将堆中元素排序后返回。

代码示例

# coding:utf-8

class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # 创建最大堆,最大堆用数组表示,
        maxHeap =[]

        # 创建或者插入最大堆
        # 要传入一个num的值
        def creatMaHeap(num):
            # 向最大堆中插入一个值,要判断是否大于其父节点,
            # 大于其父节点,就交换,直到为根节点(索引为0)
            maxHeap.append(num)  # 加到堆最后
            currentIndex = len(maxHeap) - 1  # 当前数在堆中的索引
            # 当当前节点不是根节点(索引不为0)时,
            while currentIndex != 0:
                parentIndex = (currentIndex - 1) // 2  # 当前节点的父节点索引
                # 如果当前节点值大于其父节点的值,做替换
                if maxHeap[parentIndex] < maxHeap[currentIndex]:
                    maxHeap[currentIndex], maxHeap[parentIndex] = maxHeap[parentIndex], maxHeap[currentIndex]
                # 如果当前节点值小于父节点的值,不用做替换,直接跳出循环
                else:
                    break

        # 调整最大堆 头结点发生改变
        # 输入一个num与堆顶元素做替换
        def adjustMaxHeap(num):
            # 如果输入的num小于堆顶元素的值,就进行替换,替换后调整堆结构使其继续满足最大堆的特性
            if num < maxHeap[0]:
                maxHeap[0] = num

            maxHeapLen = len(maxHeap)
            index = 0
            # 当index小于最大索引时,(最后一个节点)
            while index < maxHeapLen:
                # 计算出左右节点的索引值
                leftIndex = index * 2 + 1
                rightIndex = index * 2 + 2
                '''判断与哪个节点做替换'''
                # 首先确定左右节点中值最大的节点,
                largerIndex = 0  # 保存左右节点的最大值的索引
                # 然后再用这个最大值的节点与父节点做替换

                # 如果右节点的索引小于最大堆的节点长度(存在右节点,就一定存在左节点)
                # 比较左右节点值的大小;将最大的所在索引赋值给largerIndex
                if rightIndex < maxHeapLen:
                    if maxHeap[rightIndex] < maxHeap[leftIndex]:
                        largerIndex = leftIndex
                    else:
                        largerIndex = rightIndex
                #  如果左节点索引小于最大堆节点长度(存在左节点,不存在右节点)
                elif leftIndex < maxHeapLen:
                    # 因为只有左节点,所以直接将左节点的索引给最大值索引
                    largerIndex = leftIndex
                # 既没有左节点,又没有右节点的情况(走到最底)
                else:
                    break

                # 找到左右节点中的最大值索引后,与父节点的值进行比较
                # 如果左右节点(下面的节点)中的最大值比父节点(上面的)的值大,则做替换,保持最大堆特性
                if maxHeap[index] < maxHeap[largerIndex]:
                    # 交换两个节点值的位置
                    maxHeap[index], maxHeap[largerIndex] = maxHeap[largerIndex], maxHeap[index]
                # 将当前最大值索引赋给index,继续接下来的替换
                index = largerIndex
                
        tinputLen = len(tinput)
        # 边界条件:如果输入的序列长度不足k,返回空
        if tinputLen < k or k <= 0:
            return []

        # 循环遍历序列
        for i in range(tinputLen):
            # 当堆中的节点数小于k时,创建堆
            if i < k:
                creatMaHeap(tinput[i])
            # 当堆中的节点数为k时,每加入一个数,调整堆
            else:
                adjustMaxHeap(tinput[i])

        maxHeap.sort()
        return maxHeap




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值