一、题目
题目链接:力扣
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
示例 1:
输入: [10,2]
输出: "102"
示例 2:
输入: [3,30,34,5,9]
输出: "3033459"
提示:
0 < nums.length <= 100
说明:
输出结果可能非常大,所以你需要返回一个字符串而不是整数
拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0
二、题解
1、思路
👩⚕️ 自定义归则 + 快速排序
思路描述:首先我们要明白就是
- 无论这些数字怎么取排列,形成的数字的位数是不变的
那么就是高位的数字肯定是越小越好。
我们先考虑一下怎么排列两个数字,比如 1 和 20,高位越小越好,放 1,组合成 120
我们再看一下三个数的情况,比如 36、38 和 5,首先肯定先放 36,剩下 38 和 5,然后对这两个数进行排列 385,所以最后的结果为 36385。
由上面的两个例子我们其实就可以知道,放数字的顺序肯定是先放第一位(最左边一位)最小的元素,如果第一位相等,比较第二位....,以此类推。
关键是怎么进行排序呢?
设数组 nums 中任意两数字的字符串为 x 和 y ,则规定排序判断规则为:注意”+“代表两个数字组合成数字。
- 若拼接字符串 x + y > y + x ,则 x “大于” y ;
- 反之,若 x + y < y + x ,则 x “小于” y ;
- 总结:哪个字符在前组成的数组大,哪个就大,比如,36、38、5,5>38>36
x “小于” y 代表:排序完成后,数组中 x 应在 y 左边;“大于” 则反之。
对于上面的36、38、5:
36 + 38 --> 3638
38 + 36 --> 3836
36 + 38 < 38 + 36,因此38>36
38 + 5 --> 385
5 + 38 --> 538
38 + 5 < 5 + 38,因此5>38
以上说法仅针对两个数字,针对n个数字的数组结论是否正确呢?也就是是否具有传递性呢?
具有。不再证明,详见力扣。
那么对于该题,我们只需要:
1.先将数字转化为字符串数组;
2.对字符串数组按照自定义的归则进行升序排序;
3.从小到大连接字符串作为结果。
2、代码实现
👩⚕️ 自定义归则 + 快速排序
快排:排序算法--快速排序--详解与代码示例_快速排序算法_阿尔兹的博客-CSDN博客
易错点:
- 首先要将数组转化为字符串数组,也就是vector<string> str;
- j > i,而不是 j > low,因为i,j都是在变的;
- low是可能大于high的,而不是在low==high的时候停止排序;
- 右侧找到一个最小的(左同),赋值到左侧空位置后,左侧下标记得加一。
class Solution {
public:
string minNumber(vector<int>& nums) {
int n = nums.size();// 题目中说n一定大于0
vector<string> strs;// 将数组中的数字转化为string数组
// 数字转化为string数组
for(int i= 0; i < n; i++)
{
strs.emplace_back(to_string(nums[i]));
}
// 自定义归则快排,s升序
quick_sort(strs, 0, n-1);
// 结果重组
// += 运算符:追加单个参数值。
// append 函数:允许追加多个参数值。
// push_back 函数:只能追加单个字符。
string res;
for(string &str : strs)res += str;
return res;
}
private:
// https://blog.csdn.net/qq_41709234/article/details/128476788
// 对输入数组strs进行快速排序
// low: 需要排序的最低下标
// high: 需要排序的最高下标
// 每次排好一个元素的位置
void quick_sort(vector<string>& strs, int low, int high)
{
if(low >= high)return;// low=high指向同一个数字,不需要排序
int i = low, j = high;
string pivot = strs[i];
while(i < j)
{
// 找一个比pivot小的
while(i<j && pivot+strs[j] < strs[j]+pivot)j--;
if(i<j)strs[i++] = strs[j];// 覆盖pivot所在位置 i++;刚移动过来肯定比pivot小,而下面找大的
// 找一个比pivot大的
while(i<j && pivot+strs[i] > strs[i]+pivot)i++;
// 放到上面j位置
if(i<j)strs[j--] = strs[i];//j--;下次循环找比pivot小的
// 注意上面的i++,这里的j--必须放在if(i<j)里面,否则i=j不需要排序的时候,仍然会被排序
}
// 为什么i=j的时候一定是可以被覆盖呢?
// 看上方代码,while中i、j每次只能一个一移动、一个静止,静止的指向要被覆盖的内容,比如,第一次,首个元素可以被覆盖,i指向,是静止的,j移动。最终一个一移动、一个静止,所以这个内容一定可以被覆盖
strs[i] = pivot;// i == j
// 至此一个元素的位置已经完全找好,开始对pivot左侧、右侧进行排序
quick_sort(strs, low, i - 1);
quick_sort(strs, i + 1, high);
}
};
3、复杂度分析
👩⚕️ 自定义归则 + 快速排序
时间复杂度:O(nlogn);快速排序平均时间复杂度O(nlogn);
空间复杂度:O(n)。字符串占用空间。
4、运行结果
👩⚕️ 自定义归则 + 快速排序