这周讲到分治算法,因此我想用随机分治算法解决这道选择问题。
目录:
- 问题描述
- 解题步骤
- 代码
- 更新
问题描述
输入: 一列数的集合nums;一个整数k
输出: 集合S中第k大的元素
解题步骤
这道题与书上2.4节的问题如出一辙,因此,我们可以首先随机选择一个数v,然后把集合nums中的数分为三组:比v小的数,与v相等的数以及比v大的数,并分别记这三组数为left,mid,right(由于题目已经令k合法,即 1≤k≤nums.size(),我们可以不对k的合法性进行检验):
int v = nums[0];
for(int i = 0; i < nums.size(); i++) {
if(nums[i] < v) left.push_back(nums[i]);
else if(nums[i] == v) mid.push_back(nums[i]);
else right.push_back(nums[i]);
}
如果right.size() ≥ k,则在right中继续寻找第k大的数;如果right.size() + mid.size() ≥ k,则v就是第k大的数,返回v;否则,在left中寻找第(k - right.size() - mid.size())大的数:
if(right.size() >= k) return findKthLargest(right, k);
else if(right.size() + mid.size() >= k) return v;
return findKthLargest(left, k - right.size() - mid.size());
该算法的运行时间在 到 的范围内,且其平均运行时间与最佳情形下的运行时间很接近,即:
写到这里应该差不多了,我们提交试试:
哎?怎么超内存了?用自定义测试执行代码可以得到正确答案,说明算法基本正确,仔细看看这个用例,它很大,而且是按降序排好序了的,它要求找到最大的数,也就是说,left.size()将会很大,而mid中只有一个数,right.size()则为0。
为了使这个样例通过测试,我们可以试试换个v值,比如取v = nums[nums.size() / 2],再次提交,总算通过了~
以下是完整代码。
代码
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
vector<int> left;
vector<int> mid;
vector<int> right;
int v = nums[nums.size() / 2];
for(int i = 0; i < nums.size(); i++) {
if(nums[i] < v) left.push_back(nums[i]);
else if(nums[i] == v) mid.push_back(nums[i]);
else right.push_back(nums[i]);
}
if(right.size() >= k) return findKthLargest(right, k);
else if(right.size() + mid.size() >= k) return v;
return findKthLargest(left, k - right.size() - mid.size());
}
};
更新
为了避免一碰到特殊数组,算法复杂度就爆炸的问题,我们可以把v变成真正的随机数,即使用rand(),虽然运行时间在这道题上并没有得到很大的改进。
srand(time(NULL));
int index = rand() % (nums.size());
int v = nums[index];