LeetCode 179. 最大数
1. 解题思路
题目给出一个非负整数数组,要求将所有数字按照某种顺序排列,再拼接成一个尽可能大的整数,最后以字符串形式返回。
最容易想到的方法就是将数组从大到小排序,再依次拼接。
但仔细想想,这样真的能得到最大的结果吗?
例如:
nums = [3, 30, 34, 5, 9]如果按照数字本身的大小排序,显然不一定正确。
虽然 30 大于 9,但把它们拼接起来:
9 + 30 = 930
30 + 9 = 309显然,930 比 309 更大,因此 9 应该排在 30 前面。
那么,直接将数字转换成字符串,再按照字典序从大到小排序呢?
这样也不行。
例如比较 "3" 和 "30",按照普通字典序降序排列,"30" 会排在 "3" 前面。
但实际拼接后:
"3" + "30" = "330"
"30" + "3" = "303"显然,330 > 303,所以 "3" 才应该排在前面。
通过这两个例子可以发现:这道题不能只比较数字本身的大小,而应该比较它们拼接之后的结果。
2. 自定义排序规则
假设现在有两个数字字符串 A 和 B。
它们只有两种拼接顺序:
A + BB + A
既然我们要得到尽可能大的结果,那么只需要比较这两种拼接方式。
如果:
就应该将 A 排在 B 前面。
反之,则将 B 排在前面。
例如:
A = "3"
B = "30"
A + B = "330"
B + A = "303"由于 "330" > "303",因此 "3" 应该排在 "30" 前面。
同样的规则也可以用于更多数字。
假设有三个字符串 A、B、C,通过两两比较发现:
A应该排在B前面。A应该排在C前面。C应该排在B前面。
那么最终顺序就是 ACB。
这样,我们就可以利用排序算法,根据两个元素的拼接结果决定它们的先后顺序。
整体步骤也就明确了:
- 将数组中的所有整数转换成字符串。
- 定义自定义比较函数,比较
x + y和y + x。 - 根据比较结果对字符串数组进行排序。
- 将排序后的字符串依次拼接,得到最终答案。
3. Python 实现
from functools import cmp_to_key
class Solution:
def largestNumber(self, nums: List[int]) -> str:
num_strs = list(map(str, nums))
def compare(x, y):
if x + y > y + x:
return -1
return 1
num_strs.sort(key=cmp_to_key(compare))
res = "".join(num_strs)
if num_strs[0] == "0":
return "0"
return res4. cmp_to_key 的作用
这里有一个需要注意的地方:Python 的 sort() 接收的是 key 参数,而不是直接接收两个元素进行比较的函数。
通常情况下,key 函数只需要根据单个元素生成排序依据。
但这道题不一样。
我们需要同时拿到两个字符串,比较 x + y 和 y + x,才能确定它们的先后顺序。
因此,可以先定义一个比较函数 compare(x, y),再使用 functools 模块中的 cmp_to_key(),将它转换成 sort() 可以使用的排序键。
比较函数的返回值含义如下:
- 小于 0:
x应该排在y前面。 - 大于 0:
x应该排在y后面。 - 等于 0:两个元素在当前比较规则下没有先后之分。
所以,当 x + y > y + x 时,返回 -1,让 x 排在前面。
反过来,如果 x + y < y + x,就应该返回正数,让 y 排在前面。
当两种拼接结果相等时,比较函数应该返回 0。原代码没有单独处理相等的情况,这一点可以进一步完善。
5. 处理全零的特殊情况
最后还需要考虑一个特殊情况:如果数组中的所有数字都是 0,应该如何处理?
例如:
nums = [0, 0, 0]如果直接将排序后的字符串拼接,就会得到:
"000"但题目要求返回的是数字的字符串表示,因此正确答案应该是:
"0"这里不需要专门遍历一遍数组来判断是否全部为零。
因为按照前面的排序规则,如果排序后的第一个字符串就是 "0",说明数组中不存在其他非零数字。
因此,可以直接判断:
if num_strs[0] == "0":
return "0"否则,正常返回拼接后的字符串即可。
6. 复杂度分析
假设数组中有 n 个整数,每个数字转换成字符串后的最大长度为 m。
排序通常需要进行 次比较,而每次比较都需要拼接并比较两个字符串,开销与字符串长度有关。
因此,整体时间复杂度可以表示为:
如果把题目中整数的最大位数视为常数,则可以简化为 。
空间方面,需要额外保存转换后的字符串及排序过程中的辅助数据。
这道题最关键的地方,是将排序依据从数字本身的大小,改成两个数字不同拼接顺序的大小。
只要确定了这个比较规则,就可以交给排序算法处理,而不需要自己枚举所有排列方式。