字典序

一、前言

经常刷算法题的朋友,肯定会频繁看到题目中提到“字典序”这样的字眼。或者在遇到一些看似没有头绪的字符串、排列组合题时,题解往往会甩出一句:“通过字典序规则结合贪心/单调栈即可解决”。

由于很多新手对字典序的底层逻辑了解得不够透彻,导致做题时总会卡在边缘情况上。

二、什么是字典序?

1. 字典序概念

字典序(Dictionary Order),又称字母序(Alphabetical Order)。它的字面含义就是英文单词在纸质字典中的先后顺序。在计算机算法领域,它被扩展成了任意两个字符串或序列之间的大小比较关系

举个最简单的例子,在字典中,apple 肯定排在 banana 前面。当首字母相同时,就去比较第二个字母,比如 account 排在 advanced 之前,以此类推。

2. 深度理解字典序(底层逻辑)

在绝大部分编程语言(C++、Java、Python)中,系统已经默认实现了字符串的比较规则:

  • 逐位比较,遇异即决:比如 "abc" < "acb" < "acbd"。先比较第一位,相同;比较第二位,b < c,此时直接出结果,高位字符具有“一票否决权”
  • 前缀包含,短的优先:如果比较到其中一个字符串结束了都没分出胜负,说明短的是长的前缀,那么长度较短的更小。比如 "acb" < "acbd"

我们习惯了数值比较,认为 2 < 10。但在字典序的世界里,数字也被当成左对齐的字符串来逐位比较! 对数字 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 按照字典序排列,结果为: [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9] (因为第一位是 1 的数字,字典序一定小于第一位是 2 的数字)。

三、常考面试题实战

题型一:下一个排列(LeetCode 31)

题目描述:给定一个整数数组,找出这个数组的“下一个字典序排列”。如果是最后一个排列,则返回字典序最小的排列(即升序排列)。例如,1,2,3 的下一个是 1,3,2解题思路: 这就是典型的“从后往前找波峰”套路:

  1. 从后向前遍历,找到第一个下降的元素 A
  2. 再从后向前,找第一个比 A 大的元素 B
  3. 交换 AB
  4. A 原本位置后面的所有元素反转,使其变成升序(字典序最小)。

C++

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 2;
    // 1. 找第一个下降点
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        i--;
    }
    if (i >= 0) {
        int j = nums.size() - 1;
        // 2. 找第一个比它大的元素
        while (j >= 0 && nums[i] >= nums[j]) {
            j--;
        }
        // 3. 交换
        swap(nums[i], nums[j]);
    }
    // 4. 翻转后面的部分
    reverse(nums.begin() + i + 1, nums.end());
}

题型二:字典序排数(LeetCode 386)

题目描述:给定一个整数 n,按字典序返回范围 [1, n] 内所有整数。要求时间复杂度 O(n),空间复杂度 O(1)。 解题思路: 这题完美诠释了数字的字典序本质。其实这就是一棵十叉树的深度优先搜索(DFS)先序遍历结果! 根节点为空,第一层子节点是 1~9,数字 1 的子节点是 10~19…以此类推。

C++

vector<int> lexicalOrder(int n) {
    vector<int> res;
    int curr = 1;
    for (int i = 0; i < n; i++) {
        res.push_back(curr);
        if (curr * 10 <= n) {
            curr *= 10; // 往下钻(类似 DFS 的走向左子树)
        } else {
            // 退回并向右平移
            while (curr % 10 == 9 || curr + 1 > n) {
                curr /= 10;
            }
            curr++;
        }
    }
    return res;
}

题型三:最大数 / 最小数拼接(经典贪心)

题目描述:给定一组数字,将其拼接起来,求能组成的字典序最小的字符串。 解题思路: 千万不能直接把各个字符串按字典序排序!正确的贪心规则是:对于字符串 a 和 b,比较 a + bb + a 的字典序大小。如果 a + b < b + a,说明 a 应该排在 b 前面。

C++

bool cmp(const string& a, const string& b) {
    return a + b < b + a;
}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