归并排序算法是一个时间复杂度为O(N*logN)的算法,在求解某些问题上使用归并排序会产生巧妙的作用。
归并排序代码:
#include<iostream>
#include<vector>
using namespace std;
//合并两个有序序列
void Merge(vector<int>& v, int l, int mid, int r)
{
vector<int> help(r-l+1);//准备空间存储数据合并和的有序数据
int p1 = l;
int p2 = mid + 1;
int i = 0;
while (p1 <= mid && p2 <= r)
{
help[i++] = v[p1] <= v[p2] ? v[p1++] : v[p2++];//左、右侧数据谁小就先合并,相等则先合并左侧数据
}
while (p1 <= mid)//两个循环只会执行一次
{
help[i++] = v[p1++];
}
while (p2 <= r)
{
help[i++] = v[p2++];
}
for (int j = 0; j < help.size(); ++j)
{
v[l + j] = help[j];//将合并好的数据返还到原数组
}
}
//归并递归函数
void process(vector<int>& v, int l, int r)
{
if (l == r)
{
return;//递归出口
}
int mid = l + ((r - l) >> 1);
process(v, l, mid);//左侧数据递归调用
process(v, mid + 1, r);//右侧数据递归调用
Merge(v, l, mid, r);//将左右两侧数据合并(此时两侧数据均已有序)
}
int main()
{
int n = 0;
cin >> n;
vector<int> v;
for (int i = 0; i < n; i++)
{
int num;
cin >> num;
v.push_back(num);
}
if (n < 2)
{
return 0;
}
else
{
process(v, 0, v.size() - 1);//调用递归算法进行排序
}
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
return 0;
}
归并排序的应用:在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
例子:[1,3,4,2,5] 1左边比1小的数,没有; 3左边比3小的数,1; 4左边比4小的数,1、3; 2左边比2小的数,1; 5左边比5小的数,1、3、4、2; 所以小和为1+1+3+1+1+3+4+2=16
思路:1右边比1大的数3、4、2、5,产生4个1;3右边比3大的数,4、5,产生2个3;4右边比4大的数,5,产生1个4;2右边比2大的数,5,产生1个2,所以小和为4*1+2*3+1*4+1*2=16;
代码:
#include<iostream>
#include<vector>
using namespace std;
//求最小和,该数右侧有几个比它大的数,就加上该数乘以比他大的数的个数
int Merge(vector<int>& v, int l, int mid, int r)
{
vector<int> help(r - l + 1);
int p1 = l;
int p2 = mid + 1;
int i = 0;
int res = 0;
while (p1 <= mid && p2 <= r)
{
res += v[p1] < v[p2] ? v[p1] * (r - p2 + 1) : 0;//合并时,判断左侧数据小于右侧数据
//如果小于,则会产生(r-p2+1)个左侧数据(因为此时左右两次均是有序,如 1 2 || 3 4,1小于3则会产生2个1,依次类推)
help[i++] = v[p1] < v[p2] ? v[p1++] : v[p2++];//合并操作,不同点在于左右数据相等时,要先合并右边的数据
}
while (p1 <= mid)
{
help[i++] = v[p1++];
}
while (p2 <= r)
{
help[i++] = v[p2++];
}
for (int j = 0; j < help.size(); ++j)
{
v[l + j] = help[j];
}
return res;
}
int process(vector<int>& v, int l, int r)
{
if (l == r)
{
return 0;
}
int mid = l + ((r - l) >> 1);
return process(v, l, mid) + process(v, mid + 1, r) + Merge(v, l, mid, r);//返回最小和,在Merge时求小数和
}
int main()
{
int n = 0;
cin >> n;
vector<int> v;
int res = 0;
for (int i = 0; i < n; i++)
{
int num;
cin >> num;
v.push_back(num);
}
if (n < 2)
{
return 0;
}
else
{
res = process(v, 0, v.size() - 1);
}
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << res << endl;//输出最小和
return 0;
}
归并排序的应用:对于一个包含N个非负整数的数组,如果有,且,则称为数组A中的一个逆序对。
例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。
思路如上题类似:归并排序在合并时,左侧比右侧数据大时,则左侧数据后面的所有数据都大于右侧某个数据,即产生mid - 左侧数据的位置+1个逆序对
代码:
#include<iostream>
#include<vector>
using namespace std;
//求逆序对
int Merge(vector<int>& v, int l, int mid, int r)
{
vector<int> help(r-l+1);
int p1 = l;
int p2 = mid + 1;
int i = 0;
int res = 0;
while (p1 <= mid && p2 <= r)
{
res += v[p1] > v[p2] ? (mid - p1 + 1) : 0;//如果左侧某个数据大于右侧某个数据,则产生mid-p1+1个逆序对,否则就产生0个
help[i++] = v[p1] <= v[p2] ? v[p1++] : v[p2++];//合并
}
while (p1 <= mid)
{
help[i++] = v[p1++];
}
while (p2 <= r)
{
help[i++] = v[p2++];
}
for (int j = 0; j < help.size(); ++j)
{
v[l + j] = help[j];
}
return res;
}
int process(vector<int>& v, int l, int r)
{
if (l == r)
{
return 0;
}
int mid = l + ((r - l) >> 1);
return process(v, l, mid) + process(v, mid + 1, r) + Merge(v, l, mid, r);//Merge时计算逆序对个数
}
int main()
{
int n = 0;
cin >> n;
vector<int> v;
int res = 0;
for (int i = 0; i < n; i++)
{
int num;
cin >> num;
v.push_back(num);
}
if (n < 2)
{
return 0;
}
else
{
res = process(v, 0, v.size() - 1);
cout << res << endl;
}
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
return 0;
}
总结:求解最小和、逆序对思路基本上没有差别,利用归并排序合并时,增加计算小和、计算逆序对个数的代码。