高频算法面试题(六)- 寻找第K大

448 阅读2分钟

「这是我参与11月更文挑战的第 3 天,活动详情查看:2021最后一次更文挑战

寻找第K大

题目来源LeetCode-1985. 找出数组中的第 K 大整数

题目描述

给你一个字符串数组 nums 和一个整数 k 。nums 中的每个字符串都表示一个不含前导零的整数。

返回 nums 中表示第 k 大整数的字符串

注意:重复的数字在统计时会视为不同元素考虑。例如,如果 nums 是 ["1","2","2"],那么 "2" 是最大的整数,"2" 是第二大的整数,"1" 是第三大的整数

示例

示例 1

输入:nums = ["3","6","7","10"], k = 4
输出:"3"
解释:nums 中的数字按非递减顺序排列为 ["3","6","7","10"],其中第 4 大整数是 "3"

示例 2

输入:nums = ["2","21","12","1"], k = 3
输出:"2"
解释:nums 中的数字按非递减顺序排列为 ["1","2","12","21"],其中第 3 大整数是 "2"

示例 3

输入:nums = ["0","0"], k = 2
输出:"0"
解释:nums 中的数字按非递减顺序排列为 ["0","0"],其中第 2 大整数是 "0"

提示:

  • 1 <= k <= nums.length <= 104
  • 1 <= nums[i].length <= 100
  • nums[i] 仅由数字组成
  • nums[i] 不含任何前导零

解题

解法一:快排思想

思路

跟上一篇文章中的求最小的k个数思路是一样的,这里直接说过程:

  1. 按照快排思想,找到一个分区点,将数组中大于该分区点元素的,放到分区点右边,小于的放左边
  2. 判断分区点的位置与k的大小关系,如果分区点位置大于k,则取分区点的左边部分数组,重复步骤1
  3. 如果分区点位置小于k,则取分区点的右边部分数组,重复步骤1

代码

func FindK(arr []int, k int) int {
	if k == 0 {
		return 0
	}

	return QuickSortK(arr, 0, len(arr)-1, k)
}

func QuickSortK(arr []int, start, end, k int) int {
	for true {
		if start > end {
			return -1
		}

		pivot := partionK(arr, start, end)
		if pivot == k {
			return arr[k-1]
		} else if pivot < k {
			start = pivot + 1
		} else {
			end = pivot - 1
		}
	}

	return -1
}

func partionK(arr []int, start, end int) int {
	pivotValue := arr[end]

	i := start
	for j := start; j < len(arr); j++ {
		if arr[j] > pivotValue {
			arr[i], arr[j] = arr[j], arr[i]
			i++
		}
	}

	arr[end], arr[i] = arr[i], arr[end]

	return i
}

解法二:大顶堆

思路

跟上一篇文章中的求最小的k个数思路是一样的,这里直接说过程(维护一个大小为K的小顶堆):

  1. 遍历数组中的每一个元素
  2. 将数组中的前K个数初始化成一个小顶堆
  3. 数组中的剩余元素和堆顶元素进行比较,如果大于堆顶元素,则替换堆顶元素,并堆化
  4. 元素遍历完之后,堆顶元素就是第K大的元素

代码

func HeapFinK(arr []int, k int) int {
	if k == 0 {
		return -1
	}

	length := len(arr)
	maxK := make([]int, k+1)
	for j := 0;j < k; j++ { //arr中的前k个复制到minK中
		maxK[j+1] = arr[j]
	}

	for i:=k/2; i>0; i-- { // 堆化小顶堆
		heapifyK(maxK, k+1, i)
	}

	for x := k; x < length; x ++ {
		if arr[x] > maxK[1] { //如果当前元素大于堆顶元素
			//移除堆顶元素(其实就是从堆中删除一个元素) 删除堆顶元素,然后将最后一个元素放到堆顶,然后再进行堆化
			count := len(maxK)
			maxK[1] = maxK[count-1]
			count--
			heapifyK(maxK, count, 1)

			//将当前元素插入到堆中
			count++
			maxK[count-1] = arr[x]
			m := count-1
			for m/2 > 0 && maxK[m] < maxK[m/2] {
				maxK[m], maxK[m/2] = maxK[m/2], maxK[m]
				m = m/2
			}
		}
	}

	return maxK[1]
}

func heapifyK(a []int, n, i int) {
	for true {
		maxPos := i
		if 2*i < n && a[i] > a[2*i] {
			maxPos = 2*i
		}
		if 2*i+1 < n && a[maxPos] > a[2*i+1] {
			maxPos = 2*i+1
		}
		if maxPos == i {
			break
		}
		a[i], a[maxPos] = a[maxPos], a[i]
		i = maxPos
	}
}