027.归并排序
归并排序就是二叉树后序遍历
二叉树后序遍历
-
先遍历左右子树,再处理根节点
-
可以获取左右子树的信息
void postorder(TreeNode*root){
if(root==nullptr){
return;
}
//先处理左右子树
postorder(root->left);
postorder(root->right);
//后序位置
}
归并排序
-
区间一分为二,先排序左右区间,再归并
-
得到左右两个有序子区间
void mergesort(vector<int>&nums,int left,int right){
//只有一个元素,本身就是有序的
if(left==right){
return;
}
vector<int>temp;
int mid=left+(right-left)>>1;
//先排序左右区间
mergesort(nums,left,mid);
mergesort(nums,mid+1,right);
//得到两个有序子区间
//归并
int i=left,j=mid+1;
while(i<=mid&&j<=right){
if(nums[i]<nums[j]){
temp.push_back(nums[i++]);
}
else temp.push_back(nums[j++]);
}
while(i<=mid){
temp.push_back(nums[i++]);
}
while(j<=right){
temp.push_back(nums[j++]);
}
for(int x:temp){
nums[left++]=x;
}
}
应用 :不止排序
如果只是为了排序我们有很多平替,一般没人特意写个归并排序出来, std::sort() 不香吗
归并排序的特殊之处在于它在排序的过程中可以得到两个有序子区间
我们往往在归并这一过程中对这两个有序区间进行操作
经典场景比如:统计逆序对数量
题目
排序数组
class Solution {
vector<int>t;
public:
vector<int> sortArray(vector<int>& nums) {
t.resize(nums.size());
sort(nums,0,nums.size()-1);
return nums;
}
void sort(vector<int>&n,int l,int r){
if(l>=r)return;
int m=(l+r)>>1;
sort(n,l,m);
sort(n,m+1,r);
int i=l,j=m+1,p=l;
while(i<=m&&j<=r){
if(n[i]<n[j])t[p++]=n[i++];
else t[p++]=n[j++];
}
while(i<=m)t[p++]=n[i++];
while(j<=r)t[p++]=n[j++];
for(;l<=r;l++)n[l]=t[l];
}
};
计算右侧小于当前元素的个数
每当我们判断出
n[i]<n[j]时说明从mid+1到j-1都小于i
若最后剩下的是左区间,那么对于剩下的元素 : 右区间所有元素都比它小
class Solution {
vector<pair<int,int>>n;
vector<pair<int,int>>t;
vector<int>cnt;
public:
vector<int> countSmaller(vector<int>& nums) {
n.resize(nums.size());
t.resize(nums.size());
cnt.resize(nums.size());
for(size_t i=0;i<nums.size();++i){
n[i].first=i;
n[i].second=nums[i];
}
sort(0,nums.size()-1);
return cnt;
}
void sort(int l,int r){
if(l>=r)return;
int mid=(l+r)>>1;
sort(l,mid);
sort(mid+1,r);
int i=l,j=mid+1,p=l;
while(i<=mid&&j<=r){
if(n[i].second<=n[j].second){
t[p++]=n[i];
cnt[n[i++].first]+=j-mid-1;
}
else t[p++]=n[j++];
}
while(i<=mid){
t[p++]=n[i];
cnt[n[i++].first]+=r-mid;
}
while(j<=r)t[p++]=n[j++];
for(;l<=r;++l)n[l]=t[l];
}
};
翻转对
双指针尺取
充分利用区间单调性,指针只向一个方向移动
class Solution {
vector<int>t;
int ans=0;
public:
int reversePairs(vector<int>& nums) {
t.resize(nums.size());
sort(nums,0,nums.size()-1);
return ans;
}
void sort(vector<int>&n,int l,int r){
if(l>=r)return;
int m=(l+r)>>1;
sort(n,l,m);
sort(n,m+1,r);
int rr=m+1;
for(int i=l;i<=m;++i){
while(rr<=r&&(long)n[i]>(long)n[rr]*2)rr++;
ans+=rr-m-1;
}
int i=l,j=m+1,p=l;
while(i<=m&&j<=r){
if(n[i]<n[j])t[p++]=n[i++];
else t[p++]=n[j++];
}
while(i<=m)t[p++]=n[i++];
while(j<=r)t[p++]=n[j++];
for(;l<=r;l++)n[l]=t[l];
}
};
区间和的个数
区间和问题转换为前缀和相减
枚举左区间每一个
i在右区间尺取合法区间
class Solution {
vector<long long>pre;
vector<long long>t;
int lower,upper,ans=0;
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
this->lower=lower;
this->upper=upper;
int n=nums.size();
pre.resize(n+1);
t.resize(n+1);
for(int i=1;i<=n;++i)pre[i]=pre[i-1]+nums[i-1];
sort(pre,0,n);
return ans;
}
void sort(vector<long long>&n,int l,int r){
if(l>=r)return;
int m=(l+r)>>1;
sort(n,l,m);
sort(n,m+1,r);
int ll=m+1,rr=m+1;
for(int i=l;i<=m;++i){
while(ll<=r&&n[ll]-n[i]<lower)ll++;
while(rr<=r&&n[rr]-n[i]<=upper)rr++;
ans+=rr-ll;
}
int i=l,j=m+1,p=l;
while(i<=m&&j<=r){
if(n[i]<n[j])t[p++]=n[i++];
else t[p++]=n[j++];
}
while(i<=m)t[p++]=n[i++];
while(j<=r)t[p++]=n[j++];
for(;l<=r;l++)n[l]=t[l];
}
};
I am the bone of my sword

浙公网安备 33010602011771号