📅  最后修改于: 2023-12-03 14:58:03.975000             🧑  作者: Mango
给定一个长度为 n
的数组 nums
,你需要通过多次交换一对随机索引的元素来对数组进行排序。现在你需要找到排序后的第 k
个最小元素。
例如,给定 nums = [1,3,4,2,5]
,通过交换索引为 2 和 3 的元素可以得到排序后的数组 [1,2,4,3,5]
。第 k 个最小元素是 4。
本题可以利用快速排序的思想来解决,对于快速排序而言,我们并不关心每次交换后的位置,而只关心该位置上的值和其他值的大小关系。因此,我们可以通过随机索引来打乱数组,然后使用快速排序的方法进行排序,最后找到第 k
小的元素。
具体做法如下:
pivot
,并以该索引的值为 pivot。随机选择一个索引 i
并将数组中的 pivot
与 nums[i]
互换位置,这样 pivot
将被置于正确的位置上。pivot
的元素放到左边,大于等于 pivot
的元素放到右边。同时,统计左边元素的个数 left_cnt
。left_cnt = k-1
,则 pivot
即为结果;如果 left_cnt < k-1
,则在右边继续查找第 k-left_cnt-1
个最小元素;如果 left_cnt > k-1
,则在左边继续查找第 k
个最小元素。k
个最小元素。代码如下:
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
self.shuffle(nums)
return self.quick_select(nums, 0, len(nums)-1, k)
def shuffle(self, nums: List[int]) -> None:
n = len(nums)
for i in range(n):
j = random.randint(i, n-1)
nums[i], nums[j] = nums[j], nums[i]
def partition(self, nums: List[int], left: int, right: int) -> int:
pivot = nums[left]
i, j = left, right
while i < j:
while i < j and nums[j] >= pivot:
j -= 1
nums[i] = nums[j]
while i < j and nums[i] < pivot:
i += 1
nums[j] = nums[i]
nums[i] = pivot
return i
def quick_select(self, nums: List[int], left: int, right: int, k: int) -> int:
if left == right:
return nums[left]
p = self.partition(nums, left, right)
left_cnt = p - left + 1
if left_cnt == k:
return nums[p]
elif left_cnt < k:
return self.quick_select(nums, p+1, right, k-left_cnt)
else:
return self.quick_select(nums, left, p-1, k)
快速选择算法的平均时间复杂度为 $O(n)$,最坏时间复杂度为 $O(n^2)$。其中最坏情况发生在每次选到的 pivot 都是数组中的最大/最小元素的情况下,可以通过随机选择 pivot 来避免。因此,本算法的期望时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。