快排,归并排序,利用快排解决数组中的第K个最大元素
1 归并排序
归并排序的时间复杂度为稳定的,比冒泡排序的要好,但需要开辟额外的空间。
归并排序的基本思想是假设有这样一个数组,
1之前的是排好序的,8之后的是排好序的,则我们现在只需要在建立一个数组tmp,将原始数组nums分为两块,将上面这个数组从新排列到新建的tmp数组。这一步只需要一个for循环就可以做到。
当然我们绝大部分的要排序的数组并不是这样近似排好序的,所以我们先将原始数组分为两部分,然后递归的进行对左右两部分的数组进行归并排序。当递归到只有两个元素的时候我们就可以认为这两个元素就是进行排好序的,随后直接在调用我们的归并方法。
代码分为两部分,第一部分是归并排序 void merge_sort(vector &nums,int left,int right),第二部分是归并方法void merge(vector &nums, int left, int mid, int right)。
class Solution {
public:
void merge(vector<int> &nums, int left, int mid, int right)
{
if (left >= right)
return;
vector<int> tmp;
int i = left, j = mid + 1;
while (i <= mid && j <= right)
{
if (nums[i] < nums[j])
{
tmp.push_back(nums[i]);
i++;
}
else if (nums[i] >= nums[j])
{
tmp.push_back(nums[j]);
j++;
}
}
while (i <= mid)
{
tmp.push_back(nums[i]);
i++;
}
while (j <= right)
{
tmp.push_back(nums[j]);
j++;
}
for (int i = 0; i < tmp.size(); i++)
{
nums[i+left] = tmp[i];
}
}
void merge_sort(vector<int> &nums,int left,int right)
{
if(left>=right)
return;
int mid=left+(right-left)/2;
merge_sort(nums,left,mid);
merge_sort(nums,mid+1,right);
merge(nums,left,mid,right);
}
vector<int> sortArray(vector<int>& nums) {
merge_sort(nums,0,nums.size()-1);
return nums;
}
};
2 快排
快排的时间复杂度为不稳定的O(nlogn),最好为O(n),最坏为O(n^2)。不需要从新开辟空间。
快排是利用二分的思想,将第一个位置的元素设为标记位,然后进行一次循环,交换,将数组变为标记位的左边都是小于它的数,右边都是大于他的数。然后对标记位的左右两边进行递归。
代码如下:
void quick_sort(vector<int>& nums,int left,int right)
{
if(left>=right)
return;
int i=left;
int j=right;
while(i<j)
{
// j向左走,找出比nums[left]小的数
while(nums[j]>nums[left]&&i<j)
j--;
//i向右走,找出比nums[left]大的数
while(nums[i]<=nums[left]&&i<j)
i++;
if(i<j)
swap(nums[i],nums[j]);
}
swap(nums[left],nums[i]);
quick_sort(nums,left,i-1);
quick_sort(nums,i+1,right);
}
vector<int> sortArray(vector<int>& nums) {
if(nums.size()==0||nums.size()==1)
return nums;
quick_sort(nums,0,nums.size()-1);
return nums;
}
3 利用快排解决数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
首先我们想到的是利用冒泡或者选择排序,从大到小排序,排好序后返回元素。时间复杂度太高 。但题目只要求返回第k大的元素,并没有要求我们完全排好序,所以我们想的是不要完全排好序,只排部分。有三种方法,1、利用堆排的性质,建立大根堆,使用 priority_queue。2 、快排,直接调用系统函数。 3、我们自己实现快排,对我们上面的快排方法进行剪枝。
1 堆排
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int> pq;
for(auto e:nums)
pq.push(e);
for(int i=0;i<k-1;i++)
{
pq.pop();
}
return pq.top();
}
2 快排
int findKthLargest(vector<int>& nums, int k) {
//使用快排
sort(nums.begin(), nums.end(), greater<int>());
return nums[k-1];
}
3 快排
从大到小排列
这里注意在递归的时候,如果第k大的元素出现在i的左边,k不变,但如果第k大的元素出现在i的右边,则需要在递归的时候改变k,这是因为前i个元素一定是比第k大的元素大的,在递归到i的右边时,需要将k变为k-(i-left)-1,找出右边数组的第k-(i-left)-1大的元素。
void quick_sort(vector<int>& nums, int left, int right,int k)
{
//从大到小排列
if(left>=right)
return;
int i=left,j=right;
while(i<j)
{
//从right向左找出大于nums[left]
while(i<j&&nums[j]<nums[left])
{
j--;
}
//从left向右找出小于nums[left]
while(i<j&&nums[i]>=nums[left])
{
i++;
}
if(i<j)
swap(nums[i],nums[j]);
}
swap(nums[left], nums[i]);
if (i - left + 1 == k)
return;
if (i - left + 1 > k)
quick_sort(nums, left, i - 1, k);
else
quick_sort(nums, i + 1, right, k - (i-left)-1);
}
int findKthLargest(vector<int>& nums, int k) {
//使用快排
quick_sort(nums,0,nums.size()-1,k);
return nums[k-1];
}