目录
[2.洛谷: P1177 模版排序题](#2.洛谷: P1177 模版排序题)
1.定义
和常规意义上的排序算法不同,基数排序不需要比较+交换数据,也不需要像快速排序那样选key后比较关键字
基数排序(Radix Sorting,radix n.基数):一种利用多关键字 进行++分配类排序++的方法
解释多关键字:扑克牌从小到大排序
扑克牌一共54张牌,其中52张是正牌,另2张是副牌(大王和小王)
52张正牌按花色一共分为4组:黑桃组♠、红桃组♥、梅花组♣、方块组♦
认为花色的大小关系为:♣ < ♦ < ♥ < ♠
每组花色的牌的面值包括从1~10以及J、Q、K牌,大小关系`1<2<...<10<J<Q<K
其中花色的地位比面值高,那就有两种排序方法
最高位(注:位指的是地位)优先法(Most Significant Digit First):花色的地位比面值高,先按花色排
最低位优先法(Least Significant Digit first):面值的地位比花色低,先按面值排
2.洛谷: P1177 模版排序题
https://www.luogu.com.cn/problem/P1177
题目描述
将读入的 N 个数从小到大排序后输出。
输入格式
第一行为一个正整数 N。
第二行包含 N 个空格隔开的正整数 ai,为你需要进行排序的数。
输出格式
将给定的 N 个数从小到大输出,数之间空格隔开,行末换行且无空格。
输入输出样例
输入 #1
5 4 2 4 5 1
输出 #1
1 2 4 4 5
说明/提示
对于 20% 的数据,有 1≤N≤103;
对于 100% 的数据,有 1≤N≤105,1≤ai≤109。
3.算法
过程
以一个例子说明基数排序,设有一个无序数组nums={123,789,012,2,0,99,147,83,091,235},数组中的元素的值**>=0**,要求从小到大排序
按"重复先分发后回收步骤 "的思想排序,排序后的数组相邻元素之间一定满足个位<=个位,十位<=十位,百位<=百位
数组中的元素最多3位,这些位为个位、十位、百位
显然地位上:百位>十位>个位,如果按照最低位优先法排序,那就按个位、十位、百位的顺序进行基数排序,其实类似哈希桶的思想:将不同的数字分成不同的数据堆,数据堆可以看成桶
可以创建一个队列数组queue<int> tmp[10],其中tmp[i]表示存储位值为i的队列
例如:123按个位分类时,其个位为3,那就进入tmp[3]的队列
取出元素的各个位的方法:
设某元素的值为x
个位:x/
%10
十位;x/
%10
百位:x/
$10
......
按个位分类,从头到尾遍历数组,先分发

tmp[0]~tmp[9]依次pop(元素)重新拷贝到原来的数组中,后回收
nums={0 ,91 ,12 ,2 ,123 ,83 ,235 ,147 ,789 ,99},个位满足从大到小的顺序
按十位分类,从头到尾遍历数组,先分发

tmp[0]~tmp[9]依次pop(元素)重新拷贝到原来的数组中,后回收
nums={0 0,0 2,1 2,12 3,23 5,14 7,8 3,78 9,9 1,99},个位和十位都满足从大到小的顺序
按百位分类,从头到尾遍历数组,先分发

tmp[0]~tmp[9]依次pop(元素)重新拷贝到原来的数组中,后回收
nums={0 00,0 02,0 12,0 83,0 91,0 99,1 23,1 47,2 35,789},个位、十位和百位都满足从大到小的顺序,那数组就是有序的
时间复杂度分析
纯数字排序的时间复杂度:如果数组中一共N个元素,其中元素的最高位为第M位,那么每次分配的时间复杂度为,每次回收的时间复杂度为
,一共要重复先分发后回收m次,总时间复杂度为
4.代码
读题发现所有元素都是正数,可以用position来控制第几位,power_10表示10的次方,那么分发num[j]的第position位的值position_val为
(nums[j] / power_10) % 10 ,可push到对应的子队列中:tmp[position_val].push(nums[j])
那么可以使用循环来回收tmp[k]:
cpp
while (!tmp[k].empty())
{
nums[l++] = tmp[k].front();
tmp[k].pop();
}
则完整代码为:
用C语言比较麻烦,要自己实现数据结构,这里用C++(放到C语言专栏只是为了好管理)
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
vector<int> sortArray(vector<int>& nums)
{
//使用队列
queue<int> tmp[10];
int position = 0;
int power_10 = 1;//10的n次方
//1≤ai≤10^9,最多10位
while (position <= 10)
{
for (int j = 0; j < nums.size(); j++)
{
//先分发
int position_val = (nums[j] / power_10) % 10;
tmp[position_val].push(nums[j]);
}
power_10 *= 10;
//后回收
for (int k = 0, l = 0; l < nums.size();)
{
while (!tmp[k].empty())
{
nums[l++] = tmp[k].front();
tmp[k].pop();
}
k++;
}
position++;
}
return nums;
}
};
int main()
{
int n;
cin >> n;
vector<int> arr(n);
for (int i = 0; i < n; i++)
cin >> arr[i];//必须全是正数
Solution().sortArray(arr);
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
}
5.提交结果

6.思考题:如果带上负数又应该怎么排序?
因为负数的第position位的值为(( - nums[j]) / power_10) % 10,显然正数和负数要分开存储,分发时正数存储在positive_tmp队列数组中,负数存储在negative_tmp队列数组中
而回收时,由于负数越小,其绝对值越大,那就要按tmp[9]~tmp[0]的顺序出队
将以下代码放到LeetCode上测试:
cpp
class Solution {
public:
vector<int> sortArray(vector<int>& nums)
{
//使用队列
queue<int> positive_tmp[10];
queue<int> negative_tmp[10];
//5 * 10^4=50000
int position = 0;
int power_10 = 1;
//-5 * 10^4 <= nums[i] <= 5 * 10^4
while (position <= 5)
{
for (int j = 0; j < nums.size(); j++)
{
//取出对应位添加到对应的子队列中
if (nums[j] >= 0)
{
int position_val = (nums[j] / power_10) % 10;
positive_tmp[position_val].push(nums[j]);
}
else
{
int position_val = (( - nums[j]) / power_10) % 10;
negative_tmp[position_val].push(nums[j]);
}
}
power_10 *= 10;
//拷贝回nums
int k = 0, l = 0;
while (k <= 9)
{
while (!negative_tmp[9 - k].empty())
{
nums[l++] = negative_tmp[9 - k].front();
negative_tmp[9 - k].pop();
}
k++;
}
k = 0;
while (k <= 9)
{
while (!positive_tmp[k].empty())
{
nums[l++] = positive_tmp[k].front();
positive_tmp[k].pop();
}
k++;
}
position++;
}
return nums;
}
};
提交结果:
