一、题目描述
题目大意:给定一个nums数组由一些非负整数组成,现需要将他们进行排列并拼接,每个数不可拆分,使得最后的结果最大,返回值需要是string类型,否则可能会溢出
注意审题:由一些非负整数组成,因此不必考虑负数的情况
二、算法一(排序)
解题思路
- 要使得拼接出的数尽可能的大,一种直观的想法就是令拼接出的数高位尽可能的大,这样整体就更大。对于两个数a、b,要求得最大值,直接比较两者拼接后的大小即可,因此我们可以定义一种比较大小的规则,即若 ab < ba ,则数b小于数a,应该排在数a之前,其中 ab = a * 10^count(b) + b (count(b)为b的位数)
- 当元素个数大于2时,我们只要证明了这种比较规则是满足传递性,就可以确定两个元素在排序后的相对位置关系,当确定了所有数的相对位置关系之后,整个数组拼接成的序列就是最终的结果
- 当一个数a排在数b使得结果更优时,我们记作 a#b ,要证明比较规则具有传递性,即证明 a#b 且 b#c 时,满足 a#c,证明如下:
由 a#b 且 b#c 可知:
- a * 10^count(b) + b >= b * 10^count(a) + a
- b * 10^count(c) + c >= c * 10^count(b) + b
整理得: - a * (10^count(b) - 1) >= b * (10^count(a) - 1)
- b * (10^count(c) - 1) >= c * (10^count(b) - 1)
由于均为非负整数,故将两式相乘得:
a * (10^count(c) - 1) * b * (10^count(b) - 1) >= c * (10^count(a) - 1) * b * (10^count(b) - 1)
为了证明 a#c,我们对b进行分类讨论:
(1) b = 0 时:
- 10^count(b) = 1,带入得等式两边都为0,此时满足a#c
(2) b > 0 时:
- b * (10^count(b) - 1) > 0,故等式两边同时除以b * (10^count(b) - 1)
- 得 a * 10^count(c) - a >= c * 10^count(a) - c,移项得 a * 10^count(c) + c >= c * 10^count(a) + a,满足 a#c
故得证排序规则具有传递性
代码实现(C++11)
class Solution { public: string solve(vector<int>& nums) { // 为防止拼接后溢出 using LL = long long; // 求非负整数x的数位 auto count = [](int x) -> int { if(x == 0) return 1; int cnt = 0; while(x) { ++cnt; x /= 10; } return cnt; }; // 求10的k次幂 auto qmi = [](int k) -> int { int ans = 1, a = 10; while(k) { if(k & 1) ans *= a; a *= a; k >>= 1; } return ans; }; // 自定义排序 sort(nums.begin(), nums.end(), [=](const int &a, const int &b){ return (LL)b * qmi(count(a)) + a < (LL)a * qmi(count(b)) + b; }); // 拼接整个字符串 string ans; for(auto &num : nums) { ans += to_string(num); } // 去除前导零 reverse(ans.begin(), ans.end()); while(ans.size() > 1 && ans.back() == '0') { ans.pop_back(); } reverse(ans.begin(), ans.end()); return ans; } };
时间复杂度:,n为元素个数,时间复杂度取决于排序,使用快速排序,进行了次比较,由于数据每个数都是小于的非负整数,故每次比较的时间可以看做很小的常数,故总的时间复杂度为
空间复杂度:,排序递归深度为
三、算法二(优先级队列)
类似地,利用以上的结论,我们自定义比较规则,不过这里用优先级队列(堆)来实现,先将所有元素入堆,根据自定义的比较规则,每次先出堆的元素优先级更高,即出堆元素与堆中任意数两两组合形成的最大数,出堆元素总在前半部分,故只要依次将元素出堆即可得到最终的结果序列
代码实现(C++11)
struct cmp { // 为防止拼接后溢出 using LL = long long; // 求非负整数x的数位 int count(int x) { if(x == 0) return 1; int cnt = 0; while(x) { ++cnt; x /= 10; } return cnt; } // 求10的k次幂 int qmi(int k) { int ans = 1, a = 10; while(k) { if(k & 1) ans *= a; a *= a; k >>= 1; } return ans; } // 重载()运算符 bool operator()(const int& a, const int& b) noexcept { return (LL)b * qmi(count(a)) + a > (LL)a * qmi(count(b)) + b; } }; class Solution { public: string solve(vector<int>& nums) { priority_queue<int, vector<int>, cmp> hp(nums.begin(), nums.end()); // 拼接整个字符串 string ans; while(hp.size()) { ans += to_string(hp.top()); hp.pop(); } // 去除前导零 reverse(ans.begin(), ans.end()); while(ans.size() > 1 && ans.back() == '0') { ans.pop_back(); } reverse(ans.begin(), ans.end()); return ans; } };
时间复杂度:,n为元素个数,堆的每次插入操作时间复杂度为,共总时间复杂度为
空间复杂度:,堆使用的空间是的