排序(三):归并排序、基数排序

一、排序分析:
7、归并排序
时间复杂度:O(nlog2^n)
空间复杂度:O(n)
算法稳定性:稳定
这里写图片描述

//1、申请新数组,用来保存排序结果
//2、初始start/end1/start2/end2
//3、归并排序:
//  有两个归并段时,start1与start2元素进行比较
//      小于等于,保存start1值同时后移;
//      大于,保存start2值同时后移。
//  注:当任意一个归并段start移至end后时,即另一段剩余元素同时有序;
//      更新start/end1/start2/end2。
//  有一个归并段时,仅将其按顺序保存至brr
//4、更新arr
void Merge(int *arr, int len, int gap)//一次归并排序
{
    //申请新数组
    int *brr = (int *)malloc(sizeof(int) * len);
    assert(brr != NULL);
    //初始start/end1/start2/end2
    int i = 0;
    int start1 = 0;
    int end1 = start1 + gap - 1;
    int start2 = end1 + 1;
    int end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
    //开始归并排序
    while(start2 < len)//有两个归并段
    {
        while(start1 <= end1 && start2 <=end2)
        {
            if(arr[start1] <= arr[start2])//稳定性
            {
                brr[i++] = arr[start1++];
            }
            else
            {
                brr[i++] = arr[start2++];
            }
        }
        while(start1 <= end1)
        {
            brr[i++] = arr[start1++];
        }
        while(start2 <= end2)
        {
            brr[i++] = arr[start2++];
        }
        //更新start/end1/start2/end2
        start1 = end2 + 1;
        end1 = start1 + gap -1;
        start2 = end1 + 1;
        end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
    }
    while(start1 < len)//有一个归并段
    {
        brr[i++] = arr[start1++];
    }
    //更新arr
    for(i = 0; i < len; i++)
    {
        arr[i] = brr[i];
    }
    free(brr);
    brr = NULL;
}
//按 i*2 的步进方式,每次平分为 i*2 个元素的数据段,进行二路排序
void MergeSort(int *arr, int len)//归并排序
{
    for(int i = 1; i < len; i *= 2)
    {
        Merge(arr, len, i);
    }
}

8、基数排序
时间复杂度:O(d(r+n))
空间复杂度:O(rd+n)
算法稳定性:稳定
这里写图片描述

//1、初始十个桶(0-9):单链表建立
//2、确定入桶(与出桶)次数:最大元素位数count
//3、完成每次的入桶与出桶:即figure位的排序
//  得到 figure位数字;
//  按照0-9号桶对应插入;
//  从0-9号桶依次出桶(当前桶全部出完才能开始出下一个桶)。
//4、当完成count次入桶、出桶操作后,即整体有序
void Base(int *arr, int len, int figure)//一次入桶、出桶
{
    //初始十个桶
    Node head[10];
    int i = 0;
    int tmp = 0;
    for(i = 0; i < 10; i++)
    {
        InitList(&head[i]);
    }
    //入桶
    for(i = 0; i < len; i++)
    {
        tmp = GetNum(arr[i], figure);
        Insert(&head[tmp], arr[i]);
    }
    //出桶
    i = 0;
    for(int j = 0; j <10; )
    {
        if(Delet(&head[j], &arr[i]))
        {
            i++;
        }
        else
        {
            j++;
        }
    }
}

void BaseSort(int *arr, int len)//基数排序
{
    int count = GetMaxBit(arr, len);

    for(int i = 0; i <count; i++)
    {
        Base(arr, len, i);
    }
}

二、代码实现

#include<stdio.h>
#include<assert.h>
#include<malloc.h>

//归并排序(二路归并)
//子序列有序->子序列间有序->整体有序


//1、申请新数组,用来保存排序结果
//2、初始start/end1/start2/end2
//3、归并排序:
//  有两个归并段时,start1与start2元素进行比较
//      小于等于,保存start1值同时后移;
//      大于,保存start2值同时后移。
//  注:当任意一个归并段start移至end后时,即另一段剩余元素同时有序;
//      更新start/end1/start2/end2。
//  有一个归并段时,仅将其按顺序保存至brr
//4、更新arr
void Merge(int *arr, int len, int gap)//一次归并排序
{
    //申请新数组
    int *brr = (int *)malloc(sizeof(int) * len);
    assert(brr != NULL);
    //初始start/end1/start2/end2
    int i = 0;
    int start1 = 0;
    int end1 = start1 + gap - 1;
    int start2 = end1 + 1;
    int end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
    //开始归并排序
    while(start2 < len)//有两个归并段
    {
        while(start1 <= end1 && start2 <=end2)
        {
            if(arr[start1] <= arr[start2])//稳定性
            {
                brr[i++] = arr[start1++];
            }
            else
            {
                brr[i++] = arr[start2++];
            }
        }
        while(start1 <= end1)
        {
            brr[i++] = arr[start1++];
        }
        while(start2 <= end2)
        {
            brr[i++] = arr[start2++];
        }
        //更新start/end1/start2/end2
        start1 = end2 + 1;
        end1 = start1 + gap -1;
        start2 = end1 + 1;
        end2 = start2+gap-1 > len-1 ? len-1 : start2+gap-1;
    }
    while(start1 < len)//有一个归并段
    {
        brr[i++] = arr[start1++];
    }
    //更新arr
    for(i = 0; i < len; i++)
    {
        arr[i] = brr[i];
    }
    free(brr);
    brr = NULL;
}


