[数据结构]排序相关算法

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;
}

运行结果

posted @ 2019-11-14 11:15  Xu_Lin  阅读(261)  评论(0编辑  收藏  举报