Kth Largest Element in an Array解题心得
https://leetcode.com/problems/kth-largest-element-in-an-array/description/
题目描述
如题意所示:寻找第K大的数,这里选这道题的原因是因为,在查阅这道题更优的算法时,引出了一个重要而且可以在其他题目里使用的算法,即为 Partition 算法,以及一个极其精简版本的partition代码
Partition算法
在解这道题前先描述一种Partition算法:
无序序列
S={a0,a1,a2,a3,....an}
随机取一个数为中间数
pivot=ai
算法的目的是使得序列
S
分成两个个子序列
其中:
S1:ai<=pivot
S2:ai>pivot
实现方法通过两个头尾指针
left right
,同时向中间过滤,直到碰到冲突的情况即对于左边
S[left]>pivot
,对于右边
S[right]<=pivot
,每次将两个冲突的元素交换;再重复两指针向中间过滤,直到两指针相遇即
left>=right
简单版本代码如下:
int partition(vector<int> &nums, int begin, int end){ //[begin, end) end--; int pivot=nums[begin] while(begin<end){ while(begin<=end&&nums[begin]<=pivot) begin++; while(begin<=end&&nums[end]>pivot) end--; if (begin<end) { int temp=nums[begin]; nums[begin]=nums[end]; nums[end]=temp; } } return begin; }
在查阅资料的过程中我发现一个更加精妙的一个实现:
int partition(vector<int> &nums, int begin, int end){ //[begin, end) int pivot=nums[begin]; while(begin<end){ while(begin<end&&nums[--end]>pivot); nums[begin]=nums[end]; while(begin<end&&nums[++begin]<=pivot); nums[end]=nums[begin]; } nums[begin]=pivot; return begin; }
在这里的代码我们可以看到,这里没有用到交换的过程,而是通过pivot元素实现了全局的左右交换,而且代码的实现精简而又富有对称性
- 对partition的研究和可以应用到快速排序算法中,甚至还能进一步实现three path partition问题(即将S分为三组,分别为小于,等于,大于)
将partition算法应用于求第k大的数
以分治的思想我们可以逐步将问题分解化
- 先对序列进行partition算法,分为两部分
- 大于等于部分的元素个数与k比较
- 决定第k大的数在哪一部分,再对新的部分重复上述partition算法,缩小求解的范围,最后得到第k大的数
- 实现
#include <stdio.h> class Solution { public: int findKthLargest(vector<int>& nums, int k) { int begin=0, end=nums.size(); while(begin<end){ int pivot=partition(nums, begin, end); if (k-1==pivot){ return nums[pivot]; } else if (k-1<pivot){ end=pivot; } else { begin=pivot+1; } } return nums[begin]; } int partition(vector<int> &nums, int begin, int end){ int pivot=nums[begin]; while(begin<end){ while(begin<end&&nums[--end]<=pivot); nums[begin]=nums[end]; while(begin<end&&nums[++begin]>=pivot); nums[end]=nums[begin]; } nums[begin]=pivot; return begin; } };
算法的时间复杂度可以通过递推式得到: T(n)=T(n/2)+O(n)
- 则实际复杂度为 O(n)