数据结构-排序

插入排序 Insert Sort

算法思想:将数据按照一定的顺序一个一个的插入到有序的表中,最终得到的序列就是已经排序好的数据。
代码实现:

void InsertSort(int a[], int n)
{
    for(int i= 1; i<n; i++){
        if(a[i] < a[i-1]){//若第 i 个元素大于 i-1 元素则直接插入;反之,需要找到适当的插入位置后在插入。
            int j= i-1;
            int x = a[i];
            while(j>-1 && x < a[j]){  //采用顺序查找方式找到插入的位置,在查找的同时,将数组中的元素进行后移操作,给插入元素腾出空间
                a[j+1] = a[j];
                j--;
            }
            a[j+1] = x;      //插入到正确位置
        }
        print(a,n,i);//打印每次排序后的结果
    }
}

希尔排序 Shell Sort

算法思想:将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
代码实现:

void ShellShort(int a[],int n){
    int d,i,j;  //d为增量
    for (d=n/2 ;d>=1 ;d=d/2) {
        for (i=d;i<n;++i){
            if (a[i]<a[i-d]){
                int x=a[i];
                for (j=i-d;j>=0&&x<=a[j];j-=d)
                    a[j+d]=a[j];
                a[j+d]=x;
            }
        }
        print(a,n,d);//打印每次排序后的结果
    }
}

冒泡排序 Bubble Sort

算法思想:将无序表中的所有记录,通过两两比较关键字,得出升序序列或者降序序列。
代码实现:

