前言
题目的链接,大家可以先试着去做一下再来看一下思路。179. 最大数 - 力扣(LeetCode)
题目解析
还是老样子,把题目读懂,画出有用信息。
认真看示例,要好好去想一下,特殊示例的情况。
算法原理
字典序
插个小曲,介绍一下字典序。
- 概念:
字典序(LexicographicalOrder),也称为词典序、字母序或词典顺序,是一种基于字符顺序的字符串比较方法。它类似于我们在字典中查找单词的顺序。 - 字符编码基础
字典序依赖于字符的编码系统:
ASCII:‘0’=48, ‘1’=49, …, ‘9’=57, ‘A’=65, ‘B’=66, …, ‘a’=97, ‘b’=98, …
Unicode:包含更多字符,但数字和字母的顺序与ASCII一致。 - 比较规则
字典序的核心规则是从左到右逐字符比较:
比较第一个字符: 如果字符不同,则根据字符的编码值(如ASCII或Unicode)决定顺序;编码值小的字符串排在前面 。
例一: “apple” vs “banana” → “apple” < “banana”(因为 ‘a’<‘b’);如果第一个字符相同:比较第二个字符;依此类推,直到找到不同的字符。
例二:“dat” vs “dog” → “dat” < “dog”(因为’a’<‘o’)所有字符都相同: 较短的字符串排在较长的字符串前面。
例三:“hello” vs “hell” → “hell” <“hello”(相同前缀,较短者优先)
- 示例解析
数字字符串比较:
“2” vs “10” → “10” < “2”(因为 ‘1’<‘2’)
“30” vs “4” → “30” <“4”(因为 ‘3’<‘4’)
“100” vs “2” → “100” < “2”(因为 ‘1’<‘2’)
混合比较:
“file9” vs “file10” → “file10” < “file9”(因为 ‘1’<‘9’)
“item2” vs “item10” → “item10” < “item2”(因为 ‘1’<‘2’)
- 在本代码中的应用
在largestNumber算法中(largestNumber 算法通常指的是如何将一组非负整数重新排列,使得它们拼接后的数字是最大的。),我们使用字典序比较拼接后的字符串为什么有效呢。
//这行代码是自定义排序比较逻辑的核心,它决定了两个字符串在排序时的相对顺序。
//如果 s1 + s2 组成的字符串更大,返回 true,表示 s1 应该排在 s2 前面。
//如果 s2 + s1 更大,返回 false,表示 s2 应该排在 s1 前面。
return s1 + s2 > s2 + s1;
长度相等:s1+s2和s2+s1长度相同(len(s1)+len(s2))
字典序等价数值序:当两个数字字符串长度相同时,字典序大小关系等同于数值大小关系。
- 字典序与数值序的区别
比较方式 | “2” vs “10” | “30” vs “4” | “100” vs “2” |
---|---|---|---|
字典序 | “10” < “2” | “30” < “4” | “100” < “2” |
数值序 | 10 > 2 | 30 > 4 | 100 > 2 |
字典序:基于字符编码从左到右比较.。
数值序:基于实际数值大小比较。
- 在算法中的重要性
在本问题中,使用字典序比较拼接字符串是高效的,因为:
避免了大数计算(拼接后可能超出整数范围) 。
利用字符串比较的内置优化。
当长度相同时,保持数值大小关系。
- 总结
字典序是一种基于字符编码顺序的字符串比较方法:
从左到右逐字符比较。
编码值小的字符排在前。
相同前缀时较短字符串排在前。
在等长数字字符串比较中等价于数值比较。
代码示例
class Solution {
public:
string largestNumber(vector<int>& nums)
{
//优化一下,把所有的整数转化为字符串,我们用字典序去比较,比直接转换整形比较好很多,
vector<string> str;
for(auto x : nums) str.push_back(to_string(x));
//排序,拼接字符串,比较字典序。当两个数字字符串长度相同时,字典序大小关系等同于数值大小关系。
//标准库的 sort 通常使用快速排序,这类比较排序算法的核心是通过多次比较和交换元素位置来达到有序。
sort(str.begin(),str.end(),[](const string &s1, const string &s2)
{
return s1 + s2 > s2 + s1;
});
//提取结果
string ret;
for(auto &s : str) ret += s;
//特殊情况,数组中全是0不能返回"000...",应该返回"0"
if(ret[0] == '0') return "0";
return ret;
}
};
策略证明
证明方法:全序关系
全序关系(Total Order)是指在一个集合X上,满足以下三个条件的二元关系:
完全性:对于集合中的任意两个元素a和b,要么a ≤ b,要么b ≤ a 。
反对称性:如果a ≤ b且b ≤ a,则a = b。
传递性:如果a ≤ b且b ≤ c,则a ≤ c。
全序关系也称为线性序或简单序。一个具有全序关系的集合称为全序集。
例如,实数集上的小于等于关系(≤)就是一个全序关系,因为任意两个实数都可以比较大小。
假设定义了集合{a,b,c,d,e,f,g},我从集合中任意挑选两个元素,如果这两个元素在你定义的比较规则下,满足全序关系的话,我们就说这个集合是可以排序的。
只要满足下面三个性质,就有全序关系。
1.完全性,2.反对称性,3.传递性
我们接下来就要去证明我们算法原理中的自定义排序是否有效。