排序(五)--非比较排序:计数排序、基数排序

一、计数排序

1.算法思想

给定一组要排序的序列,找出这组序列中的最大值,然后开辟一个最大值加1大小的数组,将这个数组里面的元素全部置零,然后用这个数组统计出要排序的序列中各个元素出现的次数。等到统计完成的时候,排序就已经完成了。

2.算法具体步骤

(1)找出待排序序列里的最大值max;
(2)开辟一个大小为max+1的临时数组tmp ,将数组元素的所有初值赋为0;
(3)遍历待排序序列,将序列中出现值作为下标,对tmp 数组的对应位置数据进行++;
(4)上述操作完成后,tmp中统计了每个数据在待排序列中出现的次数;
(5)最后,将tmp数组里值不为0的所有下标拷进原序列(注意同一个下标可能有多个重复值,都要进行拷贝,不能遗漏),排序就算完成。

3.图解举例

这里写图片描述

4.代码实现
//计数排序
void CountSort1(int *arr, int size)
{
    if (arr == NULL || size <= 0)
        return;

    //遍历求数组中最大的数
    int max = arr[0];
    for (int i = 1; i < size; ++i)
    {
        if (arr[i]>max)
        {
            max = arr[i];
        }
    }

    //开辟一个max+1大小的空间
    int* tmp = new int[max + 1];
    //将tmp初始化为0
    memset(tmp, 0, sizeof(max + 1));

    //遍历原数组中每一个数出现的次数
    for (int j = 0; j < size; j++)
    {
        ++tmp[arr[j]];
    }

    int index = 0;
    for (int k = 0; k < size; k++)
    {
        while (tmp[k]--)
        {
            arr[index] = k;
            ++index;
        }
    }

    delete[] tmp;
}

代码优化:

void CountSort2(int* arr, size_t n)
{
    //找到待排序序列的最大值和最小值  
    int max = arr[0];
    int min = arr[0];
    for (size_t i = 0; i < n - 1; ++i)
    {
        if (max < arr[i + 1])
        {
            max = arr[i + 1];
        }
        if (min > arr[i + 1])
        {
            min = arr[i + 1];
        }
    }

    //开辟适当空间的数组  
    int range = max - min + 1;
    int* tmp = new int[range];
    memset(tmp, 0, sizeof(int)*(range));
    for (size_t i = 0; i < n; ++i)
    {
        tmp[arr[i] - min]++;
    }

    //将数据拷回原数组  
    int index = 0;
    for (int i = 0; i < range; ++i)
    {
        while (tmp[i]--)
        {
            arr[index] = i + min;
            ++index;
        }
    }
    delete[]tmp;
}
5.其他

(1)时间复杂度:O(N+K),其中K主要是取决于排序数组的范围。
(2)空间复杂度:O(N),实际上,计数排序是一种以空间换时间的做法。
(3)稳定性:不稳定。
(4)使用场景:适合元素排列比较集中的情况,并且数组中出现的数字次数不止一次。

二、基数排序

1.基数排序的分类
  • LSD– Least Significant Digit first(从低位向高位排)
  • MSD– Most Significant Digit first(从高位向低位排)
2.算法思想

基数排序又称”桶子法”,它从低位开始将待排序的数按照这一位的值放到相应的编号为0到9的桶中。等到低位排完之后得到一个序列,再将这个序列按照次低位的大小进入相应的桶中。以此类推,直到将所有的位都排完之后,这组数就已经有序了。

3.算法的具体步骤

(1)统计待排序序列中最大的数是几位数;
(2)开辟一个与原数组大小相同的临时数组tmp;
(3)用一个count数组统计原数组中某一位(从个位向高位统计)相同的数据出现的次数。用一个start数组计算原数组中某一位(从个位向高位计算)相同的数据出现的位置。将桶中数据从小到大(由顶至底)用tmp数组收集起来;
(4)重复上述过程,直到所有位都被统计并计算过,然后用tmp收集起来;
(5)将tmp数组的数据拷回原数组,排序完成。

4.图解举例

这里写图片描述

5.代码实现
int GetMaxdigit(int* arr, int size)
{
    if (arr == NULL || size <= 0)
        return NULL;

    int MAX = arr[0];
    for (size_t i = 0; i < size; ++i)
    {
        if (arr[i] > MAX)
            MAX = arr[i];
    }
    int count = 0;
    while (MAX)
    {
        MAX /= 10;
        ++count;
    }
    return count;
}
void LSDSort(int *arr, int size)//基数排序
{
    if (arr == NULL || size <= 0)
        return;

    int base = 1;
    int digit = GetMaxdigit(arr, size);//获取最大元素的位数
    int *tmp = new int[size];//开辟临时数组
    while (digit--)
    {
        //统计某位相同的数据出现的次数
        int count[10] = { 0 };
        for (size_t i = 0; i < size; ++i)
        {
            int index = arr[i] / base % 10;
            count[index]++;
        }
        int start[10] = { 0 };
        //统计个位相同的数在数组中出现的位置
        for (size_t j = 1; j < size; ++j)
        {
            start[j] = count[j - 1] + start[j - 1];
        }
        memset(tmp, 0, sizeof(int)*size);
        for (int k = 0; k < size; ++k)
        {
            int index = arr[k] / base % 10;
            tmp[start[index]++] = arr[k];

        }
        memcpy(arr, tmp, sizeof(int)*size);
        base *= 10;

    }

    delete[]tmp;
}
6.其他

(1)时间复杂度:O(N*digit)
(2)空间复杂度:O(N)
(3)稳定性:稳定

猜你喜欢

转载自blog.csdn.net/cherrydreamsover/article/details/81193069