第十章 内部排序

插入排序 :直接插入 折半插入 2-路插入 表插入排序 希尔排序
#include<stdio.h>

//关键字比较
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )
#define LQ(a,b) ( (a)> (b) )

#define MAXSIZE 20
typedef int KeyType;
typedef struct {

}InfoType;
typedef struct {
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项
}RedType;
typedef struct {
    RedType r[MAXSIZE];     //r[0]闲置
    int length;
}SqList;                    //顺序表类型

//-------------------直接插入排序-------------------
/*
 * 对于每个关键字,从后往前搜索
 * r[0]设置监视哨避免越界
 * 效率:
 *  空间上:一个辅助空间
 *  时间上:
 *      总共n-1趟排序
 *      最好:待排序序列中记录按关键字正序排列,关键字比较次数n-1
 *      最坏:待排序序列中记录按关键字逆序排列,关键字比较次数2+3+...+n=(n+2)(n-1)/2,关键字移动次数3+...+(n+1)=(n+4)(n-1)/2
 *      平均次数:n*n/4
 *      复杂度:O(n*n)
*/

void InsertSort(SqList &L){
    int i, j;
    for(i=2;i<=L.length;i++)
        if(LT(L.r[i].key, L.r[i-1].key)){
            L.r[0] = L.r[i];
            L.r[i] = L.r[i-1];
            for(j=i-2; LT(L.r[0].key, L.r[j].key); --j)
                L.r[j+1] = L.r[j];
            L.r[j+1] = L.r[0];
        }
}

//-------------------折半插入排序-------------------
/*
 * 在直接插入的基础上,减少“比较”“移动”这两种操作的次数
 * 很明显,减少了一半的关键字的比较次数,而移动次数没有减少
 * O(n*n)
 * 稳定
 */
void BInsertSort(SqList &L){
    int i, j, low, high, mid;
    for(i=2;i<=L.length;i++){
        L.r[0] = L.r[i];
        low = 1; high = i-1;
        while (low<=high){
            mid = (low +high)/2;
            if(LT(L.r[0].key, L.r[mid].key)) high -= 1;
            else low += 1;
        }
        for(j=i-1;j>=high+1;j--) L.r[j+1] = L.r[j];
        L.r[high+1] = L.r[0];
    }
}

//-------------------2-路插入排序-------------------
/*
 * 在折半插入的基础上,减少移动次数
 * 但是需要n+2个辅助空间
 * 步骤:
 * 不稳定
 * 最好:对于逆序或正序,每次只比较两次,共2*(n-1)次
 * 最坏:除去第一个剩下的逆序,比较次数2+...+n=(n-1)(n+2)/2,移动次数1+2+3+...+(n-1)=n(n-1)/2
 */
void TInsertSort(SqList &L){
    int d[L.length+2], first=L.length+1, final=1, m ,n;
    d[first] = d[final] = L.r[1].key;
    for(int i=2;i<=L.length;i++){
        if (LT(L.r[i].key,d[1])){
            n = first-1;
            while (LQ(L.r[i].key, d[first])){
                d[first-1] = d[first];
                first++;
            }
            d[first-1] = L.r[i].key;
            first = n;
        }else{
            m = final+1;
            while ( LT(L.r[i].key, d[final])){
                d[final+1] = d[final];
                final--;
            }
            d[final+1] = L.r[i].key;
            final = m;
        }
    }
    int k=1;
    for(int i=first;i<=L.length;i++) L.r[k++].key = d[i];
    for(int i=1;i<=final;i++) L.r[k++].key = d[i];
}

//-------------------表插入排序-------------------
#define SIZE 100        //静态链表容量
#define MAXINT 9999
typedef struct {
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项
}RcdType;
typedef struct {
    RcdType rc;         //记录项
    int next;           //指针项
}SLNode;        //表节点类型
typedef struct {
    SLNode r[SIZE];     //0号单元为表头
    int length;         //链表当前长度
}SLinkListType;

