外部排序
文件中的记录太多,无法全部将其同时调入内存进行的排序。
归并排序
定义:将两个或两个以上的有序表组合成一个新的有序表
2-路归并排序过程:
- 设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1;
- 两两合并,得到n/2 个长度为2或1的有序子序列;
- 再两两合并,……如此重复,直至得到一个长度为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不同特点
- 按MSD排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序。
- 按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 ;如果在排序之前和排序之后,它们的相对位置保持不变,则这种排序方法是稳定的,否则是不稳定的。
算法稳定的意义: 排序的内容是一个复杂对象的多个数字属性(次关键字),且其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,破坏了原有排序就要再次面对复杂的数据处理过程。
稳定算法:
- 冒泡排序
- 直接插入排序
- 折半插入排序
- 归并排序
- 基数排序
不稳定算法 :
- 选择排序
- 快速排序
- 希尔排序
- 堆排序
好啦,朋友们下次见!!!