C++三种线性排序算法

本文介绍了基数排序、计数排序和桶排序三种线性排序算法的特点及应用场景,并提供了详细的实现代码。这些算法在特定条件下能达到O(N)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

三种线性排序算法

  • 基数排序、计数排序和桶排序都是特定情况下时间复杂度为 O(N) O ( N ) 的算法(对于特定算法可能需要加上常数项),他们都属于非比较的排序算法。但是他们的空间复杂度都不是 O(1) O ( 1 ) (部分基于比较的排序算法可以做到 O(1) O ( 1 ) 的空间复杂度)。

基数排序

  • 基于比较的排序方法无法做到在 O(N) O ( N ) 的时间复杂度内完成对数据的排序,但是基数排序不是基于比较的排序方法。
  • 基于排序的原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
  • 基数排序的平均时间复杂度为 O(dN) O ( d N ) ,d是数据的位数,空间复杂度是 O(kN) O ( k N ) ,k为一个基数所具有的可能的取值个数(10进制的话,就是k=10)
  • 参考链接
  • 注意:虽然基数排序的时间复杂度很小,但是一方面他的空间复杂度很大(基于比较的排序一般空间复杂度都是 O(1) O ( 1 ) ),另一方面很多需要排序的数据没有基数,因此无法使用基数排序。

代码

#include <cstdlib>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <map>
#include <set>
#include <stdio.h>
#include <string.h>

using namespace std;
typedef long long ll;
int dirs[8][2] = { 1, 1, 1, 0, 1, -1, 0, 1, 0, -1, -1, 1, -1, 0, -1, -1 };

// 获取最大位数
int maxBit(const vector<int>& data)
{
    int d = 1;
    int p = 10;
    int ret = 0;
    for (int i = 0; i < data.size(); ++i)
    {
        d = 1;
        p = 10;
        while ( data[i] >= p )
        {
            p *= 10;
            ++d;
        }
        ret = max( ret, d );
    }
    return ret;
}

// 获取数字num在第pos位的数值
int getDigit( const int& num, int pos )
{
    int p = 1;
    for (int i = 1; i < pos; i++)
    {
        p *= 10;
    }
    return (num / p) % 10;
}

// 从低位开始的基数排序
void LSDRadixSort(vector<int>& nums)
{
    if (nums.size() <= 1)
        return;
    int bits = maxBit( nums );
    vector<int> bucket(nums.size(), 0);
    vector<int> count(10, 0);
    for (int k = 1; k <= bits; ++k)
    {
        for (int i = 0; i < 10; ++i)
            count[i] = 0;

        for (int i = 0; i < nums.size(); ++i)
            ++count[getDigit(nums[i], k)];

        // count[i]表示第i个桶的右边界index
        for (int i = 1; i < 10; ++i)
        {
            count[i] += count[i - 1];
        }

        // 从右向左扫描,保证排序的稳定性
        // 当前位不同时,能够保证后面的数比前面的数大
        for (int i = nums.size() - 1; i >= 0; --i)
        {
            int j = getDigit( nums[i], k );
            // 将nums[i]从到左依次放置
            bucket[count[j] - 1] = nums[i];
            // 右边界向左减一
            --count[j];
        }

        // 保存当前的排序顺序
        // 注意:这一步保证第k-1位的都已经被正确排序,并且之后该顺序不会被打乱
        for (int i = 0; i < nums.size(); ++i)
            nums[i] = bucket[i];
    }
}

// 从高位开始的基数排序的递归调用,
// 加入了begin和end,方便对区域子桶中的数据进行进一步的排序
void mergeMSD(vector<int> &nums, int begin, int end, int bits)
{
    vector<int> count(10, 0);
    vector<int> bucket( end-begin, 0 );
    for (int i = begin; i < end; ++i)
        ++count[getDigit(nums[i], bits)];

    for (int i = 1; i < 10; i++)
    {
        count[i] += count[i - 1];
    }

    for (int i = end - 1; i >= begin; --i)
    {
        int j = getDigit( nums[i], bits );
        bucket[count[j] - 1] = nums[i];
        --count[j];
    }

    for (int i = begin; i < end; ++i)
    {
        nums[i] = bucket[i];
    }

    // 递归对每个子桶中的数据进行排序
    for (int i = 0; i < 9; i++)
    {
        int p1 = begin + count[i];
        int p2 = begin + count[i+1];
        if (p1 + 1 < p2 && bits > 1)
            mergeMSD( nums, p1, p2, bits-1 );
    }
}

