归并排序、求逆序对(offer51)
本题求数组中的逆序对,很自然的想到了两层for循环,然而困难题,一定超时!
在评论区看到了一种利用归并排序“治”的过程,顺带将逆序对求出来了,记录一下,顺便将归并排序和快排做一个比较。
归并排序
正如图所示,归并排序是将原数组不断的划分,然后开始合并,有点二分法的意思;到只剩一个元素的时候,开始合并,将小的有序数组合并为大的有序数组
恰好,这个合并的时候,如果加一个比较的步骤,就可以求出逆序对的个数,例如:
原本的数组是:【7,3,2,6,0,1,5,4】
原本的方法是:将7与余下的数字进行比较,得出逆序次数,余下的元素也是一样的操作;
归并排序的方法:7仍然要跟所有的元素进行比较(效果上面),但是如果它的前面有比7小的数字,这个比较的过程就可以省略(实现上面)
7首先和3比较,得出逆序对1;(2,6)、(0,1)、(5,4)也相应的进行比较;
然后3和2进行比较,3比2大,所以3后面的数字也比2大(小数组已经有序了),因此逆序个数=3及3后面的元素个数;间接得到了7大于2的结果,所以时间复杂度降下来了嘛!
cpp实现
#include <bits/stdc++.h>
using namespace std;
/* 归并排序,一般也可用于求逆序对 */
class Solution
{
private:
static void MergeSort(int left,int right,vector<int>& nums,vector<int>& tmp)
{
{
//分
if(left>=right)//只有一个元素的时候开始返回
return;
int m=left+(right-left)/2;
MergeSort(left,m,nums,tmp);
MergeSort(m+1,right,nums,tmp);
//治
for(int k=left;k<=right;++k)
{
tmp[k]=nums[k];
}
int i=left,j=m+1;
for(int k=left;k<=right;++k)
{
//如果i越界了,就将nums[j]添加进tmp
if(i==m+1)
nums[k]=tmp[j++];
//如果j越界了,就将nums[i]添加进tmp
//nums[i]<=nums[j],将nums[i]添加进tmp
else if(j==right+1||tmp[i]<=tmp[j])
nums[k]=tmp[i++];
else//nums[i]>nums[j],将nums[j]添加进tmp
{
nums[k]=tmp[j++];
}
}
}
}
static int sort(int left,int right,vector<int>& nums,vector<int>& tmp)
{
//分
if(left>=right)//只有一个元素的时候开始返回
return 0;
int m=(right+left)/2;
int res=sort(left,m,nums,tmp)+sort(m+1,right,nums,tmp);
//治
for(int k=left;k<=right;++k)
{
tmp[k]=nums[k];
}
int i=left,j=m+1;
for(int k=left;k<=right;++k)
{
//如果i越界了,就将nums[j]添加进tmp
if(i==m+1)
nums[k]=tmp[j++];
//如果j越界了,就将nums[i]添加进tmp
//nums[i]<=nums[j],将nums[i]添加进tmp
else if(j==right+1||tmp[i]<=tmp[j])
nums[k]=tmp[i++];
else//nums[i]>nums[j],将nums[j]添加进tmp,同时逆序对的数量+1;
{
nums[k]=tmp[j++];
res+=m-i+1;
}
}
return res;
}
public:
static int reversePairs(vector<int>& nums)
{
vector<int> tmp(nums.size());
return sort(0,nums.size()-1,nums,tmp);
}
static void TestMergeSort(vector<int>& nums)
{
vector<int> tmp(nums.size());
MergeSort(0,nums.size()-1,nums,tmp);
}
};
int main()
{
vector<int> nums={
1,3,2,5,6,8};
vector<int> nums2={
1,3,2,5,6,8};
Solution::TestMergeSort(nums);
int c=Solution::reversePairs(nums2);
return 0;
}
与快速排序进行比较
快速排序是每次选择一个基准数据,将原数组分为两部分,左边比它小,右边比它大,然后继续将左右两边的数组进行排序;
快速排序利用了问题的信息,如果,每次都是在中间位置进行划分,那么快排的时间复杂度是O(Nlog2N),但如果,每次划分的位置在首部,那么时间复杂度就变成了O(N2),因此是不稳定的;
归并排序,在分的过程中,一直是对半分,没有利用问题的信息;在合的过程中,才开始利用问题的信息;
快速排序cpp实现
void QuickSort2(vector<int>& arr,int left,int right){
//快速排序,每次选取第一个元素作为基准数据,将数组分为两个部分,
//左边的元素都小于基准数据,右边的元素都大于基准数据。
if(left>=right)
return;
int low=left;
int high=right;
while(low<high){
while(low<high&&arr[high]>=arr[left])
high--;
while(low<high&&arr[low]<=arr[left])
low++;
swap(arr[low],arr[high]);
}
swap(arr[left],arr[low]);
QuickSort2(arr,left,low-1);
QuickSort2(arr,low+1,right);
}
end!
刷题总结类 文章被收录于专栏
这里记录一些刷题时候的总结思考