LeetCode刷题之回溯算法

LeetCode之搜索专题

1.回溯算法

回溯方法基本套路:

result = [];
def backtrack():
    if 满足结束条件:
    	result.add(路径)
        return
    for 选择 in 选择列表:
    	做选择
        backtrack(路径, 选择列表)
        撤销选择

1.LeetCode之[N皇后问题]

  • 解题思路

  • 代码实现

    1.递归解法(回溯方法基本套路)

    /*
     * @lc app=leetcode.cn id=51 lang=cpp
     *
     * [51] N 皇后
     */
    
    // @lc code=start
    class Solution {
    public:
        vector<vector<string>> res;
    
        vector<vector<string>> solveNQueens(int n) {
            vector<string> board(n, string(n, '.'));
            backtrace(board, 0);
            return res;
        }
    
        void backtrace(vector<string> &board, int row) {
            if (row == board.size()) {
                res.push_back(board);
                return;
            }
    
            int n = board[row].size();
            for (int col = 0; col < n; col++) {
                if (!isValid(board, row, col)) {
                    continue;
                }
                board[row][col] = 'Q';
                backtrace(board, row + 1);
                board[row][col] = '.';
            }
        }
    
        //是否能在board[row][col]放置皇后
        bool isValid(vector<string> &board, int row,int col) {
            int n = board.size();
    
            //检查列是否有皇后冲突
            for (int i = 0; i < n; i++) {
                if (board[i][col] == 'Q') {
                    return false;
                }
            }
    
            //检查右上方是否有冲突
            for (int i = row - 1, j = col + 1; i >= 0 && j < board[0].size(); i--, j++) {
                if (board[i][j] == 'Q') {
                    return false;
                }
            }
    
            //检查左上方是否有皇后冲突
            for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--,j--) {
                if (board[i][j] == 'Q') {
                    return false;
                }
            }
            return true;
        }
    };
    // @lc code=end
    

    2.非递归解法(回溯方法)

    class Solution {
    public:
    
        vector<vector<string>> res;
        int a[10];
    
        vector<vector<string>> solveNQueens(int n) {
            vector<string> nums(n,string(n,'.'));
            int k = 0;
            a[k] = 0;
            while (k >= 0) {
                if (k < n && a[k] < n) {
                    if (isValid(nums, k ,a[k])) {
                        nums[k][a[k]] = 'Q';
                        k++;
                        a[k] = 0;
                    } else {
                        a[k]++;
                    }
                } else {
                    if (k >= n) {
                        res.push_back(nums);
                    }
                    k--;
                    if (k < 0) break;
                    nums[k][a[k]] = '.';
                    a[k]++;
                }
            }
            return res;
        }
    
        bool isValid(vector<string> &board, int row, int col) {
            int n = board.size();
    
            //检查列是否有皇后冲突
            for (int i = 0; i < n; i++) {
                if (board[i][col] == 'Q') {
                    return false;
                }
            }
    
            //检查右上方是否有冲突
            for (int i = row - 1, j = col + 1; i >= 0 && j < board[0].size(); i--, j++) {
                if (board[i][j] == 'Q') {
                    return false;
                }
            }
    
            //检查左上方是否有皇后冲突
            for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--,j--) {
                if (board[i][j] == 'Q') {
                    return false;
                }
            }
            return true;
        }
    };
    