// 从高位开始的基数排序
void MSDRadixSort(vector<int> &nums)
{
    if (nums.size() <= 1)
        return;
    int bits = maxBit(nums);
    mergeMSD(nums, 0, nums.size(), bits);
}

int main()
{
    int row = 0, col = 0;
    // ifstream is("data.txt", ios::in);
    vector<int> nums = {23, 18, 98, 67, 101};
    LSDRadixSort( nums );
    for_each(nums.begin(), nums.end(), [](int num)  { cout << num << " "; });
    cout << endl;


    nums = { 23, 18, 98, 67, 101 };
    MSDRadixSort(nums);
    for_each(nums.begin(), nums.end(), [](int num)  { cout << num << " "; });
    cout << endl;
    system("pause");
    return 0;
}

计数排序

  • 对于一些数据非常集中的整数,可以使用这种方法进行排序

代码

#include <cstdlib>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <map>
#include <set>
#include <stdio.h>
#include <string.h>

using namespace std;
typedef long long ll;

void countSort( vector<int> &nums )
{
    if (nums.size() <= 1)
        return;

    int maxV = nums[0];
    int minV = nums[0];
    for (int i = 1; i < nums.size(); ++i)
    {
        maxV = max( maxV, nums[i] );
        minV = min(minV, nums[i]);
    }

    // 建立help数组,存储每个值(减去最小值之后)出现的次数
    vector<int> help( maxV-minV+1, 0 );
    for (int i = 0; i < nums.size(); ++i)
        ++help[ nums[i] - minV ];

    int index = 0;
    for (int i = 0; i < help.size(); ++i)
    {
        while (help[i] > 0)
        {
            nums[index++] = i + minV;
            --help[i];
        }
    }

}

int main()
{
    int row = 0, col = 0;
    // ifstream is("data.txt", ios::in);
    vector<int> nums = {6,6,4,3,5,7,9,3,20};
    countSort( nums );
    for_each(nums.begin(), nums.end(), [](int num)  { cout << num << " "; });
    cout << endl;


    system("pause");
    return 0;
}

桶排序

  • 数据数据偏差很大的情况(最大值与最小值偏差很大),此时使用计数排序十分浪费空间,我们可以使用桶排序。
  • 建立K个桶,每个桶中放置一个子数组,保证靠前的数组中的最大值小于靠后的数组中的最小值,然后对桶内数据进行排序,最后再合并即可。
  • 使用桶排序时,我们假定数据是均匀随机分布的,否则如果数据被分到同一个桶内,会造成消耗大量内存空间的基础上(空间复杂度),排序的时间复杂度也不是 O(N) O ( N )

代码

    #include <cstdlib>
    #include <string>
    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <sstream>
    #include <unordered_map>
    #include <algorithm>
    #include <map>
    #include <set>
    #include <stdio.h>
    #include <string.h>

    using namespace std;
    typedef long long ll;

    void bucketSort( vector<int> &nums )
    {
        if (nums.size() <= 1)
            return;

        int maxV = nums[0];
        int minV = nums[0];
        for (int i = 1; i < nums.size(); ++i)
        {
            maxV = max(maxV, nums[i]);
            minV = min(minV, nums[i]);
        }

        // 桶的数量,这里可以随意指定,但是对于具体情况,建议具体分析
        int bucketNumber =  (maxV - minV) / 10 + 1;
        vector<vector<int>> bucketNums(bucketNumber);

        for (int i = 0; i < nums.size(); i++)
        {
            int num = (nums[i] - minV) / 10;
            bucketNums[num].push_back( nums[i] );
        }

        // 对于每个桶中使用的排序算法的复杂度就是桶排序的最坏时间复杂度
        for (int i = 0; i < bucketNums.size(); i++)
        {
            std::sort(bucketNums[i].begin(), bucketNums[i].end());
        }

        int index = 0;
        for (int i = 0; i < bucketNums.size(); i++)
        {
            for (auto &num : bucketNums[i])
                nums[index++] = num;
        }
    }

    int main()
    {
        int row = 0, col = 0;
        // ifstream is("data.txt", ios::in);
        vector<int> nums = {6,6,4,3,5,7,9,3,20};
        bucketSort( nums );
        for_each(nums.begin(), nums.end(), [](int num)  { cout << num << " "; });
        cout << endl;


        system("pause");
        return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

littletomatodonkey

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值