数据结构之排序(4)——归并排序和基数排序

归并排序

基本思想:分而治之,然后将有序子表合并。

这里写图片描述

这里写图片描述

算法思路:
1. 将两段相邻有序表放到临时数组中;
2. 两个值i,j分别记录当前读取位子;
3. 读取当前位子值并比较,小的放到数组中;
4. 如果i或者j超过表长,则将剩下的直接复制过去。

int n = 8;
int *b = (int *)malloc((n + 1)*sizeof(int));
void merge(int arr[], int low, int mid, int high)
{   
    int i, j, k;
    for (k = low; k <= high; ++k)
        b[k] = arr[k];
    for (i = low, j = mid + 1, k = i; i <= mid&&j <= high; ++k){
        if (b[i] <= b[j])
            arr[k] = b[i++];
        else
            arr[k] = b[j++];
    }
    while (i <= mid)
        arr[k++] = b[i++];//第一个表剩下的复制过去
    while (j <= high)
        arr[k++] = b[j++];//第二个表剩下的复制过去
}
void mergeSort(int arr[], int low, int high)
{
    if (low < high){
        int mid = (low + high) / 2;
        mergeSort(arr, low, mid);
        mergeSort(arr, mid+1, high);
        merge(arr, low, mid, high);
        print(arr, 8, 1);
    }
}

输出:

6 5 3 1 8 7 2 4
5 6 3 1 8 7 2 4
5 6 1 3 8 7 2 4
1 3 5 6 8 7 2 4
1 3 5 6 7 8 2 4
1 3 5 6 7 8 2 4
1 3 5 6 2 4 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8

空间复杂度:辅助空间占n个单元,还要一个递归栈,深度最大为 log2n ,所以复杂度为 O(n)

时间复杂度:一趟归并必定要移动n次(从辅助空间b到arr),需要 log2n 次归并,故为 nlog2n

稳定性:一个个比的,也没有交换的情况,很稳定。

基数排序

基本思想:通过序列中各个元素的值,对排序的N个元素进行若干趟的“分配”与“收集”来实现排序。
分配:我们将L[i]中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中
收集:当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列L[ ]
对新形成的序列L[]重复执行分配和收集元素中的十位、百位…直到分配完该序列中的最高位,则排序结束。

以下是最低位优先时的基数排序的实现:

void radixSortLSD(int arr[], int n)
{
    int i, maxVal INT_MIN, digitPos = 1;
    int *bucket = new int[n];
    for (i = 0; i < n; ++i)
        if (arr[i]>maxVal)
            maxVal = arr[i];
    while (maxVal/digitPos > 0){
        int digitCount[RADIX] = { 0 };

        //统计各个桶有几个数
        for (i = 0; i < n; ++i)
            ++digitCount[arr[i] / digitPos % 10];

        //将个数转换为桶中最后一个数的下标
        for (i = 1; i < RADIX; ++i)
            digitCount[i] += digitCount[i - 1];

        //依次从后往前计算每个数属于哪个桶,再根据那个桶中的数决定放在辅助数组哪个位子,从后往前能保证算法稳定
        for (i = n - 1; i >= 0; --i)
            bucket[--digitCount[arr[i]/digitPos%10]] = arr[i];

        //替换到原数组中
        for (i = 0; i < n; ++i)
            arr[i] = bucket[i];

        //下次循环位数增加
        digitPos *= 10;
    }
}

时间复杂度:
设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为 O(d(n+r)) ,其中,一趟分配时间复杂度为 O(n) ,一趟收集时间复杂度为 O(r) ,共进行d趟分配和收集。

空间复杂度:
需要一个指向队列长度的辅助空间和一个radix长度的辅助空间,共 O(n+r)

各算法复杂度表

加上前几篇的内容,表扩充为:

算法平均时间复杂度最好时间复杂度最差时间复杂度空间复杂度稳定性备注
直接插入 O(n2) O(n) O(n2) O(1) 稳定
折半插入 O(n2) O(nlog2n) O(n2) O(1) 稳定
shell O(n1.3) O(n) O(n2) O(1) 不稳定和增量序列有关
冒泡 O(n2) O(n) O(n2) O(1) 稳定子序列全局有序,不同于插入排序
快排 O(nlog2n) O(nlog2n) O(n2) O(log2n) 不稳定
简单选择 O(n2) O(n2) O(n2) O(1) 不稳定
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定
2路归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(n+r) 稳定其中,d 为位数,r 为基数,n 为原数组个数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值