高等排序

高等排序

之前我们了解了一些复杂度为O(n^2)的低效率排序算法。但是当我们面对庞大的输入数据时,这些初等算法就显得没有什么实用价值了。不过,只要运用我们在前面一章所学的递归与分治法的编程技巧,就可以实现更加高效的算法。
本篇仍以之前的例题来结合说明。
例题:将给出的一串数字按照从小到大的顺序排,在第1行输入定义数组长度的整数N(N<10000),在第2行输入N个整数,以空格隔开,输出N个整数。
例如输入:1 5 3 4 7 9 8
输出:1 3 4 5 7 8 9

归并排序

归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
思路
分解(Divide):将n个元素分成个含n/2个元素的子序列。
解决(Conquer):用合并排序法对两个子序列递归的排序。
合并(Combine):合并两个已排序的子序列已得到排序结果。
代码

#include<iostream>
#define MAXN 10000 
using namespace std;
void mergeSort(int a[],int reg[],int start,int end){
	if(start >= end)
		return;
		
	int len = end - start, mid = (len>>1) + start;
	int start1 = start, end1 = mid;
	int start2 = mid+1, end2 = end; 
	mergeSort(a,reg,start1,end1);			//	归的过程 
	mergeSort(a,reg,start2,end2);
	int k = start;
	while(start1 <= end1 && start2 <= end2){		//开始筛选 
		reg[k] = a[start1] < a[start2] ? a[start1++] : a[start2++];
		k++;
	}
	while(start1 <= end1){
		reg[k] = a[start1];
		k++;
		start1++;
	}
	while(start2 <= end2){
		reg[k] = a[start2];
		k++;
		start2++;
	}
	for(k = start;k <= end;k++)				//	并的过程 
		a[k] = reg[k];
}

int main(){
	int n,a[MAXN];
	cin>>n;
	for(int i = 0;i < n;i++)
		cin>>a[i];
	int reg[n];
	mergeSort(a,reg,0,n-1);
	for(int i = 0;i < n;i++)
		cout<<a[i]<<" ";
	return 0;
} 

归并排序算法的复杂度为O(nlogn)。归并排序包含不相邻元素之间的比较,但并不会直接交换。在合并两个已排序数组时,如果遇到了相同元素,只要保证前半部分数组优于后半部分数组,相同元素的顺序就不会颠倒。因此归并排序属于稳定的排序算法。
归并排序算法虽然高效且稳定,但它在处理过程中,除了用于保存输入数据的数组之外,还需要临时占用一部分内存空间。

快速排序

快速排序,又称划分交换排序(partition-exchange sort)。其基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
思路
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。
① 从数列中挑出一个元素,称为 “基准”(pivot),
② 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③ 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
代码

#include<iostream>
#define MAXN 10000
using namespace std;
int a[MAXN];
void swap(int *x,int *y){
	int t = *x;
	*x = *y;
	*y = t;
}
void Quick_Sort(int a[],int start,int end){
	if(start >= end)
		return;
	int mid = a[end];
	int left = start, right = end - 1;
	while(left<right){
		while(a[left] < mid && left < right)
			left++;
		while(a[right] >= mid && left < right)
			right--;
		swap(&a[left],&a[right]);
	}
	if(a[left] >= a[end])
		swap(&a[left],&a[end]);
	else
		left++;
	if(left)
		Quick_Sort(a,start,left - 1);
	Quick_Sort(a,left+1,end);
}
int main(){
	int n;
	cin>>n;
	for(int i = 0;i < n;i++)
		cin>>a[i];
	Quick_Sort(a,0,n-1);
	for(int i = 0;i < n;i++)
		cout<<a[i]<<" ";
	return 0;
}

计数排序

思路
计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),然后进行分配、收集处理:
① 找出待排序的数组中最大和最小的元素
② 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
③ 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
④ 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
代码

#include<iostream>
#define MAXN 1000
using namespace std;
int a[MAXN];
int b[MAXN];

int main(){
	int n,i;
	int c[MAXN];
	cin>>n;
	for(i = 0;i < MAXN;++i)
		c[i] = 0;
	
	for(i = 0;i < n;++i){
		cin>>a[i+1];
		c[a[i+1]]++;
	}
	
	for(i = 1;i <= MAXN;++i)			//初始化计数数组 
		c[i] += c[i-1];
		
	for(i = 1;i <= n;++i){
		b[c[a[i]]] = a[i];
		c[a[i]]--;
	}
	
	for(i = 1;i <= n;++i){
		cout<<b[i]<<" ";
	}
	
	return 0;
}
 

分析
平均时间复杂度:O(n + k)
最佳时间复杂度:O(n + k)
最差时间复杂度:O(n + k)
空间复杂度:O(n + k)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值