数据结构:外部排序有妙招(归并排序和基数排序)

本文介绍了外部排序的两种主要方法:归并排序和基数排序。归并排序通过分治策略将大问题分解为小问题进行排序,最后合并成有序序列。基数排序则利用多关键字排序思想,通过分配和收集过程实现排序。文章还提供了这两种排序算法的C语言实现,并讨论了它们的时间复杂度和辅助空间需求。此外,还阐述了排序算法的稳定性,稳定排序包括冒泡、插入、归并和基数排序,而不稳定排序如选择、快速、希尔和堆排序。
摘要由CSDN通过智能技术生成

外部排序

文件中的记录太多,无法全部将其同时调入内存进行的排序。

归并排序

定义:将两个或两个以上的有序表组合成一个新的有序表

2-路归并排序过程:

  1. 设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1;
  2. 两两合并,得到n/2  个长度为2或1的有序子序列;
  3. 再两两合并,……如此重复,直至得到一个长度为n的有序序列为止;

在这里插入图片描述

代码:

#include <stdio.h>
void combine(int num[], int first, int mid, int last, int num2[])//合并
{
	int i=first, j=mid+1, k=0;
	while (i <= mid&&j <= last) {//合并左右两个部分
		if (num[i] < num[j]) {
			num2[k++] = num[i++];
		} else {
			num2[k++] = num[j++];
		}
	}
	while (i <= mid) {			//若右边部分元素已取完,单独取左边部分
		num2[k++] = num[i++];
	}
	while (j <= last) {			//若左边部分元素已取完,单独取右边部分
		num2[k++] = num[j++];
	}
	for (int m = 0; m < k; m++) {//将排好序的部分装入原数组相应位置
		num[m + first] = num2[m];
	}
}
void Sort(int num[], int first, int last, int num1[])
{
	if (first < last) {
		int mid = (first + last) / 2;
		Sort(num, first, mid, num1);//递归排左边
		Sort(num, mid + 1, last, num1);//递归排右边
		combine(num, first, mid, last, num1);//合并
	}
}

int main()
{
	int num[10] = { 0,12,54,4,3,5,78,34,22,90 },num1[20];
	Sort(num, 0, 9,num1);
	for (int i = 0; i < 10; i++) {
		printf("%d ", num[i]);
	}
	return 0;
}

时间复杂度:T(n)=O(nlog2n)
辅助空间:S(n)=O(n)

基数排序

基数排序是借助多关键字排序思想:
在这里插入图片描述
最高位优先法(MSD): 先对最高位关键字k1(如花色) 排序,将序列分成若干子序列,每个子序列有相同的k1值;然后让每个子序列对次关键字k2(如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字kd排序;最后将所有子序列依次连接在一起成为一个有序序列。

最低位优先法(LSD): 从最低位关键字kd起进行排序,然后再对高一位的关键字排序,……依次重复,直至对最高位关键字k1排序后,便成为一个有序序列。

MSD与LSD不同特点

  1. 按MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序。
  2. 按LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里f[0]=n; n为数组下标。

#include <stdio.h>
int sum(int num[],int len)					//求数组中数的最大位数
{
	int k = 0;
	for (int i = 0; i < len; i++) {
		int nu = num[i], m = 0;
		while (nu != 0) {
			m++;
			nu /= 10;
		}
		if (k < m) {
			k = m;
		}
	}
	return k;  
}
void sort(int num[], int len)
{
	int num1[20][20], r[20] = { 0 };				//分别记录每个桶的数值和大小
	int m = sum(num, len);							//求最大数字位数
	for (int i = 1,n = 1; i <= m; i++,n *= 10) {	//遍历n趟
		for (int j = 0; j < len; j++) {				//每趟遍历整个数组一遍
			int nu = num[j]/n%10;					//按比较位入座
			num1[nu][r[nu]] = num[j];
			r[nu]++;
		}
		int q = 0;
		for (int k = 0; k < len; k++) {				//遍历每一个桶,并将桶中元素放入原数组
			for (int j = 0; j < r[k]; j++) {
				num[q++] = num1[k][j];
			}
		}
		for (int j = 0; j < len; j++) {				//每一堂遍历完就要将桶的大小记录数组清零
			r[j] = 0;
		}
	}
}

int main()
{
	int num[10] = { 0,12,54,4,3,5,78,34,22,90 },num1[20];
	sort(num, 10);
	for (int i = 0; i < 10; i++) {
		printf("%d ", num[i]);
	}
	return 0;
}

时间复杂度: 分配:T(n)=O(n) 收集:T(n)=O(rd) 其中rd为基数
T(n)=O(d*(n+rd)) 其中:

  • n——记录数
  • d——关键字位数
  • rd——每位关键字取值范围

辅助空间:
S(n)=2*rd个队列指针 + n个指针域空间。

注意: 外部排序是用来处理大量数据的排序方法,上面所给的基数排序的例子仅仅是为了使程序简单明了,易于对基数排序算法的理解,在实际使用中要做修改。

小结

排序算法到这里就讲完了,我们来做做总结,首先来看一张图
在这里插入图片描述
再来聊一聊算法稳定性:

稳定与不稳定: 若记录序列中的任意两个记录 Rx、Ry 的关键字 Kx = Ky ;如果在排序之前和排序之后,它们的相对位置保持不变,则这种排序方法是稳定的,否则是不稳定的。

算法稳定的意义: 排序的内容是一个复杂对象的多个数字属性(次关键字),且其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,破坏了原有排序就要再次面对复杂的数据处理过程。

稳定算法:

  1. 冒泡排序
  2. 直接插入排序
  3. 折半插入排序
  4. 归并排序
  5. 基数排序

不稳定算法 :

  1. 选择排序
  2. 快速排序
  3. 希尔排序
  4. 堆排序

好啦,朋友们下次见!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值