LeetCode刷题笔记 - 回溯


回溯算法 Backtracking

回溯算法是聪明的枚举。其本质仍然是暴力枚举,所以除非n很小,否则很容易TLE。

回溯适用于那些很难的问题,如

  • 组合问题:N个数里面按一定规则找出k个数的集合

  • 切割问题:一个字符串按一定规则有几种切割方式

  • 子集问题:一个N个数的集合里有多少符合条件的子集

  • 排列问题:N个数按一定规则全排列,有几种排列方式

  • 棋盘问题:N皇后,解数独等等

伪代码如下

// Backtracking
void backtracking(param) {
    if (should_end) {
        save result;
        return;
    }

    for (elem : current layer) {
        process node
        backtracing(next_node, path)
        backtrace, undo result
    }
}

回溯算法可以抽象为在N叉树上DFS。

77. Combinations

One trick to make life easier is to store vectors (path and result) as the class’s member.

class Solution {
private:
    vector<int> nums;
    vector<int> path;
    vector<vector<int>> results;
    int n;
    int k;

    void backtracking(int rem, int startIdx) {
        if (rem == 0) {
            results.push_back(path);
            // for (const auto num : path) {
            //     cout << num << ", ";
            // }
            // cout << endl;
            return;
        }
        // Optimization:
        // If nums[i:].size() < rem, won't have enough 
        element to construct path, so skip
        for (int i = startIdx; i <= nums.size() - rem; i++) {
            int num = nums[i];
            path.push_back(num);
            backtracking(rem-1, i+1);
            path.pop_back();
        }
    }   

public:
    vector<vector<int>> combine(int n, int k) {
        nums = vector<int>(n);
        
        for (int i = 0; i < n; i++) {
            nums[i] = i+1;
        }    
        backtracking(k, 0);
        return results;
    }
};

216. Combination Sum III

Done.