题目
给定整数数组
nums和整数k,请返回数组中第**k**个最大的元素。 请注意,你需要找的是数组排序后的第k个最大的元素,而不是第k个不同的元素。 你必须设计并实现时间复杂度为O(n)的算法解决此问题。
代码
package main
import (
"fmt"
"math/rand"
)
func partition(nums []int, left int, right int) int {
pivot := left + rand.Intn(right-left+1)
nums[pivot], nums[right] = nums[right], nums[pivot]
store := left
for i := left; i < right; i++ {
if nums[i] > nums[right] {
nums[i], nums[store] = nums[store], nums[i]
store++
}
}
nums[store], nums[right] = nums[right], nums[store]
return store
}
func findSelect(nums []int, left int, right int, k int) int {
if left == right {
return nums[left]
}
store := partition(nums, left, right)
if store == k {
return nums[store]
} else if store > k {
return findSelect(nums, left, store-1, k)
} else {
return findSelect(nums, store+1, right, k)
}
}
func findKthLargest(nums []int, k int) int {
return findSelect(nums, 0, len(nums)-1, k-1)
}
func main() {
nums := []int{2, 3, 3}
k := 3
fmt.Printf("第 %d 大的元素是 %d\n", k, findKthLargest(nums, k))
}
解题思路
- 首先这个题要求是O(n)的时间复杂度,那么肯定不是先排序数组,再去查找
- 现在就想到分治,因为分治的时间复杂度是O(n), 因为分治的每一层都将规模缩小一半,O(n) (1 + 1/2 + 1/4 + ...),这是平均情况下, 这样下来就是O(n) * 2, 也就是O(n)
- 但是呢,如果用分治排序好,再去查找那就是 O(n^2), 肯定不行,所以就想到在分治的过程中去判断,因为每一次分治都能确定一个值的位置,此时判断这个位置是不是我们想要的,就可以了
- 至于分治的算法,首先先确定一个基准点,但是这个基准点的位置要随机,因为比如选最右边,那么如果数组是排序好的,那么每次只会筛选掉一个值,此时时间复杂度就是O(n^2),所以要随机选择基准点,这样平均下来每次都能筛选一半,用 left + rand.intn(right-left+1), 这样能得到随机的基准点,然后把基准点放到最右边,这样只处理最右边前面的数据就行,操作简单
- 从left开始遍历,和基准点值pivot比较,大于基准点值的情况就挨个从left开始放,比如放了两个大于基准点值的值,那么第三个位置和最右边互换,此时基准点左边是大于pivot的值,右边是小于pivot的值,此时pivot的位置就是确定的,判断pivot的位置是不是想要的,是就返回,不然就接着找pivot左边的区域和右边的区域,如果找到left和right值相同,可以理解为别的位置都找遍了,只剩下你了,那么你就是要找的地方