/*
 * 表插入排序的基本操作仍然是将一个记录插入到已经排好序的有序表中
 * 和直接插入排序相比,不同之处仅是以修改2n次指针代替移动记录。排序过程中的关键字比较次数相同
 * 复杂度O(n*n)
 *
 * 表插入得到的结果是一个有序链表,只能顺序查找不能随机查找
 * 所以进行重新排列
 */

//表插入
void SInsertSort(SLinkListType &SL){
    SL.r[0].rc.key = MAXINT; SL.r[0].next = 1; SL.r[1].next = 0;
    SL.length = 8;
    printf("Init(%d):\n",SL.length);
    for(int i=1;i<=SL.length;i++) scanf("%d",&SL.r[i].rc.key);

    for(int i=2;i<=SL.length;i++){
        int pos = 0;
        while (SL.r[SL.r[pos].next].rc.key!=MAXINT && SL.r[i].rc.key>=SL.r[SL.r[pos].next].rc.key) pos = SL.r[pos].next;
        SL.r[i].next = SL.r[pos].next;
        SL.r[pos].next = i;
    }
}
//输出表
void Print(SLinkListType SL){
    int p = SL.r[0].next;
    while (SL.r[p].rc.key!=MAXINT){
        printf("%d ",SL.r[p].rc.key);
        p = SL.r[p].next;
    }
    printf("\n");
}
//重新排列记录 使得静态链表中的记录按非递减排序
void Arrange(SLinkListType &SL){
    int p = SL.r[0].next, q;
    for(int i=1;i<SL.length;i++){
        while (p<i) p = SL.r[p].next;
        q = SL.r[p].next;
        if(p!=i){
            int temp = SL.r[i].rc.key;
            SL.r[i].rc.key = SL.r[p].rc.key;
            SL.r[p].rc.key = temp;
            SL.r[p].next = SL.r[i].next;
            SL.r[i].next = p;
        }
        p = q;
    }
}
void Print_(SLinkListType SL){
    for(int i=1;i<=SL.length;i++) printf("%d ",SL.r[i].rc.key);
    printf("\n");
}

//-------------------希尔排序-------------------
/*
 * 也是一种属插入排序类的方法,只是在时间效率上较前几种排序方法有较大该进
 * 设想,若待排序记录序列按关键字“基本有序”,则排序效率会提高
 * 基本思想:
 *      先将整个待排序记录序列分割成若干子序列,分别进行直接插入排序
 *      待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序
 * 子序列的构成,不是简单地“逐段分割”,而是相隔某个“增量”的记录组成一个子序列
 *
 * 分析:当增量序列为 dlta[k]=2^(t-k+1)-1时,希尔排序的时间复杂度为O(n^(3/2))
 * 不稳定
 */