//按 i*2 的步进方式,每次平分为 i*2 个元素的数据段,进行二路排序
void MergeSort(int *arr, int len)//归并排序
{
    for(int i = 1; i < len; i *= 2)
    {
        Merge(arr, len, i);
    }
}

void Show(int *arr, int len)
{
    assert(arr != NULL);
    for(int i = 0; i < len; i++)
    {
        printf("%d  ", arr[i]);
    }
    printf("\n");
}

int main()
{
    int arr[] = {7,5,0,4,3,1,2};
    int len = sizeof(arr) / sizeof(arr[0]);
    MergeSort(arr, len);
    Show(arr, len);
    getchar();
    return 0;
}
#include<stdio.h>
#include<assert.h>
#include<malloc.h>

//基数排序(桶子法)


//建立单链表
typedef struct Node
{
    int data;
    struct Node *next;
}Node, *List;

void InitList(List plist)
{
    assert(plist != NULL);
    plist->next = NULL;
}

static Node *GetNode(int val)
{
    Node *pGet = (Node *)malloc(sizeof(Node));
    assert(pGet != NULL);

    pGet->data = val;
    pGet->next = NULL;

    return pGet;
}

bool Insert(List plist, int val)//尾插
{
    assert(plist != NULL);
    Node *pGet = GetNode(val);

    Node *p = plist;
    while(p->next != NULL)
    {
        p = p->next;
    }
    p->next = pGet;

    return true;
}

bool Delet(List plist, int *rtv)//头删
{
    assert(plist != NULL);

    if(plist->next == NULL)
    {
        return false;
    }

    Node *pDel = plist->next;
    if(rtv != NULL)
    {
        *rtv = pDel->data;
    }
    plist->next = pDel->next;
    free(pDel);
    pDel = NULL;

    return true;
}

//相关函数实现
int GetMaxBit(int *arr, int len)//入桶次数:最大元素位数
{
    //得到最大元素
    int max = arr[0];
    for(int i = 1; i <len; i++)
    {
        if(max < arr[i])
        {
            max = arr[i];
        }
    }
    //得到元素位数
    int count = 0;
    while(max != 0)
    {
        count++;
        max /= 10;
    }

    return count;
}

int GetNum(int num, int figure)//应入桶号:figure位数字
{
    for(int i = 0;i < figure;i++)
    {
        num/=10;
    }

    return num%10;
}

//低位有序->高位有序->整体有序
//1、初始十个桶(0-9):单链表建立
//2、确定入桶(与出桶)次数:最大元素位数count
//3、完成每次的入桶与出桶:即figure位的排序
//  得到 figure位数字;
//  按照0-9号桶对应插入;
//  从0-9号桶依次出桶(当前桶全部出完才能开始出下一个桶)。
//4、当完成count次入桶、出桶操作后,即整体有序
void Base(int *arr, int len, int figure)//一次入桶、出桶
{
    //初始十个桶
    Node head[10];
    int i = 0;
    int tmp = 0;
    for(i = 0; i < 10; i++)
    {
        InitList(&head[i]);
    }

    //入桶
    for(i = 0; i < len; i++)
    {
        tmp = GetNum(arr[i], figure);
        Insert(&head[tmp], arr[i]);
    }

    //出桶
    i = 0;
    for(int j = 0; j <10; )
    {
        if(Delet(&head[j], &arr[i]))
        {
            i++;
        }
        else
        {
            j++;
        }
    }
}

void BaseSort(int *arr, int len)//基数排序
{
    int count = GetMaxBit(arr, len);

    for(int i = 0; i <count; i++)
    {
        Base(arr, len, i);
    }
}

void Show(int *arr, int len)
{
    assert(arr != NULL);

    for(int i = 0; i <len; i++)
    {
        printf("%d  ", arr[i]);
    }
    printf("\n");
}

int main()
{
    int arr[] = {12,3,1,8,34,21,88,54,0};
    int len = sizeof(arr)/sizeof(arr[0]);
    BaseSort(arr,len);
    Show(arr,len);
    getchar();
    return 0;
}

三、八大排序总结
1、分类:
1)、插入排序:
直接插入排序———->优化:shell排序

2)、选择排序:
直接选择排序———->优化:堆排序

3)、交换排序:
冒泡排序———->优化:快速排序

4)、归并排序

5)、基数排序
这里写图片描述

2、应用场景:
整体元素个数为n,
1)n较小,直接选择排序(不考虑稳定性)/直接插入排序

2)n较小,且整体或局部有序时,直接插入排序/冒泡排序/快速排序(随机)

3)n较大,快速排序(平均时间最短)/堆排序(内存空间允许且要求稳定性)/归并排序(内存空间允许且要求稳定性)

4)数据无符号,且可分解,基数排序

5)待排序元素间隔距离跨度较大,shell排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值