[数据结构]排序相关算法
目录
1.5 排序
1.5.1 直接插入排序
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
#define LT(a, b) ((a)<(b))
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void InsertSort(SqList *L) { /* 对顺序表L作直接插入排序。*/
int i, j;
for (i = 2; i <= (*L).length; ++i)
if LT((*L).r[i].key, (*L).r[i - 1].key) /* "<",需将L.r[i]插入有序子表 */
{
(*L).r[0] = (*L).r[i]; /* 复制为哨兵 */
for (j = i - 1; LT((*L).r[0].key, (*L).r[j].key); --j)
(*L).r[j + 1] = (*L).r[j]; /* 记录后移 */
(*L).r[j + 1] = (*L).r[0]; /* 插入到正确位置 */
}
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l1;
int i;
for (i = 0; i < N; i++) /* 给l1.r赋值 */
l1.r[i + 1] = d[i];
l1.length = N;
printf("排序前:\n");
print(l1);
InsertSort(&l1);
printf("直接插入排序后:\n");
print(l1);
return 0;
}
运行结果
1.5.2 折半插入排序(顺序结构)
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
#define LT(a, b) ((a)<(b))
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void BInsertSort(SqList *L) { /* 对顺序表L作折半插入排序。*/
int i, j, m, low, high;
for (i = 2; i <= (*L).length; ++i) {
(*L).r[0] = (*L).r[i]; /* 将L.r[i]暂存到L.r[0] */
low = 1;
high = i - 1;
while (low <= high) { /* 在r[low..high]中折半查找有序插入的位置 */
m = (low + high) / 2; /* 折半 */
if LT((*L).r[0].key, (*L).r[m].key)
high = m - 1; /* 插入点在低半区 */
else
low = m + 1; /* 插入点在高半区 */
}
for (j = i - 1; j >= high + 1; --j)
(*L).r[j + 1] = (*L).r[j]; /* 记录后移 */
(*L).r[high + 1] = (*L).r[0]; /* 插入 */
}
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l1;
int i;
for (i = 0; i < N; i++) /* 给l1.r赋值 */
l1.r[i + 1] = d[i];
l1.length = N;
printf("排序前:\n");
print(l1);
BInsertSort(&l1);
printf("折半插入排序后:\n");
print(l1);
return 0;
}
运行结果
1.5.3 2—路插入排序(顺序结构)
#include <stdio.h> /* EOF(=^Z或F6),NULL */
#include <stdlib.h>
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
#define LT(a, b) ((a)<(b))
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void P2_InsertSort(SqList *L) { /* 2_路插入排序 */
int i, j, first, final;
RedType *d;
d = (RedType *) malloc((*L).length * sizeof(RedType)); /* 生成L.length个记录的临时空间 */
d[0] = (*L).r[1]; /* 设L的第1个记录为d中排好序的记录(在位置[0]) */
first = final = 0; /* first、final分别指示d中排好序的记录的第1个和最后1个记录的位置 */
for (i = 2; i <= (*L).length; ++i) { /* 依次将L的第2个~最后1个记录插入d中 */
if ((*L).r[i].key < d[first].key) { /* 待插记录小于d中最小值,插到d[first]之前(不需移动d数组的元素) */
first = (first - 1 + (*L).length) % (*L).length; /* 设d为循环向量 */
d[first] = (*L).r[i];
} else if ((*L).r[i].key > d[final].key) { /* 待插记录大于d中最大值,插到d[final]之后(不需移动d数组的元素) */
final = final + 1;
d[final] = (*L).r[i];
} else { /* 待插记录大于d中最小值,小于d中最大值,插到d的中间(需要移动d数组的元素) */
j = final++; /* 移动d的尾部元素以便按序插入记录 */
while ((*L).r[i].key < d[j].key) {
d[(j + 1) % (*L).length] = d[j];
j = (j - 1 + (*L).length) % (*L).length;
}
d[j + 1] = (*L).r[i];
}
}
for (i = 1; i <= (*L).length; i++) /* 把d赋给L.r */
(*L).r[i] = d[(i + first - 1) % (*L).length]; /* 线性关系 */
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l1;
int i;
for (i = 0; i < N; i++) /* 给l1.r赋值 */
l1.r[i + 1] = d[i];
l1.length = N;
printf("排序前:\n");
print(l1);
P2_InsertSort(&l1);
printf("2_路插入排序后:\n");
print(l1);
}
运行结果
1.5.4 折半插入排序(链式结构)
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<stdlib.h> /* atoi() */
#define N 8
#define SIZE 100 /* 静态链表容量 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType rc; /* 记录项 */
int next; /* 指针项 */
} SLNode; /* 表结点类型 */
typedef struct {
SLNode r[SIZE]; /* 0号单元为表头结点 */
int length; /* 链表当前长度 */
} SLinkListType; /* 静态链表类型 */
void TableInsert(SLinkListType *SL, RedType D[], int n) { /* 由数组D建立n个元素的表插入排序的静态链表SL */
int i, p, q;
(*SL).r[0].rc.key = INT_MAX; /* 表头结点记录的关键字取最大整数(非降序链表的表尾) */
(*SL).r[0].next = 0; /* next域为0表示表尾(现为空表,初始化) */
for (i = 0; i < n; i++) {
(*SL).r[i + 1].rc = D[i]; /* 将数组D的值赋给静态链表SL */
q = 0;
p = (*SL).r[0].next;
while ((*SL).r[p].rc.key <= (*SL).r[i + 1].rc.key) { /* 静态链表向后移 */
q = p;
p = (*SL).r[p].next;
}
(*SL).r[i + 1].next = p; /* 将当前记录插入静态链表 */
(*SL).r[q].next = i + 1;
}
(*SL).length = n;
}
void Arrange(SLinkListType *SL) { /* 根据静态链表SL中各结点的指针值调整记录位置,使得SL中记录按关键字 */
/* 非递减有序顺序排列*/
int i, p, q;
SLNode t;
p = (*SL).r[0].next; /* p指示第一个记录的当前位置 */
for (i = 1; i < (*SL).length; ++i) { /* (*SL).r[1..i-1]中记录已按关键字有序排列,第i个记录在SL中的当前位置应不小于i */
while (p < i)
p = (*SL).r[p].next; /* 找到第i个记录,并用p指示其在SL中当前位置 */
q = (*SL).r[p].next; /* q指示尚未调整的表尾 */
if (p != i) {
t = (*SL).r[p]; /* 交换记录,使第i个记录到位 */
(*SL).r[p] = (*SL).r[i];
(*SL).r[i] = t;
(*SL).r[i].next = p; /* 指向被移走的记录,使得以后可由while循环找回 */
}
p = q; /* p指示尚未调整的表尾,为找第i+1个记录作准备 */
}
}
void Sort(SLinkListType L, int adr[]) { /* 求得adr[1..L.length],adr[i]为静态链表L的第i个最小记录的序号 */
int i = 1, p = L.r[0].next;
while (p) {
adr[i++] = p;
p = L.r[p].next;
}
}
void print(SLinkListType L) {
int i;
for (i = 1; i <= L.length; i++)
printf("key=%d ord=%d next=%d\n", L.r[i].rc.key, L.r[i].rc.otherinfo, L.r[i].next);
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SLinkListType l1;
int *adr, i;
TableInsert(&l1, d, N);
printf("排序前:\n");
print(l1);
Arrange(&l1);
printf("l1排序后:\n");
print(l1);
return 0;
}
运行结果
1.5.5 2—路插入排序(链式结构)
#include <limits.h> /* INT_MAX等 */
#include <stdio.h> /* EOF(=^Z或F6),NULL */
#include <stdlib.h>
#define N 8
#define SIZE 100 /* 静态链表容量 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType rc; /* 记录项 */
int next; /* 指针项 */
} SLNode; /* 表结点类型 */
typedef struct {
SLNode r[SIZE]; /* 0号单元为表头结点 */
int length; /* 链表当前长度 */
} SLinkListType; /* 静态链表类型 */
void TableInsert(SLinkListType *SL, RedType D[], int n) { /* 由数组D建立n个元素的表插入排序的静态链表SL */
int i, p, q;
(*SL).r[0].rc.key = INT_MAX; /* 表头结点记录的关键字取最大整数(非降序链表的表尾) */
(*SL).r[0].next = 0; /* next域为0表示表尾(现为空表,初始化) */
for (i = 0; i < n; i++) {
(*SL).r[i + 1].rc = D[i]; /* 将数组D的值赋给静态链表SL */
q = 0;
p = (*SL).r[0].next;
while ((*SL).r[p].rc.key <= (*SL).r[i + 1].rc.key) { /* 静态链表向后移 */
q = p;
p = (*SL).r[p].next;
}
(*SL).r[i + 1].next = p; /* 将当前记录插入静态链表 */
(*SL).r[q].next = i + 1;
}
(*SL).length = n;
}
void Sort(SLinkListType L, int adr[]) { /* 求得adr[1..L.length],adr[i]为静态链表L的第i个最小记录的序号 */
int i = 1, p = L.r[0].next;
while (p) {
adr[i++] = p;
p = L.r[p].next;
}
}
void Rearrange(SLinkListType *L, int adr[]) { /* adr给出静态链表L的有序次序,即L.r[adr[i]]是第i小的记录。 */
int i, j, k;
for (i = 1; i < (*L).length; ++i)
if (adr[i] != i) {
j = i;
(*L).r[0] = (*L).r[i]; /* 暂存记录(*L).r[i] */
while (adr[j] != i) { /* 调整(*L).r[adr[j]]的记录到位直到adr[j]=i为止 */
k = adr[j];
(*L).r[j] = (*L).r[k];
adr[j] = j;
j = k; /* 记录按序到位 */
}
(*L).r[j] = (*L).r[0];
adr[j] = j;
}
}
void print(SLinkListType L) {
int i;
for (i = 1; i <= L.length; i++)
printf("key=%d ord=%d next=%d\n", L.r[i].rc.key, L.r[i].rc.otherinfo, L.r[i].next);
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SLinkListType l1;
int *adr, i;
TableInsert(&l1, d, N);
printf("排序前:\n");
print(l1);
adr = (int *) malloc((l1.length + 1) * sizeof(int));
Sort(l1, adr);
for (i = 1; i <= l1.length; i++)
printf("adr[%d]=%d ", i, adr[i]);
printf("\n");
Rearrange(&l1, adr);
printf("l1排序后:\n");
print(l1);
return 0;
}
运行结果
1.5.6 希尔排序
#include<stdio.h>
#define N 10
#define T 3
#define LT(a, b) ((a)<(b))
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void ShellInsert(SqList *L, int dk) { /* 对顺序表L作一趟希尔插入排序。本算法是和一趟直接插入排序相比, */
/* 作了以下修改: */
/* 1.前后记录位置的增量是dk,而不是1; */
/* 2.r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。*/
int i, j;
for (i = dk + 1; i <= (*L).length; ++i)
if LT((*L).r[i].key, (*L).r[i - dk].key) { /* 需将(*L).r[i]插入有序增量子表 */
(*L).r[0] = (*L).r[i]; /* 暂存在(*L).r[0] */
for (j = i - dk; j > 0 && LT((*L).r[0].key, (*L).r[j].key); j -= dk)
(*L).r[j + dk] = (*L).r[j]; /* 记录后移,查找插入位置 */
(*L).r[j + dk] = (*L).r[0]; /* 插入 */
}
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("%d ", L.r[i].key);
printf("\n");
}
void print1(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
void ShellSort(SqList *L, int dlta[], int t) { /* 按增量序列dlta[0..t-1]对顺序表L作希尔排序。*/
int k;
for (k = 0; k < t; ++k) {
ShellInsert(L, dlta[k]); /* 一趟增量为dlta[k]的插入排序 */
printf("第%d趟排序结果: ", k + 1);
print(*L);
}
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8},
{55, 9},
{4, 10}};
SqList l;
int i, dt[T] = {5, 3, 1}; /* 增量序列数组 */
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前: ");
print(l);
ShellSort(&l, dt, T);
printf("排序后: ");
print1(l);
return 0;
}
运行结果
1.5.7 冒泡排序
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#define TRUE 1
#define FALSE 0
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
#define N 8
void bubble_sort(int a[], int n) { /* 将a中整数序列重新排列成自小至大有序的整数序列(起泡排序) */
int i, j, t;
Status change;
for (i = n - 1, change = TRUE; i > 1 && change; --i) {
change = FALSE;
for (j = 0; j < i; ++j)
if (a[j] > a[j + 1]) {
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
change = TRUE;
}
}
}
void print(int r[], int n) {
int i;
for (i = 0; i < n; i++)
printf("%d ", r[i]);
printf("\n");
}
int main() {
int d[N] = {49, 38, 65, 97, 76, 13, 27, 49};
printf("排序前:\n");
print(d, N);
bubble_sort(d, N);
printf("排序后:\n");
print(d, N);
return 0;
}
运行结果
1.5.8 一趟快速排序
#include<stdio.h>
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef int InfoType;
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
int Partition(SqList *L, int low, int high) { /* 交换顺序表L中子表r[low..high]的记录,枢轴记录到位,并返回其 */
/* 所在位置,此时在它之前(后)的记录均不大(小)于它。*/
KeyType pivotkey;
(*L).r[0] = (*L).r[low]; /* 用子表的第一个记录作枢轴记录 */
pivotkey = (*L).r[low].key; /* 枢轴记录关键字 */
while (low < high) { /* 从表的两端交替地向中间扫描 */
while (low < high && (*L).r[high].key >= pivotkey)
--high;
(*L).r[low] = (*L).r[high]; /* 将比枢轴记录小的记录移到低端 */
while (low < high && (*L).r[low].key <= pivotkey)
++low;
(*L).r[high] = (*L).r[low]; /* 将比枢轴记录大的记录移到高端 */
}
(*L).r[low] = (*L).r[0]; /* 枢轴记录到位 */
return low; /* 返回枢轴位置 */
}
void QSort(SqList *L, int low, int high) { /* 对顺序表L中的子序列L.r[low..high]作快速排序。*/
int pivotloc;
if (low < high) { /* 长度大于1 */
pivotloc = Partition(L, low, high); /* 将L.r[low..high]一分为二 */
QSort(L, low, pivotloc - 1); /* 对低子表递归排序,pivotloc是枢轴位置 */
QSort(L, pivotloc + 1, high); /* 对高子表递归排序 */
}
}
void QuickSort(SqList *L) { /* 对顺序表L作快速排序。*/
QSort(L, 1, (*L).length);
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l;
int i;
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前:\n");
print(l);
QuickSort(&l);
printf("排序后:\n");
print(l);
return 0;
}
运行结果
1.5.9 一趟快速排序的改进算法
#include<stdio.h>
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef int InfoType;
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
int Partition(SqList *L, int low, int high) { /* 交换顺序表L中子表r[low..high]的记录,枢轴记录到位,并返回其 */
/* 所在位置,此时在它之前(后)的记录均不大(小)于它。*/
KeyType pivotkey;
(*L).r[0] = (*L).r[low]; /* 用子表的第一个记录作枢轴记录 */
pivotkey = (*L).r[low].key; /* 枢轴记录关键字 */
while (low < high) { /* 从表的两端交替地向中间扫描 */
while (low < high && (*L).r[high].key >= pivotkey)
--high;
(*L).r[low] = (*L).r[high]; /* 将比枢轴记录小的记录移到低端 */
while (low < high && (*L).r[low].key <= pivotkey)
++low;
(*L).r[high] = (*L).r[low]; /* 将比枢轴记录大的记录移到高端 */
}
(*L).r[low] = (*L).r[0]; /* 枢轴记录到位 */
return low; /* 返回枢轴位置 */
}
void QSort(SqList *L, int low, int high) { /* 对顺序表L中的子序列L.r[low..high]作快速排序。*/
int pivotloc;
if (low < high) { /* 长度大于1 */
pivotloc = Partition(L, low, high); /* 将L.r[low..high]一分为二 */
QSort(L, low, pivotloc - 1); /* 对低子表递归排序,pivotloc是枢轴位置 */
QSort(L, pivotloc + 1, high); /* 对高子表递归排序 */
}
}
void QuickSort(SqList *L) { /* 对顺序表L作快速排序。*/
QSort(L, 1, (*L).length);
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l;
int i;
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前:\n");
print(l);
QuickSort(&l);
printf("排序后:\n");
print(l);
return 0;
}
运行结果
1.5.10 简单选择排序
#include<stdio.h>
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
int SelectMinKey(SqList L, int i) { /* 返回在L.r[i..L.length]中key最小的记录的序号 */
KeyType min;
int j, k;
k = i; /* 设第i个为最小 */
min = L.r[i].key;
for (j = i + 1; j <= L.length; j++)
if (L.r[j].key < min) /* 找到更小的 */
{
k = j;
min = L.r[j].key;
}
return k;
}
void SelectSort(SqList *L) { /* 对顺序表L作简单选择排序。*/
int i, j;
RedType t;
for (i = 1; i < (*L).length; ++i) { /* 选择第i小的记录,并交换到位 */
j = SelectMinKey(*L, i); /* 在L.r[i..L.length]中选择key最小的记录 */
if (i != j) { /* 与第i个记录交换 */
t = (*L).r[i];
(*L).r[i] = (*L).r[j];
(*L).r[j] = t;
}
}
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l;
int i;
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前:\n");
print(l);
SelectSort(&l);
printf("排序后:\n");
print(l);
return 0;
}
1.5.11 箱子排序
#include "stdio.h"
#define num_arr 5
typedef struct attr {
int key;
char name[10];
} ArrType;
void sort(ArrType A[]) {
int i;
ArrType B[num_arr];
for (i = 0; i < num_arr; i++)
B[A[i].key] = A[i];
for (i = 0; i < num_arr; i++)
printf("%s\n", B[i].name);
}
int main() {
ArrType c[num_arr] = {{4, "John"},
{1, "Jane"},
{0, "Alice"},
{2, "Peter"},
{3, "Tom"}};
sort(c);
return 0;
}
运行结果
1.5.12 树型选择排序
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<math.h> /* floor(),ceil(),abs() */
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void TreeSort(SqList *L) { /* 树形选择排序 */
int i, j, j1, k, k1, l, n = (*L).length;
RedType *t;
l = (int) ceil(log(n) / log(2)) + 1; /* 完全二叉树的层数 */
k = (int) pow(2, l) - 1; /* l层完全二叉树的结点总数 */
k1 = (int) pow(2, l - 1) - 1; /* l-1层完全二叉树的结点总数 */
t = (RedType *) malloc(k * sizeof(RedType)); /* 二叉树采用顺序存储结构 */
for (i = 1; i <= n; i++) /* 将L.r赋给叶子结点 */
t[k1 + i - 1] = (*L).r[i];
for (i = k1 + n; i < k; i++) /* 给多余的叶子的关键字赋无穷大 */
t[i].key = INT_MAX;
j1 = k1;
j = k;
while (j1) { /* 给非叶子结点赋值 */
for (i = j1; i < j; i += 2)
t[i].key < t[i + 1].key ? (t[(i + 1) / 2 - 1] = t[i]) : (t[(i + 1) / 2 - 1] = t[i + 1]);
j = j1;
j1 = (j1 - 1) / 2;
}
for (i = 0; i < n; i++) {
(*L).r[i + 1] = t[0]; /* 将当前最小值赋给L.r[i] */
j1 = 0;
for (j = 1; j < l; j++) /* 沿树根找结点t[0]在叶子中的序号j1 */
t[2 * j1 + 1].key == t[j1].key ? (j1 = 2 * j1 + 1) : (j1 = 2 * j1 + 2);
t[j1].key = INT_MAX;
while (j1) {
j1 = (j1 + 1) / 2 - 1; /* 序号为j1的结点的双亲结点序号 */
t[2 * j1 + 1].key <= t[2 * j1 + 2].key ? (t[j1] = t[2 * j1 + 1]) : (t[j1] = t[2 * j1 + 2]);
}
}
free(t);
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
SqList l;
int i;
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前:\n");
print(l);
TreeSort(&l);
printf("排序后:\n");
print(l);
return 0;
}
运行结果
1.5.13 堆排序
#include<stdio.h>
#define N 8
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
#define LT(a, b) ((a)<(b))
typedef SqList HeapType; /* 堆采用顺序表存储表示 */
void HeapAdjust(HeapType *H, int s, int m) { /* 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,本函数 */
/* 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆(对其中记录的关键字而言) */
RedType rc;
int j;
rc = (*H).r[s];
for (j = 2 * s; j <= m; j *= 2) { /* 沿key较大的孩子结点向下筛选 */
if (j < m && LT((*H).r[j].key, (*H).r[j + 1].key))
++j; /* j为key较大的记录的下标 */
if (!LT(rc.key, (*H).r[j].key))
break; /* rc应插入在位置s上 */
(*H).r[s] = (*H).r[j];
s = j;
}
(*H).r[s] = rc; /* 插入 */
}
void HeapSort(HeapType *H) { /* 对顺序表H进行堆排序。*/
RedType t;
int i;
for (i = (*H).length / 2; i > 0; --i) /* 把H.r[1..H.length]建成大顶堆 */
HeapAdjust(H, i, (*H).length);
for (i = (*H).length; i > 1; --i) { /* 将堆顶记录和当前未经排序子序列H.r[1..i]中最后一个记录相互交换 */
t = (*H).r[1];
(*H).r[1] = (*H).r[i];
(*H).r[i] = t;
HeapAdjust(H, 1, i - 1); /* 将H.r[1..i-1]重新调整为大顶堆 */
}
}
void print(HeapType H) {
int i;
for (i = 1; i <= H.length; i++)
printf("(%d,%d)", H.r[i].key, H.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7},
{49, 8}};
HeapType h;
int i;
for (i = 0; i < N; i++)
h.r[i + 1] = d[i];
h.length = N;
printf("排序前:\n");
print(h);
HeapSort(&h);
printf("排序后:\n");
print(h);
return 0;
}
1.5.14 归并排序
#include<stdio.h>
#define N 7
#define LQ(a, b) ((a)<=(b))
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef int InfoType; /* 定义其它数据项的类型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
void Merge(RedType SR[], RedType TR[], int i, int m, int n) { /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
int j, k, l;
for (j = m + 1, k = i; i <= m && j <= n; ++k) /* 将SR中记录由小到大地并入TR */
if LQ(SR[i].key, SR[j].key)
TR[k] = SR[i++];
else
TR[k] = SR[j++];
if (i <= m)
for (l = 0; l <= m - i; l++)
TR[k + l] = SR[i + l]; /* 将剩余的SR[i..m]复制到TR */
if (j <= n)
for (l = 0; l <= n - j; l++)
TR[k + l] = SR[j + l]; /* 将剩余的SR[j..n]复制到TR */
}
void MSort(RedType SR[], RedType TR1[], int s, int t) { /* 将SR[s..t]归并排序为TR1[s..t]。*/
int m;
RedType TR2[MAXSIZE + 1];
if (s == t)
TR1[s] = SR[s];
else {
m = (s + t) / 2; /* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */
MSort(SR, TR2, s, m); /* 递归地将SR[s..m]归并为有序的TR2[s..m] */
MSort(SR, TR2, m + 1, t); /* 递归地将SR[m+1..t]归并为有序的TR2[m+1..t] */
Merge(TR2, TR1, s, m, t); /* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */
}
}
void MergeSort(SqList *L) { /* 对顺序表L作归并排序。*/
MSort((*L).r, (*L).r, 1, (*L).length);
}
void print(SqList L) {
int i;
for (i = 1; i <= L.length; i++)
printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
printf("\n");
}
int main() {
RedType d[N] = {{49, 1},
{38, 2},
{65, 3},
{97, 4},
{76, 5},
{13, 6},
{27, 7}};
SqList l;
int i;
for (i = 0; i < N; i++)
l.r[i + 1] = d[i];
l.length = N;
printf("排序前:\n");
print(l);
MergeSort(&l);
printf("排序后:\n");
print(l);
return 0;
}
运行结果
1.5.15 多路平衡归并排序
#include <random>
#include <time.h>
#include <vector>
#include <array>
#include <algorithm>
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
//创建待排序文件 包含10万个随机生成的整数
void CreateFile() {
ofstream out("test.txt");
default_random_engine e(time(0));
uniform_int_distribution<int> u(0, 100000);
for (int i = 0; i < 100000; i++) {
out << u(e) << endl;
}
out.close();
}
//多路平衡归并,用来排序整数
const int BuffSize = 10000;//假设最大缓存空间为10000
const int merge_K = 4;//归并的路数
//一开始使用的内排序算法,对10万条数据进行排序,生成10个各自包含10000条有序数据的文件
int InnerSort(string filepath) {
ifstream in(filepath);
ofstream out;
vector<int> list;
int filenum = 0;
string temp;
while (true) {
while (list.size() < BuffSize) {
if (in >> temp) {
list.push_back(stoi(temp));
} else break;
}
if (list.size() == 0)break;
sort(list.begin(), list.end());
out.open("subsection_0_" + to_string(filenum) + ".txt");
for (auto val : list)out << to_string(val) << endl;//写入数据
out.close();
list.clear();
filenum++;
}
in.close();
return filenum;
}
//败者树相关
int Adjust(array<int, merge_K - 1> &lossertree, array<vector<int>::iterator, merge_K + 1> inputbfIterators, int index) {
int winner = index;
index = index + merge_K - 1;
while (index > 0) {
if (*(inputbfIterators[winner]) > *(inputbfIterators[lossertree[(index - 1) / 2]])) {
int temp = winner;
winner = lossertree[(index - 1) / 2];
lossertree[(index - 1) / 2] = temp;
}
index = (index - 1) / 2;
}
return winner;
}
int CreateLosserTree(array<int, merge_K - 1> &lossertree, array<vector<int>::iterator, merge_K + 1> inputbfIterators) {
int winner = 0;
for (int i = lossertree.size() - 1; i >= 0; i--) lossertree[i] = merge_K;
for (int i = 0; i < merge_K; i++) winner = Adjust(lossertree, inputbfIterators, i);
return winner;
}
//K路平衡归并
void K_Merge_Sort(string filepath) {
int filenum = InnerSort(filepath);
const int buffersSize = BuffSize / (merge_K + 1);//merge_K个读入数据的buffer,1个写出数据的buffer
array<vector<int>, merge_K> inputBuffer;
vector<int> minval;
minval.push_back(-1);
array<vector<int>::iterator, merge_K + 1> inputbfIterators;
inputbfIterators[merge_K] = minval.begin();
vector<int> outputBuffer;
int mergenum = 0;
string temp;
while (filenum > 1) {
cout << "第" << mergenum + 1 << "轮归并" << endl;
for (int i = 0; i < (filenum + merge_K - 1) / merge_K; i++) {
array<ifstream, merge_K> reader;
const int num_in_merge = ((i + 1) * merge_K < filenum) ? merge_K : (filenum % merge_K);//当前同时进行归并的段数
for (int j = 0; j < merge_K; j++) {
if (j < num_in_merge) {
reader[j].open("subsection_" + to_string(mergenum) + "_" + to_string(i * merge_K + j) + ".txt");
inputBuffer[j].clear();
for (int k = 0; k < buffersSize; k++) {
if (reader[j] >> temp)inputBuffer[j].push_back(stoi(temp));
else break;
}
} else {
inputBuffer[j].push_back(0x3f3f3f3f);
}
inputbfIterators[j] = inputBuffer[j].begin();
}
ofstream writer("subsection_" + to_string(mergenum + 1) + "_" + to_string(i) + ".txt");
outputBuffer.clear();
array<int, merge_K - 1> lossertree;
int winner = CreateLosserTree(lossertree, inputbfIterators);
int value = 0;
while (true) {
for (int j = 0; j < num_in_merge; j++) {
if (inputbfIterators[j] == inputBuffer[j].end()) {
inputBuffer[j].clear();
for (int k = 0; k < buffersSize; k++) {
if (reader[j] >> temp)inputBuffer[j].push_back(stoi(temp));
else {
inputBuffer[j].push_back(0x3f3f3f3f);
break;
}
}
inputbfIterators[j] = inputBuffer[j].begin();
}
}
if (outputBuffer.size() >= buffersSize) {
for (auto item : outputBuffer)writer << to_string(item) << endl;
value += outputBuffer.size();
outputBuffer.clear();
}
winner = Adjust(lossertree, inputbfIterators, winner);
if (*(inputbfIterators[winner]) != 0x3f3f3f3f)outputBuffer.push_back(*(inputbfIterators[winner]++));
else break;
}
if (outputBuffer.size() > 0) {
for (auto item : outputBuffer)writer << to_string(item);
value += outputBuffer.size();
}
cout << "文件 " << "subsection_" + to_string(mergenum + 1) + "_" + to_string(i) + ".txt 归并完毕 " << "数据数量"
<< value << endl;
}
filenum = (filenum + merge_K - 1) / merge_K;
mergenum++;
}
}
int main() {
CreateFile();
K_Merge_Sort("test.txt");
}
运行结果
1.5.16 置换—选择排序
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstring>
#include <iostream>
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
#define MAXKEY INT_MAX
#define RUNEND_SYMBOL INT_MAX
#define w 6 /* 内存工作区可容纳的记录个数 */
#define M 10 /* 设输出M个数据换行 */
#define N 24 /* 设大文件有N个数据 */
using namespace std;
typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
typedef int LoserTree[w]; /* 败者树是完全二叉树且不含叶子,可采用顺序存储结构 */
typedef struct {
RedType rec; /* 记录 */
KeyType key; /* 从记录中抽取的关键字 */
int rnum; /* 所属归并段的段号 */
} RedNode, WorkArea[w]; /* 内存工作区,容量为w */
void Select_MiniMax(LoserTree ls, WorkArea wa, int q) { /* 从wa[q]起到败者树的根比较选择MINIMAX记录,并由q指示它所在的归并段 */
int p, s, t;
for (t = (w + q) / 2, p = ls[t]; t > 0; t = t / 2, p = ls[t])
if (wa[p].rnum < wa[q].rnum || wa[p].rnum == wa[q].rnum && wa[p].key < wa[q].key) {
s = q;
q = ls[t]; /* q指示新的胜利者 */
ls[t] = s;
}
ls[0] = q;
}
void Construct_Loser(LoserTree ls, WorkArea wa, FILE *fi) { /* 输入w个记录到内存工作区wa,建得败者树ls,选出关键字最小的记录并由s指示 */
/* 其在wa中的位置。*/
int i;
for (i = 0; i < w; ++i)
wa[i].rnum = wa[i].key = ls[i] = 0; /* 工作区初始化 */
for (i = w - 1; i >= 0; --i) {
fread(&wa[i].rec, sizeof(RedType), 1, fi); /* 输入一个记录 */
wa[i].key = wa[i].rec.key; /* 提取关键字 */
wa[i].rnum = 1; /* 其段号为"1" */
Select_MiniMax(ls, wa, i); /* 调整败者 */
}
}
void get_run(LoserTree ls, WorkArea wa, int rc, int *rmax, FILE *fi, FILE *fo) { /* 求得一个初始归并段,fi为输入文件指针,fo为输出文件指针。*/
int q;
KeyType minimax;
while (wa[ls[0]].rnum == rc) /* 选得的MINIMAX记录属当前段时 */
{
q = ls[0]; /* q指示MINIMAX记录在wa中的位置 */
minimax = wa[q].key;
fwrite(&wa[q].rec, sizeof(RedType), 1, fo); /* 将刚选得的MINIMAX记录写入输出文件 */
fread(&wa[q].rec, sizeof(RedType), 1, fi); /* 从输入文件读入下一记录(改) */
if (feof(fi)) { /* 输入文件结束,虚设记录(属"rmax+1"段) */
wa[q].rnum = *rmax + 1;
wa[q].key = MAXKEY;
} else { /* 输入文件非空时 */
wa[q].key = wa[q].rec.key; /* 提取关键字 */
if (wa[q].key < minimax) { /* 新读入的记录属下一段 */
*rmax = rc + 1;
wa[q].rnum = *rmax;
} else /* 新读入的记录属当前段 */
wa[q].rnum = rc;
}
Select_MiniMax(ls, wa, q); /* 选择新的MINIMAX记录 */
}
}
void Replace_Selection(LoserTree ls, WorkArea wa, FILE *fi, FILE *fo) { /* 在败者树ls和内存工作区wa上用置换-选择排序求初始归并段,fi为输入文件 */
/* (只读文件)指针,fo为输出文件(只写文件)指针,两个文件均已打开。*/
int rc, rmax;
RedType j;
j.key = RUNEND_SYMBOL;
Construct_Loser(ls, wa, fi); /* 初建败者树 */
rc = rmax = 1; /* rc指示当前生成的初始归并段的段号,rmax指示wa中关键字所属初始归并段的最大段号 */
while (rc <= rmax) /* "rc=rmax+1"标志输入文件的置换-选择排序已完成 */
{
get_run(ls, wa, rc, &rmax, fi, fo); /* 求得一个初始归并段 */
j.otherinfo = rc;
fwrite(&j, sizeof(RedType), 1, fo); /* 将段结束标志写入输出文件 */
rc = wa[ls[0]].rnum; /* 设置下一段的段号 */
}
}
void print(RedType t) {
printf("(%d,%d)", t.key, t.otherinfo);
}
int atoi(char s[]) {
int i, n, sign;
for (i = 0; isspace(s[i]); i++)//跳过空白符;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == ' -')//跳过符号
i++;
for (n = 0; isdigit(s[i]); i++)
n = 10 * n + (s[i] - '0');//将数字字符转换成整形数字
return sign * n;
}
void itoa(int n, char s[]) {
int i, j, sign;
if ((sign = n) < 0)//记录符号
n = -n;//使n成为正数
i = 0;
do {
s[i++] = n % 10 + '0';//取下一个数字
} while ((n /= 10) > 0);//删除该数字
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
for (j = i; j >= 0; j--)//生成的数字是逆序的,所以要逆序输出
printf("%c", s[j]);
}
int main() {
RedType b, a[N] = {{51, 1},
{49, 2},
{39, 3},
{46, 4},
{38, 5},
{29, 6},
{14, 7},
{61, 8},
{15, 9},
{30, 10},
{1, 11},
{48, 12},
{52, 13},
{3, 14},
{63, 15},
{27, 16},
{4, 17},
{13, 18},
{89, 19},
{24, 20},
{46, 21},
{58, 22},
{33, 23},
{76, 24}};
FILE *fi, *fo;
LoserTree ls;
WorkArea wa;
int i, k, j = RUNEND_SYMBOL;
char s[3], fname[4];
fo = fopen("ori", "wb"); /* 以写的方式打开大文件ori */
fwrite(a, sizeof(RedType), N, fo); /* 将数组a写入大文件ori */
fclose(fo);
fi = fopen("ori", "rb"); /* 以读的方式重新打开大文件ori */
printf("大文件的记录为:\n");
for (i = 1; i <= N; i++) {
fread(&b, sizeof(RedType), 1, fi); /* 依次将大文件ori的数据读入b */
print(b); /* 输出b的内容 */
if (i % M == 0)
printf("\n");
}
printf("\n");
rewind(fi); /* 使fi的指针重新返回大文件ori的起始位置,以便重新读入内存,产生有序的子文件 */
fo = fopen("out", "wb"); /* 以写的方式打开初始归并段文件out */
Replace_Selection(ls, wa, fi, fo); /* 用置换-选择排序求初始归并段 */
fclose(fo);
fclose(fi);
fi = fopen("out", "rb"); /* 以读的方式重新打开初始归并段文件out */
printf("初始归并段文件的记录为:\n");
i = 1;
do {
k = fread(&b, sizeof(RedType), 1, fi); /* 依次将大文件out的数据读入b */
if (k == 1) {
print(b); /* 输出b的内容 */
if (i++ % M == 0)
printf("\n");
}
} while (k == 1);
printf("\n");
rewind(fi); /* 使fi的指针重新返回大文件ori的起始位置,以便重新读入内存,产生有序的子文件 */
k = 0;
while (!feof(fi)) /* 按段输出初始归并段文件out */
{
itoa(k, s); /* 依次生成文件名f0,f1,… */
strcpy(fname, "f");
strcat(fname, s);
fo = fopen(fname, "wb"); /* 依次以写的方式打开文件f0,f1,… */
do {
i = fread(&b, sizeof(RedType), 1, fi);
if (i == 1) /* fread()调用成功 */
{
fwrite(&b, sizeof(RedType), 1, fo); /* 将b写入文件f0,f1,… */
if (b.key == j) /* 1个归并段结束 */
{
k++;
fclose(fo);
break;
}
}
} while (i == 1);
};
fclose(fi);
printf("共产生%d个初始归并段文件\n", k);
}
运行结果
1.5.17 文件的归并
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include <cstring>
typedef int InfoType; /* 定义其它数据项的类型 */
#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
#define MINKEY INT_MIN
#define MAXKEY INT_MAX
#define k 3 /* k路归并 */
#define M 10 /* 设输出M个数据换行 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct {
KeyType key; /* 关键字项 */
InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
} RedType; /* 记录类型 */
typedef struct {
RedType r[MAXSIZE + 1]; /* r[0]闲置或用作哨兵单元 */
int length; /* 顺序表长度 */
} SqList; /* 顺序表类型 */
FILE *fp[k + 1]; /* k+1个文件指针(fp[k]为大文件指针),全局变量 */
typedef int LoserTree[k]; /* 败者树是完全二叉树且不含叶子,可采用顺序存储结构 */
typedef RedType ExNode, External[k + 1]; /* 外结点,有改变 */
External b; /* 全局变量 */
void input(int i, KeyType *a) { /* 从第i个文件(第i个归并段)读入该段当前第1个记录的关键字到外结点 */
fread(a, sizeof(KeyType), 1, fp[i]);
}
void output(int i) { /* 将第i个文件(第i个归并段)中当前的记录写至输出归并段 */
ExNode a;
a.key = b[i].key; /* 当前记录的关键字已读到b[i].key中 */
fread(&a.otherinfo, sizeof(InfoType), 1, fp[i]);
fwrite(&a, sizeof(ExNode), 1, fp[k]);
}
void Adjust(LoserTree ls, int s) { /* 沿从叶子结点b[s]到根结点ls[0]的路径调整败者树。算法11.2 */
int i, t;
t = (s + k) / 2; /* ls[t]是b[s]的双亲结点 */
while (t > 0) {
if (b[s].key > b[ls[t]].key) {
i = s;
s = ls[t]; /* s指示新的胜者 */
ls[t] = i;
}
t = t / 2;
}
ls[0] = s;
}
void CreateLoserTree(LoserTree ls) { /* 已知b[0]到b[k-1]为完全二叉树ls的叶子结点,存有k个关键字,沿从叶子 */
/* 到根的k条路径将ls调整成为败者树。算法11.3 */
int i;
b[k].key = MINKEY;
for (i = 0; i < k; ++i)
ls[i] = k; /* 设置ls中"败者"的初值 */
for (i = k - 1; i >= 0; --i) /* 依次从b[k-1],b[k-2],…,b[0]出发调整败者 */
Adjust(ls, i);
}
void K_Merge(LoserTree ls, External b) { /* 利用败者树ls将编号从0到k-1的k个输入归并段中的记录归并到输出归并段。 */
/* b[0]至b[k-1]为败者树上的k个叶子结点,分别存放k个输入归并段中当前 */
/* 记录的关键字。算法11.1 */
int i, q;
for (i = 0; i < k; ++i) /* 分别从k个输入归并段读人该段当前第一个记录的关键字到外结点 */
input(i, &b[i].key);
CreateLoserTree(ls); /* 建败者树ls,选得最小关键字为b[ls[0]].key */
while (b[ls[0]].key != MAXKEY) {
q = ls[0]; /* q指示当前最小关键字所在归并段 */
output(q); /* 将编号为q的归并段中当前(关键字为b[q].key)的记录写至输出归并段 */
input(q, &b[q].key); /* 从编号为q的输入归并段中读人下一个记录的关键字 */
Adjust(ls, q); /* 调整败者树,选择新的最小关键字 */
}
output(ls[0]); /* 将含最大关键字MAXKEY的记录写至输出归并段 */
}
void print(RedType t) {
printf("(%d,%d)", t.key, t.otherinfo);
}
void itoa(int n, char s[]) {
int i, j, sign;
if ((sign = n) < 0)//记录符号
n = -n;//使n成为正数
i = 0;
do {
s[i++] = n % 10 + '0';//取下一个数字
} while ((n /= 10) > 0);//删除该数字
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
for (j = i; j >= 0; j--)//生成的数字是逆序的,所以要逆序输出
printf("%c", s[j]);
}
int main() {
RedType r;
int i, j;
char fname[k][4], fout[5] = "out", s[3];
LoserTree ls;
for (i = 0; i < k; i++) { /* 依次打开f0,f1,f2,…,k个文件 */
itoa(i, s); /* 生成k个文件名f0,f1,f2,… */
strcpy(fname[i], "f");
strcat(fname[i], s);
fp[i] = fopen(fname[i], "rb"); /* 以读的方式打开文件f0,f1,… */
printf("有序子文件f%d的记录为:\n", i);
do {
j = fread(&r, sizeof(RedType), 1, fp[i]); /* 依次将f0,f1,…的数据读入r */
if (j == 1)
print(r); /* 输出r的内容 */
} while (j == 1);
printf("\n");
rewind(fp[i]); /* 使fp[i]的指针重新返回f0,f1,…的起始位置,以便重新读入内存 */
}
fp[k] = fopen(fout, "wb"); /* 以写的方式打开大文件fout */
K_Merge(ls, b); /* 利用败者树ls将k个输入归并段中的记录归并到输出归并段,即大文件fout */
for (i = 0; i <= k; i++)
fclose(fp[i]); /* 关闭文件f0,f1,…和文件fout */
fp[k] = fopen(fout, "rb"); /* 以读的方式重新打开大文件fout验证排序 */
printf("排序后的大文件的记录为:\n");
i = 1;
do {
j = fread(&r, sizeof(RedType), 1, fp[k]); /* 将fout的数据读入r */
if (j == 1)
print(r); /* 输出r的内容 */
if (i++ % M == 0)
printf("\n"); /* 换行 */
} while (j == 1);
printf("\n");
fclose(fp[k]); /* 关闭大文件fout */
return 0;
}
运行结果
Talk is cheap. Show me the code