void ShellInsert(SqList &L, int dk){
    //对顺序表作一趟希尔插入排序 前后记录的增量的dk而不是1
    //r[0]只是暂存单元,不是哨兵
    //当j=0时,插入位置已经找到
    int i, j;
    for(i=1+dk; i<=L.length; ++i)
        if(LT(L.r[i].key, L.r[i-dk].key)){
            L.r[0] = L.r[i];
            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 ShellSort(SqList &L, int dlta[], int t){
    //按增量序列dlta[0,,t-1]对顺序表L作希尔排序
    for(int k=0;k<t;k++) ShellInsert(L, dlta[k]);
}

//--------------------------------------
void CinList(SqList &L, int n){
    L.length = n;
    printf("Init(%d):\n",n);
    for(int i=1;i<=n;i++) scanf("%d",&L.r[i].key);
}
void Print(SqList L){
    printf("Out:\n");
    for(int i=1;i<=L.length;i++) printf("%d ",L.r[i].key);
    printf("\n");
}
int main(){
    SqList L;
    CinList(L, 8);  // 49 38 65 97 76 13 27 49
//    InsertSort(L);    //直接插入

//    BInsertSort(L);   //折半插入

//    TInsertSort(L);   //2-路插入
//    Print(L);

//    SLinkListType SL;
//    SInsertSort(SL);    //表插入
//    Print(SL);
//    Arrange(SL);
//    Print_(SL);

    int dlta[] = {9,5,3,2,1};
    ShellSort(L,dlta,5);    //希尔排序
    Print(L);
    return 1;
}
快速排序 :冒泡排序 快速排序
#include<stdio.h>

//关键字比较
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )
#define LQ(a,b) ( (a)> (b) )

#define MAXSIZE 20
typedef int KeyType;
typedef struct {

}InfoType;
typedef struct {
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项
}RedType;
typedef struct {
    RedType r[MAXSIZE];     //r[0]闲置
    int length;
}SqList;                    //顺序表类型

//-------------------起泡排序-------------------
/*
 * O(n*n)
 * 稳定
*/
void BubbleSort(SqList &L){
    for(int i=1;i<L.length;i++)
        for(int j=1;j<=L.length-i;j++)
            if(LQ(L.r[j].key, L.r[j+1].key)){
                int temp = L.r[j].key;
                L.r[j].key = L.r[j+1].key;
                L.r[j+1].key = temp;
            }
}

//-------------------快速排序-------------------
/*
 * O(nln(n))
 */
//一趟快排
int Partition(SqList &L, int low, int high){
    //在顺序表L的[low, high]上作一次快速排序  使得枢纽记录到位,并返回所在位置
    L.r[0] = L.r[low];
    KeyType pivoteky = L.r[low].key;
    while(low<high){
        while ( L.r[high].key>=pivoteky && low<high  ) high--;
        L.r[low] = L.r[high];
        while ( L.r[low].key<=pivoteky && low<high  ) low++;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[0];
    return low;
}
void QSort(SqList &L, int low, int high){
    if(low<high){
        int pivotloc = Partition(L,low,high);
        QSort(L,low,pivotloc-1);
        QSort(L,pivotloc+1,high);
    }
}
void QuickSort(SqList &L){
    QSort(L,1,L.length);
}

//--------------------------------------
void CinList(SqList &L, int n){
    L.length = n;
    printf("Init(%d):\n",n);
    for(int i=1;i<=n;i++) scanf("%d",&L.r[i].key);
}
void Print(SqList L){
    printf("Out:\n");
    for(int i=1;i<=L.length;i++) printf("%d ",L.r[i].key);
    printf("\n");
}
int main(){
    SqList L;
    CinList(L, 8);  // 49 38 65 97 76 13 27 49

//    BubbleSort(L);    //冒泡排序

    QuickSort(L);   //快速排序

    Print(L);
    return 1;
}
选择排序 :简单选择排序 堆排序
#include<stdio.h>

//关键字比较
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )
#define LQ(a,b) ( (a)> (b) )

#define MAXSIZE 20
typedef int KeyType;
typedef struct {

}InfoType;
typedef struct {
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项
}RedType;
typedef struct {
    RedType r[MAXSIZE];     //r[0]闲置
    int length;
}SqList;                    //顺序表类型

//-------------------简单选择排序-------------------
/*
 * 对于第i个记录(1<=i<n), 通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录进行交换
 * 最好:记录移动次数0
 * 最坏:记录移动次数3(n-1)
 * 关键字的比较次数都是(n-1)+...+1=n(n-1)/2
 * 时间复杂度O(n*n)
 * 稳定
 */
int SelectMinKey(SqList L, int i){
    int min = L.r[i].key, pos = i;
    for(int j=i+1;j<=L.length;j++)
        if(L.r[j].key<min){
            min = L.r[j].key;
            pos = j;
        }
    return pos;
}
void SelectSort(SqList &L){
    int i, j;
    RedType temp;
    for(i=1;i<L.length;i++){
        j = SelectMinKey(L, i);
        if(i!=j){
            temp = L.r[i];
            L.r[i] = L.r[j];
            L.r[j] = temp;
        }
    }
}

//-------------------堆排序-------------------
/*
 * 堆排序对于记录较少的文件并不值得使用,对n较大的文件有效
 * 其运行时间主要耗费在建初始堆和调整堆的反复筛选上
 * 时间复杂度O(nlogn)
 * 空间:一个辅助存储空间
 * 不稳定
 */
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 = H.r[s];
    int j;
    for( j=2*s; j<=m; j*=2){    //沿key较大的孩子节点  向下筛选
        if(j<m && LT(H.r[j].key, H.r[j+1].key)) j ++;
        if( !LT(rc.key, H.r[j].key)) break;
        H.r[s] = H.r[j];
        s = j;
    }
    H.r[s] = rc;
}
void HeapSort(HeapType &H){
    //对顺序表进行堆排序
    int i; RedType temp;
    for(i=H.length/2; i>=1; i--) HeapAdjust(H,i,H.length);  //建堆
    for(i=H.length; i>1; --i) {
        temp = H.r[1];
        H.r[1] = H.r[i];
        H.r[i] = temp;
        HeapAdjust(H,1,i-1);
    }
}

