题目描述

输入整数数组 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个数,用快排的话时间复杂度为O( n l o g n nlogn nlogn)。
  • 也可以用快速排序的partition函数结合二分的思想来缩小区间,直到基准元素正好被放入数组中第k+1个位置,此时左边k个元素即为最小元素。

复杂度分析

问题规模为数组中元素的个数n

  • 时间复杂度:O(n), 最好时间复杂度为O( n n n),即只需进行一趟排序;最坏时间复杂度为O( n n n),每次递归后数组长度大致减半,求时间复杂度相当于等比数列求和,最坏时间复杂度约等于O( 2 n − 1 2n-1 2n−1)。所以均摊时间复杂度为O(n).
  • 空间复杂度:O(logn),平均递归深度为logn,每次递归有O(1)的空间消耗。

Golang代码如下

func partition(l, r, k int, arr []int) []int {
	pivot := arr[l]
	i, j := l, r
	for i < j {
		for i < j && arr[j] >= pivot {
			j--
		}
		arr[i] = arr[j]
		for i < j && arr[i] <= pivot {
			i++
		}
		arr[j] = arr[i]
	}
	arr[i] = pivot
	if i == k {
		return arr[:k]
	} else if i < k {
		return partition(i + 1, r, k, arr)
	} else {
		return partition(l, i - 1, k, arr)
	}
}

func getLeastNumbers(arr []int, k int) (res []int) {
	if k == len(arr) {
		return arr
	} else if k == 0 || len(arr) == 0 {
		return nil
	}
	return partition(0, len(arr) - 1, k, arr)
}