输入整数数组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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。