一、题目:Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4]
and k = 2, return 5.
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
意思:对给定的数组中找到第k大的数,
注意:这里是因为题目中给定了条件就是该数组中至少含有1个元素,若没有这个条件应该考虑为空的情况
思路:对数组从大到小排序,返回第k-1索引位置的元素。注意这里是k-1,因为数组是从0开始的。
这里首先使用了库里的排序,有一个技巧改排序算法默认是从小到大,可以使用lamda表达式或者仿函数来修改从大到小的排序。
有一个更加简单的方法就是使用反向迭代器即可。之后我会手写快排
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.rbegin(), nums.rend());
return nums[k-1];
}
};
二、手写普通快速排序
核心思路就是partition部分。
// 对arr[l...r]部分进行partition操作
// 返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
int parttion(vector<int> &nums, int l, int r)
{
//取出该区间的第一个元素v,以v为分界点,左边nums[l+1```j] < v 右边nums[j+1```i) > v
int v = nums[l];
int j = l;
// arr[l+1...j] < v ; arr[j+1...i) > v
for (int i = j + 1; i <= r;i++)
{
if (nums[i]<v)
{
swap(nums[i], nums[j + 1]);
j++;
}
}
swap(nums[j], nums[l]); //将nums[l]找到合适的位置
return j;
}
// 对arr[l...r]部分进行快速排序
void QuickSort(vector<int> &nums, int l, int r)
{
if (l >= r)
{
//递归终止的条件
return;
}
int p = parttion(nums,l,r); //将第一个元素找到合适的位置
QuickSort(nums, l, p - 1);
QuickSort(nums, p + 1, r);
}
自己写的快排速度就是不行,原因在于二叉树的平衡度性,高度可能比logn还要高,O(nlogn)最差的情况下会退化到O(n^2)会再次改进
三、随机化快速排序
针对有序的输入,普通快排会退化为O(n^2)的算法。改进的地方就是把数组之前左侧的标定改为数组内随机的元素。标红为改进处,这个改进速度一下上去
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
srand(time(NULL));
QuickSort(nums,0,nums.size()-1);
return nums[k-1];
}
int partition(vector<int> &nums, int l, int r)
{
swap(nums[l], nums[rand() % ((r - l + 1)) + 1]);
int v = nums[l];
int j = l;
for (int i = l+1; i <= r;i++)
{
if (nums[i] > v)
{
swap(nums[i], nums[j+1]);
j++;
}
}
swap(nums[l], nums[j]);
return j;
}
void QuickSort(vector<int> &nums, int l, int r)
{
if (l>=r)
{
return;
}
int p = partition(nums,l,r);
QuickSort(nums, l, p - 1);
QuickSort(nums, p + 1, r);
}
};
四、双路快排
针对有大量重复元素的数组进行随机化快排,最终随机化快排会退化成O(n^2)的算法,是因为树的高度左右两边极其不均衡,在随机化快排中将等于标定的元素放在了>v一侧也就是右侧,这样就会在右侧不平衡,造成高度高于logn,双路快排这样就能将重复的元素比较均衡的放在两侧
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
if(nums.size() == 1)
{
return nums[0];
}
srand(time(NULL));
QuickSort(nums,0,nums.size()-1);
return nums[k-1];
}
int partition2(vector<int> &nums, int l, int r)
{
//取出该区间的第一个元素v,以v为分界点,左边nums[l+1```j] < v 右边nums[j+1```i) > v
//nums[rand() % ((r - l + 1)) + 1]; //随机生成一个索引,意思就是以前是最左侧的元素作为标定,现在是数组内的随机元素作为标定
swap(nums[l], nums[rand() % ((r - l + 1)) + l]);
int v = nums[l];
int j = r;
// arr[l+1...i) <= v; arr(j...r] >= v注意条件区别于partition
int i = l + 1;
while (true)
{
//这里要注意关于重复数据处理的边界巧妙性
/*
arr[i] < v;当arr[i] >= v不会进入循环
arr[j] > v;当arr[j] <= v不会进入循环
所以当两路都有数据等于v的时候,不会进入循环,直接i++,j--
当左边一路有大于v的元素,需要交换到右边一路
当右边一路有小于v的元素,需要交换到左边一路,
*/
while (i <= r/*注意边界*/&&nums[i]>v)
{
i++;
}
while (j >= l + 1/*注意边界*/ && nums[j]<v)
{
j--;
}
if (i>j)
{
break;
}
swap(nums[i],nums[j]);
i++;
j--;
}
swap(nums[j], nums[l]);
return j;
}
void QuickSort(vector<int> &nums, int l, int r)
{
if (l>=r)
{
return;
}
int p = partition2(nums,l,r);
QuickSort(nums, l, p - 1);
QuickSort(nums, p + 1, r);
}
};
五、三路快排