void swap(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void bubbleSort(int a[],int n){
    int key;
    //有多少记录,就需要多少次冒泡,当比较过程,所有记录都按照升序排列时,排序结束
    for (int i = 0; i < n; i++){
        key=0;//每次开始冒泡前,初始化 key 值为 0
        //每次起泡从下标为 0 开始,到 n-i 结束
        for (int j = 0; j+1<n-i; j++){
            if (a[j] > a[j+1]){
                key=1;
                swap(&a[j], &a[j+1]);
            }
        }
        //如果 key 值为 0,表明表中记录排序完成
        if (key==0) {
            break;
        }
        print(a,n,i);//打印每次排序后的结果
    }
}

快速排序 QSort

算法思想:通过一次排序将整个无序表分成相互独立的两部分,其中一部分中的数据都比另一部分中包含的数据的值小,然后继续沿用此方法分别对两部分进行同样的操作,直到每一个小部分不可再分,所得到的整个序列就成为了有序序列。
代码实现:

int Partition(int a[], int low, int high){
    int x=a[low];
    while(low<high){
        while(low<high&&a[high]>=x) --high; //比x小的元素移动到x左面
        a[low]=a[high];
        while(low<high&&a[low]<=x) ++low;   //比x大的元素移动到x右面
        a[high]=a[low];
    }
    a[low]=x;
    print(a,7);//打印每次排序后的结果
    return low;
}
void QuickSort(int a[],int low,int high){
    if (low<high){
        int pivotpos=Partition(a,low,high);
        QuickSort(a,low,pivotpos-1); //左区
        QuickSort(a,pivotpos+1,high); //右区
    }
}

选择排序 SelectSort

算法思想:对于具有 n 个记录的无序表遍历 n-1 次,第 i 次从无序表中第 i 个记录开始,找出后序关键字中最小的记录,然后放置在第 i 的位置上。
代码实现:

void swap(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void SelectSort(int a[],int n){
    for (int i = 0; i < n-1; ++i) {
        int min=i;
        for (int j = i+1; j <n ; ++j) {  //找到最小元素
            if (a[j]<a[min]) min=j;
            if (min!=j)
                swap(&a[i],&a[min]); //交换元素
        }
        print(a,n,i+1);//打印每次排序后的结果
    }
}

堆排序

算法思想:在含有 n 个元素的序列中,如果序列中的元素满足下面其中一种关系时,此序列可以称之为堆。

  • ki ≤ k2i 且 ki ≤ k2i+1(在 n 个记录的范围内,第 i 个关键字的值小于第 2i 个关键字,同时也小于第 2i+1 个关键字)
  • ki ≥ k2i 且 ki ≥ k2i+1(在 n 个记录的范围内,第 i 个关键字的值大于第 2i 个关键字,同时也大于第 2i+1 个关键字)
    代码实现:http://c.biancheng.net/view/3448.html
#include <stdio.h>
#include <stdlib.h>
#define MAX 9
//单个记录的结构体
typedef struct {
    int key;
}SqNote;
//记录表的结构体
typedef struct {
    SqNote r[MAX];
    int length;
}SqList;
//将以 r[s]为根结点的子树构成堆,堆中每个根结点的值都比其孩子结点的值大
void HeapAdjust(SqList * H,int s,int m){
    SqNote rc=H->r[s];//先对操作位置上的结点数据进行保存,放置后序移动元素丢失。
    //对于第 s 个结点,筛选一直到叶子结点结束
    for (int j=2*s; j<=m; j*=2) {
        //找到值最大的孩子结点
        if (j+1<m && (H->r[j].key<H->r[j+1].key)) {
            j++;
        }
        //如果当前结点比最大的孩子结点的值还大,则不需要对此结点进行筛选,直接略过
        if (!(rc.key<H->r[j].key)) {
            break;
        }
        //如果当前结点的值比孩子结点中最大的值小,则将最大的值移至该结点,由于 rc 记录着该结点的值,所以该结点的值不会丢失
        H->r[s]=H->r[j];
        s=j;//s相当于指针的作用,指向其孩子结点,继续进行筛选
    }
    H->r[s]=rc;//最终需将rc的值添加到正确的位置
}
//交换两个记录的位置
void swap(SqNote *a,SqNote *b){
    int key=a->key;
    a->key=b->key;
    b->key=key;
}
void HeapSort(SqList *H){
    //构建堆的过程
    for (int i=H->length/2; i>0; i--) {
        //对于有孩子结点的根结点进行筛选
        HeapAdjust(H, i, H->length);
    }
    //通过不断地筛选出最大值,同时不断地进行筛选剩余元素
    for (int i=H->length; i>1; i--) {
        //交换过程,即为将选出的最大值进行保存大表的最后,同时用最后位置上的元素进行替换,为下一次筛选做准备
        swap(&(H->r[1]), &(H->r[i]));
        //进行筛选次最大值的工作
        HeapAdjust(H, 1, i-1);
    }
}
int main() {
    SqList * L=(SqList*)malloc(sizeof(SqList));
    L->length=8;
    L->r[1].key=49;
    L->r[2].key=38;
    L->r[3].key=65;
    L->r[4].key=97;
    L->r[5].key=76;
    L->r[6].key=13;
    L->r[7].key=27;
    L->r[8].key=49;
    HeapSort(L);
    for (int i=1; i<=L->length; i++) {
        printf("%d ",L->r[i].key);
    }
    return 0;
}

归并排序 Merge

算法思想:先将所有的记录完全分开,然后两两合并,在合并的过程中将其排好序,最终能够得到一个完整的有序表。
实现代码:

int *B=(int *)malloc(sizeof(int)); //辅助数组
void Merge(int a[],int low,int mid,int high){
    int i,j,k;
    for ( k = low; k <=high ; ++k) {
        B[k]=a[k];  //B数组复制数组a
    }
    for ( i = low, j=mid+1,k=i; i <=mid&&j<=high ; k++) {
        if (B[i]<=B[j])
            a[k]=B[i++];
        else
            a[k]=B[j++];
    }
    while(i<=mid) a[k++]=B[i++];
    while(j<=high) a[k++]=B[j++];
}
void MergeSort(int a[],int low,int high){
    if (low<high){
        int mid=(low+high)/2;
        MergeSort(a,low,mid);
        MergeSort(a,mid+1,high);
        Merge(a,low,mid,high);
    }
}

基数排序

算法思想:对关键字进行“分配”与“收集”两种操作即可完成。
代码实现:http://c.biancheng.net/view/vip_3450.html

#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM_OF_KEY 8//构成关键字的组成部分的最大个数
#define RADIX 10        //基数,例如关键字是数字,无疑由0~9组成,基数就是10;如果关键字是字符串(字母组成),基数就是 26
#define MAX_SPACE 10000
//静态链表的结点结构
typedef struct{
    int data;//存储的关键字
    int keys[MAX_NUM_OF_KEY];//存储关键字的数组(此时是一位一位的存储在数组中)
    int next;//做指针用,用于是静态链表,所以每个结点中存储着下一个结点所在数组中的位置下标
}SLCell;
//静态链表结构
typedef struct{
    SLCell r[MAX_SPACE];//静态链表的可利用空间,其中r[0]为头结点
    int keynum;//当前所有的关键字中最大的关键字所包含的位数,例如最大关键字是百,说明所有keynum=3
    int recnum;//静态链表的当前长度
} SLList;
typedef int  ArrType[RADIX];//指针数组,用于记录各子序列的首尾位置
//排序的分配算法,i表示按照分配的位次(是个位,十位还是百位),f表示各子序列中第一个记录和最后一个记录的位置
void Distribute(SLCell *r,int i,ArrType f,ArrType e){
    //初始化指针数组
    for (int j=0; j<RADIX; j++) {
        f[j]=0;
    }
    //遍历各个关键字
    for (int p=r[0].next; p; p=r[p].next) {
        int j=r[p].keys[i];//取出每个关键字的第 i 位,由于采用的是最低位优先法,所以,例如,第 1 位指的就是每个关键字的个位
        if (!f[j]) {//如果只想该位数字的指针不存在,说明这是第一个关键字,直接记录该关键字的位置即可
            f[j]=p;
        }else{//如果存在,说明之前已经有同该关键字相同位的记录,所以需要将其进行连接,将最后一个相同的关键字的next指针指向该关键字所在的位置,同时最后移动尾指针的位置。
            r[e[j]].next=p;
        }
        e[j]=p;//移动尾指针的位置
    }
}
//基数排序的收集算法,即重新设置链表中各结点的尾指针
void Collect(SLCell *r,int i,ArrType f,ArrType e){
    int j;
    //从 0 开始遍历,查找头指针不为空的情况,为空表明该位没有该类型的关键字
    for (j=0;!f[j]; j++);
    r[0].next=f[j];//重新设置头结点
    int t=e[j];//找到尾指针的位置
    while (j<RADIX) {
        for (j++; j<RADIX; j++) {
            if (f[j]) {
                r[t].next=f[j];//重新连接下一个位次的首个关键字
                t=e[j];//t代表下一个位次的尾指针所在的位置
            }
        }
    }
    r[t].next=0;//0表示链表结束
}
void RadixSort(SLList *L){
    ArrType f,e;
    //根据记录中所包含的关键字的最大位数,一位一位的进行分配与收集
    for (int i=0; i<L->keynum; i++) {
        //秉着先分配后收集的顺序
        Distribute(L->r, i, f, e);
        Collect(L->r, i, f, e);
    }
}
//创建静态链表
void creatList(SLList * L){
    int key,i=1,j;
    scanf("%d",&key);
    while (key!=-1) {
        L->r[i].data=key;
        for (j=0; j<=L->keynum; j++) {
            L->r[i].keys[j]=key%10;
            key/=10;
        }
        L->r[i-1].next=i;
        i++;
        scanf("%d",&key);
    }
    L->recnum=i-1;
    L->r[L->recnum].next=0;
}
//输出静态链表
void print(SLList*L){
    for (int p=L->r[0].next; p; p=L->r[p].next) {
        printf("%d ",L->r[p].data);
    }
    printf("\n");
}
int main(int argc, const char * argv[]) {
    SLList *L=(SLList*)malloc(sizeof(SLList));
    L->keynum=3;
    L->recnum=0;
    creatList(L);//创建静态链表
    printf("排序前:");
    print(L);
   
    RadixSort(L);//对静态链表中的记录进行基数排序
   
    printf("排序后:");
    print(L);
    return 0;
}

内部排序算法的优劣

排序方法平均时间最坏情况存储空间稳定性
简单排序O(n^2)O(n^2)O(1)----
快速排序O(logn)O(n^2)O(logn)不稳定
堆排序O(logn)O(logn)O(1)----
归并排序O(logn)O(logn)O(n)稳定
基数排序O(d*n)O(d*n)O(d*n)稳定
  • 上表中的简单排序包含出希尔排序之外的所有插入排序,冒泡排序和简单选择排序。
  • 简单排序中,无序表记录n较少直接插入为最佳。
  • 所有的排序算法中单就平均时间性能上分析,快速排序算法最佳,其运行所需的时间最短,但其在最坏的情况下的时间性能不如堆排序和归并排序。
  • 堆排序和归并排序相比较,当无序表中记录的数量 n 较大时,归并排序所需时间比堆排序短,但是在运行过程中所需的辅助存储空间更多(以空间换时间)。
  • 从基数排序的时间复杂度上分析,该算法最适用于对 n 值很大但是关键字较小的序列进行排序。
  • 选择排序、快速排序和希尔排序都不是稳定的排序算法;而冒泡排序、插入排序、归并排序和基数排序都是稳定的排序算法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值