一 实现思路
1 思维层面
1 这道题的难点是能相出 a,b 之间的比较规则f,以及怎么证明 f具体传递性,即 如果 aΘb, bΘc, 那么 aΘc
2 如果f传递性成立,那么就意味着我们可以按序 两两比较元素,而不用暴力比较 所有其他成员
3 f的传递性证明如下:
S1 定义 x⊕y
定义一种非负整数集合上的二元运算,记作 ⊕,它表示将两个数拼接后的结果
令 s(x) 表示大于非负整数 x 的最小的十的整次幂(即当 x>0 时 s(x)=10 ^ (log10x + 1),
特别地,s(0)=10)
那么 x⊕y = x×s(y)+y, 比如 12⊕456 = 12*1000 + 456 = 12456
S2 定义一个非负整数集合上的二元关系,记作 Θ
当一个数x 排在数y 前面更优时,(即 x⊕y≥y⊕x),我们认为 xΘy。
S3 证明 Θ关系的 传递性:即 如果 aΘb 且 bΘc,那么 aΘc 也成立。传递性证明如下:
由 aΘb 且 bΘc 可知:
(p1): a × s(b)+b ≥ b×s(a)+a
(p2): b× s(c)+c ≥ c×s(b)+b
分别给 p1 和 p2 左右2边 加上 -a-b 和 -b-c,移项整理得:
(p3): a × (s(b)−1) ≥ b × (s(a)−1)
(p4): b × (s(c)−1) ≥ c × (s(b)−1)
两式的左右两边均非负,因此由两式相乘可得:
(p5): a × b × (s(b)−1) × (s(c)−1) ≥ b × c × (s(a)−1) × (s(b)−1)
不等式两边都有 b,根据 b 是否为 0 分类讨论:
-
当 b = 0 时:
- 将 b = 0 代入(p2)的 bΘc 可知:c ≥ c×10,即 c = 0
- 当 c = 0 时,有 a × s(c)+c ≥ c × s(a) + a(因为10a >= a必然成立),恰符合 aΘc 的定义
-
当 b > 0 时:
- 此时 b × (s(b)−1)) > 0 必然成立,所以P5的 不等式两边同时除以 b×(s(b)−1))
- 化简得:a × (s(c)−1) ≥ c × (s(a)−1),恰符合 aΘc 的定义。
综上,有 aΘc
4.1 证明了传递性后,只是说明这个排序规则是合理的,确保了可以使用排序算法,原因是 满足了“全序关系”的3个性质
-
反身性 a Θ a:
- a⊕a ≥ a⊕a ==> a×s(a) + a ≥ a×s(a) + a, 显然是成立的
-
传递性 若 aΘb 且 bΘc,则aΘc:
- 上述已证明
-
完全性:对任意a,b,必有 aΘb 或 bΘa:
- 即 要么 a⊕b ≥ b⊕a,要么 b⊕a > a⊕b,显然成立,因为a,b 都是非负整数,相关运算结果 必然是可比较的
这三个性质确保了可以使用任何比较排序算法
但是 还需要证明:按这个规则排序得到的序列 就是 最优解
4.2 反证法步骤:
假设: 存在一个序列P是最优解(能得到最大数)
但P不完全遵守规则Θ
推理:
由于P不完全遵守规则Θ
必然存在至少一对相邻元素(s1, s2),其中s1在s2前面
但它们违反规则Θ,即 (k1)s2⊕s1 > s1⊕s2
关键性质:
对于任意序列[..., x, y, ...],整个序列拼接后的值可以表示为:[...⊕x⊕y⊕...]
当且仅当x,y相邻时,交换x,y的结果等价于:[...⊕y⊕x⊕...]
矛盾推导:
设序列P是最优解 = [..., s1, s2, ...],
将s1,s2交换后得到新序列P' = [..., s2, s1, ...]
由于(k1)s2⊕s1 > s1⊕s2,且s1,s2相邻,其他元素位置不变
所以P'拼接后的值 > P拼接后的值
这与P是最优解矛盾
结论: 原假设不成立
因此最优解必须完全遵守规则Θ
即:按规则Θ排序得到的序列就是最优解
2 JS里Array.sort() 的排序规则
1 遵循以下规则:
- 如果返回值 < 0,表示 a 排在 b 前面, 如 (a,b) => a-b是 升序排列
- 如果返回值 > 0,表示 b 排在 a 前面, 如 (a,b) => b-a是 降序排列
- 如果返回值 = 0,保持原来的顺序
3 参考实现
二 代码实现
1 方法1: 排序 时间复杂度:O(nlognlogm); 空间复杂度: O(logn)
function largestNumber(nums: number[]): string {
// 数学排序规则(这里^表示的数学上的幂运算,而不是异或符):
// b * 10 ^ a + a - (a * 10 ^ b.length + b) 降序排列
// 易错点1:由于此题是大数比较,直接用数字比较可能会溢出,所以这里用字符串比较
// 这里期望是降序,所以字符串的返回值期望是正数
nums.sort((a,b) => +`${b}${a}` - +`${a}${b}`)
// 易错点2:如果排序后的数组第一个元素是0,有可能存在前导0情况
// 所以这里直接返回0
if (nums[0] === 0) return '0'
return nums.join('')
};