归并可以解决求每个数字前面有多少个比他大或者比他小的总数问题。
归并时间复杂度:O(NlogN)
归并思想:将数组分成两部分,前半部分进行归并,后半部分归并,之后merge两部分,在merge中两个指针指向左右两部分(外排),利用一个额外的数组,左边的小就先拷贝左边,否则拷贝右边。(类似有序链表的合并)
//【归并排序】
void gui(vector<int>& nums, int left, int right) {
if(right - left <= 1 || !nums.size())
return;
gui(nums, left, left+((right-left)>>1));
gui(nums, left+((right-left)>>1)+1, right);
merge(nums, left, left+((right-left)>>1), right);
}
//【归并】
void merge(vector<int>& nums, int left, int mid, int right) {
vector<int> help(right-left+1, 0);
int i = 0, l = left, r = mid+1;
while(l <= mid && r <= right) {
help[i++] = (nums[l] <= nums[r]?) nums[l++]:nums[r++];
}
while(l <= mid)
help[i++] = nums[l++];
while(r <= right)
help[i++] = nums[r++];
for(int i = 0; i < help.size(); i++)
nums[i+left] = help[i];
}
求解逆序对的个数,只需要在merge的时候判断左边是否有比右边大的数,有就说明左边比右边指针之后的数都大,因为左右都是已经排好序的数了。
//【归并排序】
int gui(vector<int>& nums, int left, int right) {
if(right - left <= 1 || !nums.size())
return 0;
return gui(nums, left, left+((right-left)>>1)) + gui(nums, left+((right-left)>>1)+1, right) + merge(nums, left, left+((right-left)>>1), right);
}
//【归并】
void merge(vector<int>& nums, int left, int mid, int right) {
vector<int> help(right-left+1, 0);
int i = 0, l = left, r = mid+1;
int res = 0;
while(l <= mid && r <= right) {
res += (nums[l] > nums[r]?) right - r + 1 : 0;
help[i++] = (nums[l] > nums[r]?) nums[l++] : nums[r++];
}
while(l <= mid)
help[i++] = nums[l++];
while(r <= right)
help[i++] = nums[r++];
for(int i = 0; i < help.size(); i++)
nums[i+left] = help[i];
}