输入整数数组arr,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

  • 0 <= k <= arr.length <= 10000
  • 0 <= arr[i] <= 10000

解题思路

找一个乱序数组中的最小的k个数,本质来说是一个排序问题,将数组从小到大排序,然后取出前k个数
使用十大排序算法都可解决, 对应的时间复杂度-空间复杂度为
冒泡排序: O(n2)-O(1)
选择排序: O(n2)-O(1)
插入排序: O(n2)-O(1)

快排: O(nlgn)-O(lgb)
堆排序: O(nlgn)-O(1)
归并排序:O(nlgn)-O(n)

希尔排序:O(1.3nlgn)-O(1)
基数排序:O(kn)-O(n+k)
计数排序
桶排序

题中测试数据最大为10000, 5位数, 使用基数排序算法复杂度为O(5n)

class Solution(object):
    def getLeastNumbers(self, arr, k):
        """
        :type arr: List[int]
        :type k: int
        :rtype: List[int]
        """
        # 5位数
        n = 5
        # 第几位数, 0表示个位数
        i = 0
        while i < n:
            # key为0~9, value为当前比较位数为对应key的值列表
            t = defaultdict(list)
            for a in arr:
                # 计算数字在该位的值
                a_t = int((a / (10 ** i)) % 10)
                # 放入位数的值对应的列表中
                t[a_t].append(a)

            j = 0
            # 将当前按照位数分类的数字取出,放回原数组中
            for k in range(10):
                temp = t[k]
                for t1 in temp:
                    arr[j] = t1
                    j += 1
            # 将要计算的数字位数加1
            i += 1
        return arr

经观察,如果我们只需要前k个数的话,实际上不需要对所有数字进行排序,只需要确保前k个数字是最小的,不需要关心第k个数字之后的列表是否有序

快排

import random

class Solution(object):
    def getLeastNumbers(self, arr, k):
        """
        :type arr: List[int]
        :type k: int
        :rtype: List[int]
        """
        # 使用快排选择前k个数
        self.quick_sort(arr, 0, len(arr) - 1, k)
        return arr[:k]
       
    def quick_sort(self, arr, left, right, k):
        """
        :type arr: List[int]
        :type left: 当前需要进行排序的区域的左开始点
        :type right: 当前需要进行排序的区域的右结束点
        :type k: 该次排序中的目标k, 需要从left到right中选取前k小的数字
        """
        if left < right:
            # 分区, 将数组分成左半区和右半区,左半区比右半区小, pos为中间位置索引
            pos = self.partition(arr, left, right)
            # 该次排序中左半区的数量
            num = pos - left + 1
            # 左半区的数量恰好等于k, 已经找到需要的数字
            if num == k:
                return
            # 如果左半区的数量大于需要筛选的k, 则继续对左半区进行划分, 直到满足等于k
            if num > k:
                # 左半区排序区间为left 到pos-1
                self.quick_sort(arr, left, pos - 1, k)
            else:
                # 左半区的数组数量小于k, 需要从右半区选择剩余的k-num个数字,作为整个数组的前k个数字
                self.quick_sort(arr, pos + 1, right, k - num)
                
    def random_select(self, arr, left, right):
        # 在左和右中随机选择一个位置
        pos = random.randint(left, right)
        # 交换该位置和左位置的值
        arr[pos], arr[left] = arr[left], arr[pos]
        return pos
      
    def partition(self, arr, left, right):
        """
        分区函数
        :type arr: List[int]
        :type left: 分区的左开始位置
        :type right: 分区的右结束位置
       
        """
        # 随机选择一个值, 并和left位置进行交换
        self.random_select(arr, left, right)
        # 取出分区的选择的中间值, 左半区需要比该值小, 右半区比该值大
        temp_value = arr[left]
        while left < right:
            while left < right and arr[right] > temp_value:
                # 从右向左推进, 遇到第一个比中间值小的数字, 将它放在原本中间值的位置上, 即left位置
                right -= 1
            arr[left] = arr[right]
            while left < right and arr[left] <= temp_value:
          
                left += 1
            arr[right] = arr[left]
            
        arr[left] = temp_value
        return left

海角七号
11 声望2 粉丝

喵~~~