美文网首页
剪枝回溯问题再记

剪枝回溯问题再记

作者: 东方胖 | 来源:发表于2024-03-05 23:45 被阅读0次

树形结构的数据都可以使用深度优先搜索进行遍历。
有些问题的困难在于,将问题转换成树形结构的问题。
例如求集合的子集合。

有一种树形搜索 会在几个 dfs(深度优先搜索的缩写)之后膨胀到很大的规模 ,但是大部分的结果实际上可以通过逻辑判断删除树枝,这种检索方式成为“剪枝+回溯”

剪枝是在适当的时候排除掉不合适的子树,减少了计算过程,回溯则是指在树遍历过程中,遇到剪枝遍历过程回到上一个分叉的过程。

递归本身含有回溯,所以使用递归实现回溯几乎是最合适的方式,当然也可以用栈这样的数据结构来实现。栈具有先进后出的特性,但发生剪枝需要回溯时,用出栈的方式回到上一个分叉点。

问题一:如何将一个集合的幂集合列出

集合 \{a_1, a_2, ... , a_n\} a_i i = 1, 2, ..., n 各不相同
的子集有 2^n
使用回溯的方法

问题二 把一个数字字符串还原成IP地址

数字字符串的点号因为某种原因被抹去了,例如 "255123" 还原成合格的 IP 地址 "25.51.2.3" 是个合法的结果,但很明显不止一种,"2.5.5.123" 也是一种可能
如何不遗漏地罗列所有的可能结果
这是典型的回溯剪枝问题

我们可以把问题转换成一棵树
起初第一段可以是 2,25 255这样从根到第一层,第一层有三个子树,沿着最左边的 2 这个子树,继续将字符串分叉,得到 5, 55, 551 三棵子树,但是 551 是不合法的段,因为它大于 255,因此从这个 551 的节点开始后面的子树都可以芟除。
继续第三和第四层 ...
以下是个草图,描述了如何将字符串 "255123" 逐步拆成一棵回溯树,其中 × 表示剪枝,该树枝生成的结果肯定不合法

回溯树
class RestoreIP {
public:
    vector<string> restoreIpAddresses(string s) {
        std::string ans;
        backTrack(0, 0, s, ans);
        return result;
    }

    void backTrack(int index, int depth, const string & s, string currentStr) {
        
        if (index == s.size() && depth == 4) {
            result.push_back(currentStr);
            return;
        } 

        if (depth > 4 || index == s.size() || (4 - depth) * 3 < s.size() - index) {
            return; 
        }
        
        for (int i = 0; i < 3; ++i) {
            if (i + index >= s.size()) {
                return;
            }
            std::string sec = s.substr(index, i + 1);
            int secInt = std::stoi(sec);
            if (sec[0] == '0' && sec.size() > 1 ) {
                continue;
            } else if (secInt >= 0 && secInt <= 255) {
                if (depth > 0) {
                    backTrack(index + sec.size(), depth + 1, s, currentStr + "." + sec);
                } else {
                    backTrack(index + sec.size(), depth + 1, s, currentStr + sec);
                }
            }
        }
        
    }

    vector<string> result;
};

用一个可变数组来存储合格的结果
剪枝逻辑:if (depth > 4 || index == s.size() || (4 - depth) * 3 < s.size() - index) 表示的意思是 深度超过 4 但是还有字符,或者余下的字符无论如何都会有剩余,则剪枝。比如已经选定了前两节是 10.20 余下七个字符,说明无论怎样都不可复原,此时提前剪枝,省去很多计算。

currentStr 是一个追踪当前未完成的 IP 串的变量,一旦检测到它满足 IP 地址的特征,那么就会添加到 可行结果中,否则回溯到上一层,由于 currentStr 是一个传值的变量,回溯到上一层会让 currentStr 移除掉不满足条件的那一节。
例如10.20 如果下一节是300,那么 currentStr = 10.20.300, 检测300大于255 不符合要求,回溯到上一层时,currentStr = 10.20,假设三种可能得子树都不满足条件,那么继续回溯到 currentStr = 10

相关文章

  • 八皇后问题

    问题的类别是回溯(backtrace). 回溯通常用递归实现。回溯中注意边界问题,避免越界。同时,剪枝可以减少ca...

  • 数独sukudo

    问题的类别是回溯。 注意边界、剪枝的问题。 具体的问题描述看leetcode这里 需要特别注意的是,leetcod...

  • DFS+记忆化搜索

    dfs 和 bsf 和 回溯回溯是有剪枝的dfshttp://blog.csdn.net/fightforyour...

  • 回溯法

    概述 Backtracking 回溯法实现 => DFS + 剪枝(跳过对某些一定不是解的子树) 可以把问题所有的...

  • 深度搜索与回溯

    深度搜索与回溯法的区别 回溯法 = 深搜 + 剪枝。一般大家用深搜时,或多或少都会剪枝。深搜一般用递归实现,比较简...

  • 深搜 排列组合

    1.DFS 2.回溯 回溯法是剪枝的暴力法。 剪枝函数包括两类:1.使用约束函数,剪去不满足约束条件的路径;2.使...

  • 22、Generate Parentheses

    题设 要点 回溯 backtracking 剪枝 明显地,可以用回溯法来解。判断条件为: 回溯法在每一步,本来应该...

  • 暴力搜索 | DFS 回溯剪枝

    「算法笔记」「上机指南」买来一个多月了。还是在自己胡乱coding,今天本打算看一下我并不熟的最小堆emmmmmm...

  • 回溯

    回溯:递归的一种,或者说是通过递归这种代码结构来实现回溯这个目的。回溯法可以被认为是一个有过剪枝的DFS过程。把问...

  • 算法

    算法应用BFS广度优先搜索DFS深度优先搜索回溯法(DFS+剪枝)部分和问题分治法快速排序、归并排序动态规划背包问...

网友评论

      本文标题:剪枝回溯问题再记

      本文链接:https://www.haomeiwen.com/subject/hzglzdtx.html