归并排序

归并排序

合并排序就是采用分治的策略,将一个大的问题分成很多小问题,先解决小问题,再通过小问题解决大问题。由于排序问题给定的是一个无序的序列,可以把待排序元素分解成两个规模大致相等的子序列。如果不易解决,再将得到的子序列继续分解,直到子序列中包含的元素个数为1。因为单个元素的序列本身是有序的,此时便可以进行合并,从而得到一个完整的有序序列。

算法设计:
合并排序是采用分治策略实现对n个元素进行排序的算法,是分治法的一个典型应用和完美体现。它是一种平衡,简单的二分分治策略,过程大致分为:
1)分解-----将待排序元素分成大小大致相同的两个子序列
2)治理-----对两个子序列进行合并排序
3)合并-----将排好序的有序子序列进行合并,得到最终的有序序列

在这里插入图片描述
合并排序的核心代码:

void Merge(int A[], int low, int mid, int high)
{ //完整的合并程序
	int *B = new int[high - low + 1]; //申请一个辅助数组
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high){
		//按从小到大存放到辅助数组B[]中
		if (A[i] <= A[j])
			B[k++] = A[i++];
		else
			B[k++] = A[j++];
	}
	//对于序列A[low:middle]剩余的依次处理
	while (i <= mid) B[k++] = A[i++];
	//对于序列A[middle+1:high]剩余的依次处理 
	while (j <= high) B[k++] = A[j++];
	//将合并后的序列复制到原来的A[]序列 
	for (i = low, k = 0; i <= high; i++)
		A[i] = B[k++];
	delete[]B; //释放空间 
}
void MergeSort(int A[], int low, int high)
{
	if (low < high){
		int mid = (low + high) / 2; //取中点 
		MergeSort(A, low, mid); //对A[low:mid]中的元素合并排序 
		MergeSort(A, mid + 1, high); //对A[mid+1:high]中的元素合并排序 
		Merge(A, low, mid, high); //合并操作 
	}
}

算法分析:
(1)时间复杂度

  1. 分解:这一步仅仅是计算出子序列中的中间位置,需要常数时间O(1)。
  2. 解决子问题:递归求解两个规模为n/2的子问题,所需时间为2T(n/2)。
  3. 合并:Merge算法可以在O(n)的时间内完成。
    所以总运行时间为:
    在这里插入图片描述

(2)空间复杂度
程序中变量占用了一些辅助空间,这些辅助空间都是常数阶的,每调用一个Merge(),会分配一个适当大小的缓冲区,且退出时释放,最多分配大小为n,所以空间复杂度为O(n)。递归调用所使用的栈空间是O(logn) (每次都是折半分配)

超快速排序

https://www.acwing.com/problem/content/109/

在这个问题中,您必须分析特定的排序算法----超快速排序。
该算法通过交换两个相邻的序列元素来处理n个不同整数的序列,直到序列按升序排序。
对于输入序列9 1 0 5 4,超快速排序生成输出0 1 4 5 9。
您的任务是确定超快速排序需要执行多少交换操作才能对给定的输入序列进行排序。

输入格式
	输入包括一些测试用例。
	每个测试用例的第一行输入整数n,代表该用例中输入序列的长度。
	接下来n行每行输入一个整数ai,代表用例中输入序列的具体数据,第i行的数据代表序列中第i个数。
	当输入用例中包含的输入序列长度为0时,输入终止,该序列无需处理。

输出格式
	对于每个需要处理的输入序列,输出一个整数op,代表对给定输入序列进行排序所需的最小交换操作数,每个整数占一行。

输入样例:
	5
	9
	1
	0
	5
	4
	3
	1
	2
	3
	0

输出样例:
	6
	0

数据范围
	0 ≤ N < 500000
	0 ≤ ai ≤ 999999999