//--------------------------------------
void CinList(SqList &L, int n){
    L.length = n;
    printf("Init(%d):\n",n);
    for(int i=1;i<=n;i++) scanf("%d",&L.r[i].key);
}
void Print(SqList L){
    printf("Out:\n");
    for(int i=1;i<=L.length;i++) printf("%d ",L.r[i].key);
    printf("\n");
}
int main(){
    SqList L;
    CinList(L, 8);  // 49 38 65 97 76 13 27 49

//    SelectSort(L);      //简单选择排序

    HeapSort(L);

    Print(L);
    return 1;
}
归并排序
#include<stdio.h>

//关键字比较
#define EQ(a,b) ( (a)==(b) )
#define LT(a,b) ( (a)< (b) )
#define LQ(a,b) ( (a)> (b) )

#define MAXSIZE 20
typedef int KeyType;
typedef struct {

}InfoType;
typedef struct {
    KeyType key;            //关键字项
    InfoType otherinfo;     //其他数据项
}RedType;
typedef struct {
    RedType r[MAXSIZE];     //r[0]闲置
    int length;
}SqList;                    //顺序表类型

//-------------------归并排序-------------------
/*
 * 2-路归并排序
 * 稳定
 * O(nlog2(n))
 * 空间:O(n)开辟一个同样大小的空间来存放归并后的数据
 */

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;
    for(k=i,j=m+1; i<=m&&j<=n; ++k){    //k循环TR i和j分别循环前半部分和后半部分
        if(!LQ(SR[i].key,SR[j].key)) TR[k] = SR[i++];
        else TR[k] = SR[j++];
    }
    while (i<=m) TR[k++] = SR[i++];
    while (j<=n) TR[k++] = SR[j++];
}
void MSort(RedType SR[], RedType TR1[], int s, int t, int N){
    //将SR[s..t]归并为 TR1[s..t]   N是为了传递一个固定的数也就是所有数据的个数,为了构造数组TR2
    int m; RedType TR2[N+1];
    if (s==t) TR1[s] = SR[s];
    else{
        m = (s+t)/2;
        MSort(SR,TR2,s,m,N);
        MSort(SR,TR2,m+1,t,N);
        Merge(TR2, TR1, s, m, t);
    }

}
void MergeSort(SqList &L){
    MSort(L.r, L.r, 1, L.length, L.length);
}