2.LeetCode之46全排列问题

  • 解题思路

  • 代码实现

    1.回溯算法之递归

    /*
     * @lc app=leetcode.cn id=46 lang=cpp
     *
     * [46] 全排列
     */
    
    // @lc code=start
    class Solution {
    public:
        vector<vector<int>> res;
    
        vector<vector<int>> permute(vector<int>& nums) {
            vector<int> track;
            backtrace(nums, track);
            return res;
        }
    
        void backtrace(vector<int> nums, vector<int> track) {
            if(track.size() == nums.size()) {
                res.push_back(track);
                return;
            }
    
            for (int i = 0; i < nums.size(); i++) {
                if (find_element(track, nums[i])) {
                    continue;
                }
                track.push_back(nums[i]);
                backtrace(nums, track);
                track.pop_back();
            }
        }
    
        bool find_element(vector<int> num, int n) {
            for (const auto & d : num) {
                if(n == d) return true;
            }
            return false;
        }
    };
    // @lc code=end
    

    2.交换元素之递归

    class Solution {
    public:
        vector<vector<int>> res;
    
        vector<vector<int>> permute(vector<int>& nums) {
            int left = 0, right = nums.size() - 1;
            fullArr(nums, left, right);
            return res;
        }
    
        void fullArr(vector<int> &nums, int left, int right) {
            if (left == right) {
                res.push_back(nums);
                return;
            }
    
            for (int i = left; i <= right; i++) {
                swap(nums, left, i);
                fullArr(nums, left + 1, right);
                swap(nums, left, i);
            }
        }
    
        void swap(vector<int> &nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    };
    

    3.回溯非递归算法

    class Solution {
    public:
        vector<vector<int>> res;
        int a[7] = { 0 };
    
        void swap(vector<int>& nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    
        bool isValid(vector<int>& nums, int n) {
            for (const auto& d : nums) {
                if (d == n) return false;
            }
            return true;
        }
    
        vector<vector<int>> permute(vector<int>& nums) {
            vector<int> temp;
            int len = nums.size();
            int k = 0;
            while (k >= 0) {
                if (k < len && a[k] < len) {
                    if (isValid(temp, nums[a[k]])) {
                        temp.push_back(nums[a[k]]);
                        k++;
                        a[k] = 0;
                    }
                    else {
                        a[k]++;
                    }
                }
                else {
                    if (k == len) {
                        res.push_back(temp);
                    }
                    k--;
                    if (k < 0) break;
                    temp.pop_back();
                    a[k]++;
                }
            }
            return res;
        }
    };
    

3.LeetCode之698划分为K个相等的子集

  • 解题思路

    • 回溯加经典递归

      这里有两种思路,我们把数字分成K个和相等的集合,可以将K个集合看成是K个桶,然后我们需要将将数字放到桶里面。这里有两种视角,一种是以数字的角度去看待,遍历每一个数字,判断应该放在哪一个桶里。或者以桶的角度去看,遍历每一个桶,判断哪些数字应该放在桶里。

  • 代码实现

    1.以数字的角度去看待

    /*
     * @lc app=leetcode.cn id=698 lang=cpp
     *
     * [698] 划分为k个相等的子集
     */
    
    // @lc code=start
    class Solution {
    public:
        bool canPartitionKSubsets(vector<int>& nums, int k) {
            if (k > nums.size()) return false;
            int sum = 0;
            for (auto i : nums) {
                sum += i;
            }
            if (sum % k != 0) return false;
            int target = sum / k;
            vector<int> bucket(k);
            int index = 0;
            sort(nums.begin(), nums.end(), [](int a,int b){
                return a > b;
            });
            return backtrack(nums, index, bucket, target);
        }
    
        bool backtrack(vector<int> &nums, int index, vector<int> &bucket, int target) {
            if (index == nums.size()) {
                for (auto n : bucket) {
                    if (n != target) {
                        return false;
                    }
                }
                return true;
            }
    
            for (int i = 0; i < bucket.size(); i++) {
                if (bucket[i] + nums[index] > target) {
                    continue;
                }
                bucket[i] += nums[index];
    
                if(backtrack(nums, index + 1, bucket, target)) {
                    return true;
                }
    
                bucket[i] -= nums[index];
            }
            return false;
        }
    };
    // @lc code=end
    

    2.以桶的角度去看待

    class Solution {
    public:
        bool canPartitionKSubsets(vector<int>& nums, int k) {
            if (k > nums.size()) {
                return false;
            }
            int sum = 0;
            for (auto i : nums) {
                sum += i;
            }
            if (sum % k != 0) {
                return false;
            }
            int target = sum /k;
            vector<bool> used(nums.size(),false);
            return backtrack(k, 0, 0, used, target, nums);
        }
    
        bool backtrack(int index, int start, int bucket,vector<bool> &used, int target, vector<int> &nums) {
            if (index == 0) {
                return true;
            }
            if (bucket == target) {
                return backtrack(index - 1, 0, 0, used, target, nums);
            }
    
            for (int i = start; i < nums.size(); i++) {
                if (used[i]) {
                    continue;
                }
                if (nums[i] + bucket > target) {
                    continue;
                }
                used[i] =true;
                bucket += nums[i];
                if (backtrack(index , i + 1, bucket, used, target, nums)) {
                    return true;
                }
                used[i] = false;
                bucket -= nums[i];
            }
            return false;
        }
    };
    

4.LeetCode之78子集

  • 解题思路

    • 利用数学归纳法的思想
    • 回溯求解
  • 代码实现

    1.利用数学归纳法

    class Solution {
    public:
        vector<vector<int>> subsets(vector<int>& nums) {
            if (nums.empty()) return {{}};
            int n = nums.back();
            nums.pop_back();
    
            vector<vector<int>> res = subsets(nums);
    
            int size = res.size();
            for (int i = 0; i < size; i++) {
                res.push_back(res[i]);
                res.back().push_back(n);
            }
            return res;
        }
    };
    

    2.回溯求解(画出决策树)

    /*
     * @lc app=leetcode.cn id=78 lang=cpp
     *
     * [78] 子集
     */
    
    // @lc code=start
    class Solution {
    public:
        vector<vector<int>> res;
    
        vector<vector<int>> subsets(vector<int>& nums) {
            vector<int> track;
            backtrack(nums, 0, track);
            return res;
        }
    
        void backtrack(vector<int> &nums, int start, vector<int> &track) {
            res.push_back(track);
    
            for (int i = start; i < nums.size(); i++) {
                track.push_back(nums[i]);
                backtrack(nums, i+1, track);
                track.pop_back();
            }
        }
    };
    // @lc code=end
    

5.LeetCode之77组合

  • 解题思路

    • 回溯(想象出决策树,利用回溯进行深度优先遍历)
  • 代码实现

    /*
     * @lc app=leetcode.cn id=77 lang=cpp
     *
     * [77] 组合
     */
    
    // @lc code=start
    class Solution {
    public:
        vector<vector<int>> res;
    
        vector<vector<int>> combine(int n, int k) {
            vector<int> track;
            vector<int> nums;
            for (int i = 1; i <= n; i++) {
                nums.push_back(i);
            }
            backtrack(nums, 0, 0, k, track);
            return res;
        }
    
        void backtrack(vector<int> &nums, int k, int start, int current, vector<int> &track) {
            if (k == current) {
                res.push_back(track);
                return;
            }
    
            for (int i = start; i <nums.size(); i++) {
                track.push_back(nums[i]);
                backtrack(nums, k+1, i + 1, current, track);
                track.pop_back();
            }
        }
    };
    // @lc code=end
    
    
posted @ 2021-11-22 15:30  IU_UI  阅读(93)  评论(0编辑  收藏  举报