LeetCode 第179题:最大数
题目描述
给定一组非负整数 nums
,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
难度
中等
题目链接
示例
示例 1:
输入: nums = [10,2]
输出: "210"
示例 2:
输入: nums = [3,30,34,5,9]
输出: "9534330"
提示
1 <= nums.length <= 100
0 <= nums[i] <= 10^9
解题思路
方法:自定义排序
要得到最大的整数,我们需要将所有数字按照特定顺序排列。普通的排序(如按照数字大小排序)是不够的,因为我们需要考虑的是数字拼接后的大小。
关键点:
- 对于两个数字 a 和 b,如果将它们拼接起来,应该比较 a+b 和 b+a 这两种拼接方式哪个更大
- 根据这个比较规则对数组进行排序
- 处理特殊情况,如数组中有多个 0
实现步骤:
- 将数字转换为字符串
- 使用自定义比较函数,对于任意两个字符串 a 和 b,比较 a+b 和 b+a 的字典序
- 按照比较函数对字符串数组进行排序
- 将排序后的字符串数组连接起来
- 处理前导零的特殊情况,如果结果以 0 开头且长度大于 1,说明整个数组都是 0,应返回 “0”
时间复杂度:O(n log n),其中 n 是 nums 的长度(排序的时间复杂度)
空间复杂度:O(n),用于存储字符串数组
代码实现
C# 实现
public class Solution {
public string LargestNumber(int[] nums) {
// 将整数转换为字符串
string[] numStrs = new string[nums.Length];
for (int i = 0; i < nums.Length; i++) {
numStrs[i] = nums[i].ToString();
}
// 自定义排序,比较 a+b 和 b+a 的大小
Array.Sort(numStrs, (a, b) => (b + a).CompareTo(a + b));
// 特殊情况:如果排序后的第一个数是 0,说明所有数都是 0
if (numStrs[0] == "0") {
return "0";
}
// 连接所有字符串
return string.Join("", numStrs);
}
}
Python 实现
class Solution:
def largestNumber(self, nums: List[int]) -> str:
# 自定义比较函数
def compare(x, y):
# 比较 x+y 和 y+x 的大小
return int(y + x) - int(x + y)
# 将整数转换为字符串
nums = [str(num) for num in nums]
# 根据自定义比较函数排序
nums.sort(key=functools.cmp_to_key(compare))
# 处理特殊情况:如果排序后的第一个数是 0,说明所有数都是 0
if nums[0] == "0":
return "0"
# 连接所有字符串
return "".join(nums)
C++ 实现
class Solution {
public:
string largestNumber(vector<int>& nums) {
// 将整数转换为字符串
vector<string> numStrs;
for (int num : nums) {
numStrs.push_back(to_string(num));
}
// 自定义排序,比较 a+b 和 b+a 的大小
sort(numStrs.begin(), numStrs.end(), [](const string& a, const string& b) {
return a + b > b + a;
});
// 特殊情况:如果排序后的第一个数是 0,说明所有数都是 0
if (numStrs[0] == "0") {
return "0";
}
// 连接所有字符串
string result;
for (const string& numStr : numStrs) {
result += numStr;
}
return result;
}
};
性能分析
各语言实现的性能对比:
实现语言 | 执行用时 | 内存消耗 | 特点 |
---|---|---|---|
C# | 100 ms | 39.9 MB | 使用内置 Array.Sort 和 CompareTo 方法 |
Python | 40 ms | 16.1 MB | 使用 functools.cmp_to_key 实现自定义排序 |
C++ | 4 ms | 11.1 MB | 使用 lambda 表达式实现自定义排序,性能最佳 |
补充说明
代码亮点
- 自定义排序比较函数,直接比较两个数字拼接后的大小
- 对特殊情况(如全是 0 的数组)进行了处理
- 在各个语言中都使用了内置的排序函数,保证了 O(n log n) 的时间复杂度
为什么自定义排序函数能得到正确结果?
对于任意两个数字 a 和 b,我们希望确定它们在最终结果中的相对顺序。有两种可能的排列:a 在前面(即 ab)或 b 在前面(即 ba)。我们需要选择使得结果最大的那种排列。因此,我们直接比较 ab 和 ba 的大小。
这种比较方法是有传递性的:如果 a 应该排在 b 前面,且 b 应该排在 c 前面,那么 a 一定应该排在 c 前面。这确保了我们的排序结果是正确的。
常见错误
- 直接对数字大小进行排序,而不是比较拼接后的结果(例如,3 和 30,直接比较会将 30 排在 3 前面,但正确的顺序应该是 3 在前面)
- 忘记处理特殊情况,如全 0 数组
- 在 Python 中使用
sorted()
函数时没有正确使用functools.cmp_to_key