快速选择算法
快速选择算法
快速选择算法错误示例
假定pivot选择5,快速选择第二大的数
选择前 | 3 | 1 | 2 | 5 | 6 | 4 |
---|---|---|---|---|---|---|
选择后 | 3 | 1 | 2 | 4 | 6 | 5 |
≤ | 3 | 1 | 2 | 5 | 4 | 6 |
- 在程序运行过程中,i在5处判断失效,j在4处判断失效
- 交换5和4后,先做i++与j—跳过该位置,最后i,j处并不是要找的值
- 如果不先进行i++与j—,程序运行超时(如i与j的位置都是5,即都是去等号的时候发生死循环)
- 如果在while(nums[i] < pivot)中改成≤ ,并不能确定5在左边还是在右边,最后不能确定要找的值在那边
本题题目为Leetcode215. 数组中的第K个最大元素
给定整数数组
nums
和整数k
,请返回数组中第k
个最大的元素。请注意,你需要找的是数组排序后的第
k
个最大的元素,而不是第k
个不同的元素。你必须设计并实现时间复杂度为
O(n)
的算法解决此问题。示例 1:
输入:[3,2,1,5,6,4], k = 2 输出: 5
示例 2:
输入:[3,2,3,1,2,4,5,5,6],k = 4 输出: 4
提示:
1 <= k <= nums.length <= 105
104 <= nums[i] <= 104
class Solution {
public:
void quickselect(vector<int>& nums, int k, int l, int r)
{
int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
int sl = j - l + 1;
if(sl == k) return;
if(sl > k) quickselect(nums, k, l, j);
if(sl < k) quickselect(nums, k-sl, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
quickselect(nums, nums.size() - k + 1, 0, nums.size() - 1);
return nums[nums.size() - k];
}
};
另外尝试版本,能通过上面不通过用例,但仍失败,由于i,j处并不是要找的值,发生死循环
class Solution {
public:
void quickselect(vector<int>& nums, int k, int l, int r)
{
int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
int sl = j - l -1;
if(sl == k) return;
if(sl > k) quickselect(nums, k, l, j-1);
if(sl < k) quickselect(nums, k- sl -1, j, r);
}
int findKthLargest(vector<int>& nums, int k) {
quickselect(nums, nums.size() - k, 0, nums.size() - 1);
return nums[nums.size() - k];
}
};
class Solution {
public:
void quickselect(vector<int>& nums, int k, int l, int r)
{
int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
if(j == k) return;
if(j > k) quickselect(nums, k, l, j);
if(j < k) quickselect(nums, k, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
quickselect(nums, nums.size() - k, 0, nums.size() - 1);
return nums[nums.size() - k];
}
};
快速选择算法正确示例
一定要循环到l==r
class Solution {
public:
void quickselect(vector<int>& nums, int k, int l, int r)
{
if(l == r) return;
int i = l - 1, j = r + 1, pivot = nums[l + r >> 1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
if(j >= k) quickselect(nums, k, l, j);
if(j < k) quickselect(nums, k, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
quickselect(nums, nums.size() - k, 0, nums.size() - 1);
return nums[nums.size() - k];
}
};
- 为什么是j≥k 而不是j≤k(经测试j≤k不行)
- 由于j可能是等于k的,所以需要quickselect(nums, k, l, j)
- 例如上表 pivot取到5分为以下两种情况
- j或者i指向5且能进行swap,a[i]≥a[j]
- a[i]>a[j],会在swap之后进行j—,即j会指向5前面
- a[i]=a[j],会在swap之后进行j—,j会指向其中一个5前面(假定有多个5
- 不能换了
- j指向5
- j或者i指向5且能进行swap,a[i]≥a[j]
此版本可以使用i > k,使用i≥k会出问题
class Solution {
public:
int quickselect(vector<int>& nums, int k, int l, int r)
{
if (l == r) return nums[k];
int i = l - 1 , j = r + 1 , pivot = nums[r];
while (i < j){
do j--; while(nums[j] > pivot);
do i++; while(nums[i] < pivot);
if(i < j)swap(nums[i], nums[j]);
}
if(i > k) return quickselect(nums, k, l, i-1);
return quickselect(nums, k, i, r);
}
int findKthLargest(vector<int>& nums, int k) {
return quickselect(nums, nums.size() - k , 0, nums.size() - 1);
}
};
ACwing版本修改
class Solution {
public:
int quickselect(vector<int>& nums, int k, int l, int r)
{
if (l == r) return nums[l];
int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
int sl = j - l;
if(sl >= k) return quickselect(nums, k, l, j);
return quickselect(nums, k-sl-1, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
return quickselect(nums, nums.size() - k, 0, nums.size() - 1);
}
};
class Solution {
public:
int quickselect(vector<int>& nums, int k, int l, int r)
{
if (l == r) return nums[l];
int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
int sl = j - l + 1;
if(sl >= k) return quickselect(nums, k, l, j);
return quickselect(nums, k-sl, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
return quickselect(nums, nums.size() - k + 1, 0, nums.size() - 1);
}
};
LeetCode 解法
class Solution {
public:
int quickselect(vector<int>& nums, int k, int l, int r)
{
if (l == r) return nums[l];
int i = l - 1 , j = r + 1 , pivot = nums[l+r>>1];
while (i < j){
do i++; while(nums[i] < pivot);
do j--; while(nums[j] > pivot);
if(i < j)swap(nums[i], nums[j]);
}
if(j >= k) return quickselect(nums, k, l, j);
return quickselect(nums, k, j+1, r);
}
int findKthLargest(vector<int>& nums, int k) {
return quickselect(nums, nums.size() - k , 0, nums.size() - 1);
}
};
总结:总体来说快速选择算法思想采用快速排序的分治思想,但是需要注意边界条件,以及最终返回值的选择