【算法】回溯算法套路(dfs)

解决一个回溯问题,本质是一个决策树的遍历

三要素:
1.路径:已经做出的选择
2.选择列表:当前可以做的选择
3.结束条件:到达决策树底层,不用再做选择

算法框架

result = []
def backTrack(path, choiceList):
	if 满足结束条件:
		result.push_back(path)
		return
	for choice in choiceList:
		做选择
		backTrack(path, choiceList)
		撤销选择

注意for循环中的递归
在递归前做选择
在递归后撤销选择

  1. Permutations
    交换dfs
class Solution {
public:
    void backtrack(vector<int>& nums, vector<vector<int>>& res,int left, int right) {
        if(left == right) {
            res.push_back(nums);
            return;
        }
        for(int i = left; i < right; ++i) {
            // do choice
            swap(nums[left], nums[i]);
            backtrack(nums, res, left + 1, right);
            // withdraw choice
            swap(nums[left], nums[i]);
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        backtrack(nums, res, 0, nums.size());
        return res;
    }
};

傻瓜式完全照搬模板

class Solution {
public:
    void backtrack(vector<vector<int>>& res, vector<int>& nums, vector<int>& vis, vector<int>& path) {
        if(path.size() == nums.size()) {
            res.push_back(path);
            return;
        }
        for(int i = 0; i < nums.size(); ++i) {
            if(vis[i])
                continue;
            path.push_back(nums[i]);
            vis[i] = 1;
            backtrack(res, nums, vis, path);
            path.pop_back();
            vis[i] = 0;
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> vis(nums.size());
        vector<vector<int>> res;
        vector<int> path;
        backtrack(res, nums, vis, path);
        return res;
    }
};

这种时间反而是0
其中result和visit都可以是全局变量
通过传引用的方式实现
path的逻辑结构是栈

  1. N-Queens
    笨比版面向过程编程
class Solution {
public:
    int length = 0;
    string nthQueen(int n) { // 得到一个字符串
        string s(length, '.');
        s[n] = 'Q';
        return s;
    }

    bool inbound(int i, int j) {
        if(i < length && i >= 0) {
            if(j < length && j >= 0) 
                return true;
        }
        return false;
    }

    bool legal(vector<string>& path, int i, int j) {
        // 检测斜的就可
        for(int r = i - 1, c = j - 1; inbound(r, c); ) {
            if(path[r][c] == 'Q')
                return false;
            --r;
            --c;
        }
        for(int r = i - 1, c = j + 1; inbound(r, c); ) {
            if(path[r][c] == 'Q')
                return false;
            --r;
            ++c;
        }
        return true;
    }

    void backtrack(vector<vector<string>>& res, vector<string>& path, vector<int>& vis) {
        if(path.size() == length) {
            res.push_back(path);
            return;
        }
        for(int i = 0; i < length; ++i) {
            if(vis[i])
                continue;
            //要放入".Q..."时 i为1 应为棋盘的第i列
            if(!legal(path, path.size(), i)) //col=i, row=path.size()
                continue;
            path.push_back(nthQueen(i));
            vis[i] = 1;
            backtrack(res, path, vis);
            path.pop_back();
            vis[i] = 0;
        }
    }

    vector<vector<string>> solveNQueens(int n) {
        length = n;
        vector<int> vis(n, 0); // 0 represents fresh
        vector<vector<string>> res;
        vector<string> path;
        backtrack(res, path, vis);
        return res;
    }
};

终极简化版

class Solution {
public:
    vector<vector<string>> res;
    vector<string> ans;
    vector<bool> col, d, ud;
    int len;
    vector<vector<string>> solveNQueens(int n) {

        len = n;
        col = vector<bool> (n);
        d = vector<bool> (2 * n);
        ud = vector<bool> (2 * n);
        ans = vector<string> (n, string(n, '.'));
        dfs(0);
        return res;
    }
    void dfs(int u) // 即将要确定第u 行
    {
        if(u== len)
        {
            res.push_back(ans);
            return;
        }
        for(int i=0;i< len;i++) // 第i列是否可以放入path
        {
            if(!col[i]&&!d[i+u]&&!ud[i-u+ len])
            {
                col[i] = d[i+u]=ud[i-u+ len]=true;
                ans[u][i]='Q';
                dfs(u+1);
                ans[u][i]='.';
                col[i] = d[i+u]=ud[i-u+ len]=false;
            }
        }
    }
};

每次加一行,不用考虑同行的情况
注意右对角线上i+j不变,左对角线上i-j+n不变
这两个vec初始要设置成长度2n

  1. Course Schedule
class Solution {
public:
    bool dfs(vector<vector<int>>& adjacency, vector<int>& flag, int i) {
        if(1 == flag[i])
            return false;
        if(-1 == flag[i])
            return true;
        flag[i] = 1;
        for(int j: adjacency[i])
            if(!dfs(adjacency, flag, j))
                return false;
        flag[i] = -1;
        return true;
    }

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> adjacency(numCourses);
        vector<int> flag(numCourses, 0);
        for(int i = 0; i < prerequisites.size(); ++i) {
            adjacency[prerequisites[i][1]].push_back(prerequisites[i][0]);
        }
        for(int i = 0; i < numCourses; ++i)
            if(!dfs(adjacency, flag, i))
                return false;
        return true;
    }
};

通过dfs判断拓扑排序
要先声明adjacency长度为numcourse,不然无法访问adjacency[]

  1. Restore IP Addresses
class Solution {
public:
    // 09点49分
    vector<string> ans;
    vector<int> ipSeg;
    void dfs(string& s, int start, int segNum) {
        
        if(start == s.size()) {// s用完
            if(segNum == 4) {
            // ipSeg => string
                string ip = "";
                for(int i = 0; i < 4; ++i) {
                    ip += to_string(ipSeg[i]);
                    // string 只能加不能-
                    if(i < 3)
                        ip += '.';
                }
                ans.push_back(ip);
                return;
            } else
                return;
        }
        if(segNum == 4)
            return;
        // 0 的特殊情况要单独考虑
        // 0一定单独存在
        if(s[start] == '0') {
            ipSeg.push_back(0);
            dfs(s, start + 1, segNum + 1); 
            // dfs后一定要撤销决策!!
            ipSeg.pop_back();
            // 唯一的可行方法
            // 不用向后遍历
            return;
        }
        int addr = 0;
        for(int i = start; i < s.size(); ++i) {
            addr *= 10;
            addr += s[i] - '0';
            // 这里不能有=0的情况
            //否则00的存在 也会被归为0 影响s长度的判断
            if(addr > 0 && addr <= 0xff) {
                ipSeg.push_back(addr);
                dfs(s, i + 1, segNum + 1);
                ipSeg.pop_back();
            }else{
                break; // 超过范围也不用再考虑后续
            }
        }
    }

    vector<string> restoreIpAddresses(string s) {
        dfs(s, 0, 0);
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值