回溯算法 DFS深度优先搜索 (递归与非递归实现)

回溯法是一种选优搜索法(试探法),被称为通用的解题方法,这种方法适用于解一些组合数相当大的问题。通过剪枝(约束+限界)可以大幅减少解决问题的计算量(搜索量)。

基本思想

将n元问题P的状态空间E表示成一棵高为n的带权有序树T,把在E中求问题P的解转化为在T中搜索问题P的解。

深度优先搜索(Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。 --from wiki

实现方法

1、按选优条件对T进行深度优先搜索,以达到目标。

2、从根结点出发深度优先搜索解空间树

3、当探索到某一结点时,要先判断该结点是否包含问题的解

如果包含,就从该结点出发继续按深度优先策略搜索

否则逐层向其祖先结点回溯(退回一步重新选择)

满足回溯条件的某个状态的点称为“回溯点”

4、算法结束条件

求所有解:回溯到根,且根的所有子树均已搜索完成

求任一解:只要搜索到问题的一个解就可以结束

遍历过程

回溯算法 DFS深度优先搜索 (递归与非递归实现)

典型的解空间树 第一类解空间树:子集树

当问题是:从n个元素的集合S中找出满足某种性质的子集时相应的解空间树称为子集树,例如n个物品的0/1背包问题。

这类子集树通常有2^n个叶结点

解空间树的结点总数为2^(n+1) - 1

遍历子集树的算法需Ω(2^n)计算时间

第二类解空间树:排列树

当问题是:确定n个元素满足某种性质的排列时相应的解空间树称为排列树,例如旅行商问题。

DFS搜索在程序中可以两种方式来实现,分别是非递归方式递归方式。前者思路更加清晰,便于理解,后者代码更加简洁高效。

非递归实现

非递归实现需要借助堆栈(先入后出,后入先出),在C++中使用stack容器即可。

问题

若给定一个序列,需要找到其中的一个子序列,判断是否满足一定的条件。下面将程序实现DFS对子序列的搜索过程。

实现步骤:

1、首先将根节点放入堆栈中。

2、从堆栈中取出第一个节点,并检验它是否为目标。

如果找到目标,则结束搜寻并回传结果。

否则将它某一个尚未检验过的直接子节点加入堆栈中。

3、重复步骤2。

4、如果不存在未检测过的直接子节点。

将上一级节点加入堆栈中。

重复步骤2。

5、重复步骤4。

6、若堆栈为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。

C++代码 /********************************************************************* * * Ran Chen <wychencr@163.com> * * Back-track algorithm (by DFS) * *********************************************************************/ #include <iostream> #include <vector> #include <stack> using namespace std; class Node { public: int num; // 节点中元素个数 int sum; // 节点中元素和 int rank; // 搜索树的层级 int flag; // 0表示子节点都没访问过,1表示访问过左节点,2表示访问过左右节点 vector <int> path; // 节点元素 Node(); Node(const Node & nd); }; // 默认构造函数 Node::Node() { num = 0; sum = 0; rank = 0; flag = 0; // path is empty } // 复制构造函数 Node::Node(const Node & nd) { num = nd.num; sum = nd.sum; rank = nd.rank; flag = nd.flag; path = nd.path; } // ----------------------------------------------------------------- void DFS(const vector <int> & deque) { stack <Node *> stk; // 存储节点对应的指针 stack <Node *> pre_stk; // 存储上一级节点(回溯队列) Node * now = new Node; // 指向当前节点 Node * next = NULL; // 指向下一个节点 Node * previous = NULL; // 指向上一个节点 while (now) { if (now->rank < deque.size() && (now->flag == 0)) { // 左叶子节点,选择当前rank的数字 next = new Node(*now); next->num++; next->sum += deque[next->rank]; next->path.push_back(deque[next->rank]); next->rank++; next->flag = 0; stk.push(next); // 将左节点加入堆栈中 now->flag = 1; // 改变标志位 // 将当前节点作为上一级节点存储并删除 previous = new Node(*now); pre_stk.push(previous); delete (now); // 取出堆栈中的待选节点作为当前节点 now = stk.top(); stk.pop(); // 显示搜索路径 for (int i = 0; i < next->path.size(); ++i) { cout << " " << next->path[i] << " "; } cout << endl; continue; // DFS每次仅选取一个子节点,再进入下一步循环 } if (now->rank < deque.size() && (now->flag == 1)) { // 右节点,不选择当前rank的数字 next = new Node(*now); next->rank++; next->flag = 0; stk.push(next); now->flag = 2; // 将当前节点作为上一级节点存储并删除 previous = new Node(*now); pre_stk.push(previous); delete (now); // 取出堆栈中的待选节点作为当前节点 now = stk.top(); stk.pop(); continue; } // 回溯结束 if (pre_stk.empty()) { break; } // 没有子节点或者没有未搜索过的子节点时,回退到上一级节点(回溯) if (now->rank >= deque.size() || now->flag == 2) { delete (now); now = pre_stk.top(); pre_stk.pop(); } } } // ----------------------------------------------------------------- int main() { stack <Node*> stk; vector <int> deque { 2,3,5,7 }; DFS(deque); cin.get(); return 0; } 运行结果

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsppsw.html