原理
题目目标:通过基数排序算法对整数序列进行升序排列。
核心方法:从最低位到最高位依次进行稳定分桶排序,利用字符串处理方便按位操作。
步骤
- 转换与补零:将整数转为字符串,补前导零使等长。
- 逐位分桶:从最低位到最高位,每次按当前位数字分到0-9桶中。
- 合并桶:按桶顺序收集元素,保持稳定性。
- 输出结果:去除前导零后输出排序后的数字。
图示法表示步骤(样例输入 7 1 4 8 5 2
)
初始字符串(补零后长度1):["7","1","4","8","5","2"]
- 第1轮(个位)排序:
- 分桶:0-9桶按个位数字分组。
- 结果:
["1","2","4","5","7","8"]
(已有序)。
最终输出:1 2 4 5 7 8
代码关键行注释
for (int digit_pos = maxlength - 1; digit_pos >= 0; digit_pos--)
// 从最低位(字符串末位)向最高位依次处理
buckets[current_digit].push_back(s);
// 根据当前位数字将元素放入对应桶
vec[i].substr(first_non_zero)
// 输出去除前导零后的实际数值
完整代码程序
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
vector<string> vec(n);
int maxlength = 0;
// 转换为字符串并计算最大长度
for (int i = 0; i < n; i++) {
vec[i] = to_string(nums[i]);
maxlength = max(maxlength, (int)vec[i].size());
}
// 补前导零,使所有字符串等长
for (int i = 0; i < n; i++) {
if (vec[i].size() < maxlength) {
vec[i] = string(maxlength - vec[i].size(), '0') + vec[i];
}
}
// 基数排序:按每一位从低位到高位进行稳定排序
for (int digit_pos = maxlength - 1; digit_pos >= 0; digit_pos--) {
vector<vector<string>> buckets(10); // 0-9共10个桶
// 将当前位的数字分入对应桶中
for (const string& s : vec) {
int current_digit = s[digit_pos] - '0';
buckets[current_digit].push_back(s);
}
// 收集桶中的元素,保持稳定性
vec.clear();
for (int d = 0; d < 10; d++) {
for (const string& s : buckets[d]) {
vec.push_back(s);
}
}
}
// 输出去除前导零后的结果
for (int i = 0; i < n; i++) {
string s = vec[i];
// 找到第一个非零字符的位置
size_t first_non_zero = s.find_first_not_of('0');
if (first_non_zero == string::npos) { // 全零情况
cout << "0";
} else {
cout << s.substr(first_non_zero);
}
if (i != n - 1) cout << " ";
}
return 0;
}
时间复杂度
- 时间复杂度:O(k·n),其中
k
为最大位数,n
为元素个数。 - 空间复杂度:O(n + 10·k),用于存储分桶的临时空间。
总结
- 代码优势:
- 正确性:通过字符串处理简化了按位操作,稳定分桶确保排序正确。
- 鲁棒性:处理全零情况和前导零的去除逻辑完善。
- 潜在限制:
- 仅限非负整数:未处理负数,但题目未要求。
- 字符串转换开销:对极大整数可能影响性能,但符合题目约束。
- 适用场景:
- 整数位数差异较小的数据集。
- 需要稳定排序且数值范围可控的场景。