回溯法:实现

2021-10-10 每日一题 LeetCode

# 0017. 电话号码的字母组合 (opens new window)

题目

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
1
2

示例 2:

输入:digits = ""
输出:[]
1
2

示例 3:

输入:digits = "2"
输出:["a","b","c"]
1
2

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

解析

首先存储每个数字对应的所有可能的字母,然后进行回溯操作。回溯过程中维护一个字符串,表示已有的字母排列,并记录当前回溯位置。每次尝试对应位置数字的所有字母,即可得到完整排列。

代码

    # 0022. 括号生成 (opens new window)

    题目

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

    有效括号组合需满足:左括号必须以正确的顺序闭合。

    解析

    一个合法的括号序列需要满足以下两个条件:

    1. 任意前缀中左括号的数量 \ge 右括号的数量;
    2. 左右括号数量相等。

    因此只要在回溯的同时,记录当前状态已使用的左右括号数,根据使用情况决定下一步状态即可。

    代码

    class Solution {
        vector<string> ans;
    public:
        vector<string> generateParenthesis(int n) {
            dfs(0, 0, n, "");
            return ans;
        }
    
        void dfs(int lc, int rc, int n, string seq) {
            if (lc == n && rc == n) {
                ans.push_back(seq);
                return;
            }
            if (lc < n) dfs(lc + 1, rc, n, seq + "(");
            if (rc < n && lc > rc) dfs(lc, rc + 1, n, seq + ")");
        }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    # 0037. 解数独 (opens new window)

    题目

    编写一个程序,通过填充空格来解决数独问题。

    数独的解法需 遵循如下规则:

    • 数字 1-9 在每一行只能出现一次。
    • 数字 1-9 在每一列只能出现一次。
    • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

    数独部分空格内已填入了数字,空白格用 '.' 表示。

    解析

    使用布尔数组记录行、列、3x3 宫格内的数字是否存在,每次尝试对应位置所有可能的数字,遍历到最后一个位置即可得到正确答案。

    代码

    class Solution {
        bool rows[9][9], cols[9][9], cells[3][3][9];
    public:
        void solveSudoku(vector<vector<char>>& board) {
            memset(rows, 0, sizeof(rows));
            memset(cols, 0, sizeof(cols));
            memset(cells, 0, sizeof(cells));
    
            // 将已有数字初始化进数组
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    if (board[i][j] == '.') continue;
                    int t = board[i][j] - '1';
                    rows[i][t] = cols[j][t] = cells[i / 3][j / 3][t] = true;
                }
            }
    
            dfs(board, 0, 0);
        }
    
        bool dfs(vector<vector<char>>& board, int x, int y) {
            // 到达行末、切换至下一行
            if (y == 9) x++, y = 0;
    
            // 已处理完最后一行,返回
            if (x == 9) return true;
    
            // 当前格子有数组,跳过
            if (board[x][y] != '.') return dfs(board, x, y + 1);
    
            for (int i = 0; i < 9; i++) {
                // 如果行、列、九宫格内重复,跳过
                if (rows[x][i] || cols[y][i] || cells[x / 3][y / 3][i]) continue;
                
                rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = true;
                board[x][y] = '1' + i;
                
                if (dfs(board, x, y + 1)) return true;
                
                board[x][y] = '.';
                rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = false;
            }
    
            return false;
        }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46

    # 0140. 单词拆分 II (opens new window)

    题目

    给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

    注意:词典中的同一个单词可能在分段中被重复使用多次。

    解析

    DFS 枚举字典中出现的单词。

    代码

    class Solution {
        unordered_set<string> st;
        vector<string> ans;
    public:
        vector<string> wordBreak(string s, vector<string>& wordDict) {
            st = unordered_set<string>(wordDict.begin(), wordDict.end());
            dfs(s, 0, "");
            return ans;
        }
    
        void dfs(string &s, int idx, string cur) {
            if (idx == s.length()) {
                ans.push_back(cur);
                return;
            }
            for (int i = idx; i < s.length(); i++) {
                string word = s.substr(idx, i - idx + 1);
                if (st.count(word)) {
                    dfs(s, i + 1, !cur.empty() ? cur + " " + word : word);
                }
            }
        }
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    # 0282. 给表达式添加运算符 (opens new window)

    题目

    解析

    代码

    # 0320. 列举单词的全部缩写 (opens new window)

    题目

    解析

    代码

    # 0401. 二进制手表 (opens new window)

    题目

    解析

    代码

    # 0465. 最优账单平衡 (opens new window)

    题目

    解析

    代码

    # 0473. 火柴拼正方形 (opens new window)

    题目

    解析

    代码

    # 0491. 递增子序列 (opens new window)

    题目

    解析

    代码

    # 0526. 优美的排列 (opens new window)

    题目

    解析

    代码

    # 0582. 杀掉进程 (opens new window)

    题目

    解析

    代码

    Last Updated: 2023-01-28 4:31:25