JZ29 最小的K个数

最小的K个数

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

 

方法1:堆
思路和算法

我们用一个大根堆实时维护数组的前 k 小值。首先将前 k 个数插入大根堆中,随后从第 k+1个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。在下面的代码中,由于 C++ 语言中的堆(即优先队列)为大根堆,我们可以这么做。而 Python 语言中的堆为小根堆,因此我们要对数组中所有的数取其相反数,才能使用小根堆维护前 k 小值。

方法2、用快排最高效解决 TopK 问题:O(N)
注意找前 K 大/前 K 小问题不需要对整个数组进行 O(NlogN)的排序!
例如本题,直接通过快排切分排好第 K 小的数(下标为 K-1),那么它左边的数就是比它小的另外 K-1 个数啦~

 

func GetLeastNumbers_Solution(input []int, k int) []int {
	// write code here
	res := make([]int, 0)
	if k == 0 || len(input) == 0 {
		return res
	}

	quickSelect(input, 0, len(input)-1, k)
	return input[:k]
}

func quickSelect(nums []int, low, high, k int) {
	if low >= high {
		return
	}

	pivotIndex := partition(nums, low, high)

	if k-1 == pivotIndex {
		return
	} else if k-1 < pivotIndex {
		quickSelect(nums, low, pivotIndex-1, k)
	} else {
		quickSelect(nums, pivotIndex+1, high, k)
	}
}

func partition(nums []int, low, high int) int {
	pivot := nums[high] // 可以随机,可以固定
	i := low

	for j := low; j < high; j++ {// 双指针,i指向小于等于pivot的位置,j指向大于pivot的位置,然后交换位置
		if nums[j] <= pivot {
			nums[i], nums[j] = nums[j], nums[i]
			i++
		}
	}

	nums[i], nums[high] = nums[high], nums[i] // 把锚点放在左右的中间
	return i
}

  

posted @ 2021-04-10 10:18  zqlucky  阅读(58)  评论(0编辑  收藏  举报