归并排序算法应用——最小和、逆序对

归并排序算法是一个时间复杂度为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;
}

总结:求解最小和、逆序对思路基本上没有差别,利用归并排序合并时,增加计算小和、计算逆序对个数的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值