合并排序是使用了一个辅助数组来实现,其实合并过程也可以用两两交换的形式来合并。
而这里并不需要进行两两交换,只要知道有多少个元素是逆序的,需要多少次交换,答案只要次数。
例如: 左边是[2, 5,8],右边是[1],那么1想要跑到2,5,8前面,就需要3次交换,因为归并排序是将两个已经排好序的数组进行合并,也就是说,当知道2大于1时,那么2后面的数肯定都比1要大,那么就得交换mid - i + 1次。
合并时仍然使用归并排序的方法,并没有使用两两交换,只是在合并时多加了一行代码而已。
在这里插入图片描述

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
long long int cnt;
void Merge(long long int *A, int low, int mid, int high)
{
	int *B = new int[high - low + 1];
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high){
		if (A[i] <= A[j])
			B[k++] = A[i++];
		else{
			B[k++] = A[j++];
			cnt += mid - i + 1; // 在归并排序的基础上加上这一行代码
		}
	}
	while (i <= mid) B[k++] = A[i++];
	while (j <= high)B[k++] = A[j++];
	for (i = low, k = 0; i <= high; i++)
		A[i] = B[k++];
	delete[]B;
}
void MergeSort(long long int *A, int low, int high)
{
	if (low < high){
		int mid = low + (high - low) / 2;
		MergeSort(A, low, mid);
		MergeSort(A, mid + 1, high);
		Merge(A, low, mid, high);
	}
}
int main(){
	int n, i;
	while (1){
		scanf("%d", &n);
		if (!n)
			break;
		long long int *A = new long long int[n];
		cnt = 0;
		for (i = 0; i < n; i++)
			scanf("%lld", &A[i]);

		MergeSort(A, 0, n - 1);
		printf("%lld\n", cnt);
		delete[]A;
	}
	return 0;
}

Design T-Shirt

https://vjudge.net/problem/HDU-1031
在这里插入图片描述

Sample Input

3 6 4
2 2.5 5 1 3 4
5 1 3.5 2 2 2
1 1 1 1 1 10
3 3 2
1 2 3
2 3 1
3 1 2

Sample Output

6 5 3 1
2 1

题意:N个人给M个元素打分,每一行表示每一个人给M个元素打分的数据,从M个元素中选出K个元素

对样例1解释:

22.55134
513.5222
1111110
84.59.54616

最后一行是每个元素获得的总分,从大到小选K(K=4)个元素就是[16,9.5,8,6],输出他们的索引值即可。

对样例2解释:
因为每个元素获得的总分都相同,所以取前面K(K=2)个元素。

先对各元素总分进行非递增排序,之后再对前K个元素的索引值进行非递增排序。

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
struct data{
	int id;
	double value;
}a[10010];
void Merge(data *A, int low, int mid, int high, bool flag)
{
	data *B = new data[high - low + 1]; //申请一个辅助数组
	int i = low, j = mid + 1, k = 0;
	double x, y;
	while (i <= mid && j <= high){
		if (!flag){
			x = A[i].value;
			y = A[j].value;
		}
		else{
			x = A[i].id;
			y = A[j].id;
		}
		if (x >= y) //如果两个值相等,i先进
			B[k++] = A[i++];
		else
			B[k++] = A[j++];
	}
	while (i <= mid) B[k++] = A[i++];
	while (j <= high)B[k++] = A[j++];
	for (i = low, k = 0; i <= high; i++)
		A[i] = B[k++];
	delete[]B;
}
void MergeSort(data *A, int low, int high, bool flag)
{
	if (low < high){
		int mid = low + (high - low) / 2;
		MergeSort(A, low, mid, flag);
		MergeSort(A, mid + 1, high, flag);
		Merge(A, low, mid, high, flag);
	}
}
int main(){
	int n, m, k; // n--人数 m--元素个数 k--选取数量
	double x;
	while (scanf("%d%d%d", &n, &m, &k) != EOF){
		for (int i = 0; i < m; i++) //重新计数
			a[i].value = 0;
		for (int i = 0; i < m*n; i++){
			a[i%m].id = i % m + 1;
			scanf("%lf", &x);
			a[i%m].value += x;
		}
		MergeSort(a, 0, m - 1, 0); //按值非递增
		MergeSort(a, 0, k - 1, 1); //按序号非递增
		for (int i = 0; i < k-1; i++)
			printf("%d ", a[i].id);
		printf("%d\n", a[k - 1].id);
	}
	return 0;
}

这题用sort()函数会简便很多。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.DoubleBean.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值