LeetCode #215 数组中的第K个最大元素 快速排序 快速选择 堆排序

LeetCode #215 数组中的第K个最大元素

题目描述

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

方法一:快排

直接调用库函数,写这种肯定找死。注意可能要打乱一下数组,否则最坏情况为 O ( N 2 ) O(N^2) O(N2)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        random.shuffle(nums)
        nums.sort(reverse=True)
        return nums[k-1]

时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)
空间复杂度: O ( 1 ) O(1) O(1)

方法二:快速选择

快排的改进,快排是一种分治思想的实现,没做一层快排可以将数组分成两份并确定一个数的位置。分析题目可以知道,要找到第 k 个最大的元素,找到这个元素被划分在哪边就可以了

class Solution:
	def findKthLargest(self, nums: List[int], k: int) -> int:
		random.shuffle(nums)
		size = len(nums)

		target = size -k
		left = 0
		right = size
		while True:
			index = self.__partition(nums, left, right)
			if index == target:
				return nums[index]
			elif index < target:
				# 下一轮在 [index + 1, right) 里找
				left = index + 1
			else:
				# 下一轮在 [left, index) 里找
				right = index

    # 只做一层快排
	def __partition(self, nums, left, right):
		base = nums[left]
		j = left
		for i in range(left + 1, right):
			if nums[i] < base:
				j += 1
				nums[i], nums[j] = nums[j], nums[i]
		nums[left], nums[j] = nums[j], nums[left]
		return j
  • 时间复杂度: O ( N ) O(N) O(N) (实际上跑得比快排还慢。。。这个 80ms,快排 48ms [捂脸])
  • 空间复杂度: O ( 1 ) O(1) O(1)
方法三:堆排序

K 大元素,其实就是数组排序后后半段最小的那个元素,因此可以维系一个有 K 个元素的“最小堆”。这里的最小堆指的是后半段元素组成的最小堆。

  1. 当i<k,压数据进堆
  2. 当i>=k,比较sums[i]和堆底元素,如果大于,则使用heapreplace进行替换,否则跳过
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
    	from heapq import heappush, heapreplace
    	# 使用小顶堆
    	heap = []
    	for i in range(len(nums)):
    		if i < k:
    			heappush(heap, nums[i])
    		elif nums[i] > heap[0]:
    				m = heapreplace(heap, nums[i])

    	return heap[0]
  • 时间复杂度: O ( N ) O(N) O(N):因为堆是初始化出来的,不是重建出来的,所以键堆操作是 O ( k ) O(k) O(k)
  • 空间复杂度: O ( k ) O(k) O(k):需要维护一个大小为 k 的大顶堆
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值