//--------------------------------------
void CinList(SqList &L, int n){
    L.length = n;
    printf("Init(%d):\n",n);
    for(int i=1;i<=n;i++) scanf("%d",&L.r[i].key);
}
void Print(SqList L){
    printf("Out:\n");
    for(int i=1;i<=L.length;i++) printf("%d ",L.r[i].key);
    printf("\n");
}
int main(){
    SqList L;
    CinList(L, 8);  // 49 38 65 97 76 13 27 49

    MergeSort(L);

    Print(L);
    return 1;
}
基数排序
#include<stdio.h>
/*
 * 基数排序,不需要进行关键字之间的比较
 * 借助的是多关键字排序的思想对单逻辑关键字进行排序的方法
 *
 * 链式基数排序
 * 借助“分配”“收集”两种操作对单逻辑关键字进行排序
 *
 * 对于n个记录(每个记录含d个关键字,每个关键字的取值范围尾rd个值)
 * 进行链式基数排序的时间复杂度为O(d(n+rd))
 * 其中,每一趟“分配”的时间复杂度为O(n),每一趟“收集”的时间复杂度为O(rd),整个排序需要进行d趟分配和收集
 * 所需辅助空间为2rd个队列指针
 * 由于使用链表作存储结构,相对于其他顺序存储的排序方法而言,还增加了n个指针域
*/
#define MAX_NUM_OF_KEY 8    //关键字项数的最大值
#define RADIX 10            //关键字基数 此时是十进制
#define MAX_SPACE 10000
typedef int KeyType;
typedef struct {}InfoType;
typedef struct {
    KeyType keys[MAX_NUM_OF_KEY];   //关键字
    InfoType otheritems;            //其他数据项
    int next;
}SLCell;                    //静态链表的节点类型
typedef struct {
    SLCell r[MAX_SPACE];            //静态链表的可利用空间 r[0]为头结点
    int keynum;                     //记录的当前关键字个数
    int recnum;                     //静态链表的当前长度
}SLList;                    //静态链表类型
typedef int ArrType[RADIX];         //指针数组

//-------------------一趟分配-------------------
void Distribute(SLCell *r, int i, ArrType &f, ArrType &e){
    //按照第i个关键字keys[i]建立RADIX个子表  每个子表中记录的keys[i]相同
    //f和e分别指向各子表的第一个和最后一个记录
    int j, p;
    for(j=0; j<RADIX; j++) f[j]=0; //子表初始化为空
    for(p=r[0].next; p; p=r[p].next){
        j = r[p].keys[i];
        if(!f[j]) f[j]=p;
        else r[e[j]].next = p;
        e[j] = p;
    }
}
void Collect(SLCell *r, int i, ArrType f, ArrType e){
    //按照第i个关键字keys[i]自小至大地将f[0..RADIX-1]所指各子表一次链成一个链表
    //e[0..RADIX-1]存放的是各子表的尾指针
    int j, t;
    for(j=0; !f[j]; j++);   //找第一个不空的子表
    r[0].next = f[j];  t = e[j];    //r[0]指向第一个数据,也就是第一个非空子表的第一个数据
    while(j<RADIX-1){
        for(j+=1; j<RADIX-1 && !f[j]; j++);  //找下一个非空子表  防止最后一个子表为空
        if(f[j]) {r[t].next = f[j]; t = e[j];}
    }
    r[t].next = 0;
}
void RadixSort(SLList &L){
    //L是采用静态链表表示的顺序表
    //对L作基数排序  L.r[0]是头结点
    for(int i=0;i<L.recnum;i++) L.r[i].next=i+1;
    L.r[L.recnum].next = 0;

    ArrType f, e;
    for(int i=L.keynum-1; i>=0; i--){
        Distribute(L.r, i, f, e);
        Collect(L.r, i, f, e);
    }
}

//--------------------------------------
void CinList(SLList &L, int n, int k){
    printf("Init(%d):\n",n);
    L.keynum = k;
    L.recnum = n;
    for(int i=1;i<=n;i++)
        for(int j=0;j<k;j++)
            scanf("%d",&L.r[i].keys[j]);
}
void Print(SLList L){
    printf("Out:\n");
    for(int i=0; L.r[i].next!=0;){
        i = L.r[i].next;
        printf("%d%d%d ",L.r[i].keys[0],L.r[i].keys[1],L.r[i].keys[2]);
    }
    printf("\n");
}
int main(){
    SLList L;
    CinList(L, 10, 3);

    RadixSort(L);   //基数排序

    Print(L);
    return 1;
}
/*
2 7 8
1 0 9
0 6 3
9 3 0
5 8 9
1 8 4
5 0 5
2 6 9
0 0 8
0 8 3
 */

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在学C++

2角不嫌多,1角不嫌少

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值