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

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

程序说明

1、定义了一个Node节点类,表示当前状态下已经搜索到的序列,path记录了这个子序列的值,并且类中添加了num(子序列中元素数目)、sum(子序列元素和)等属性,通过这些属性可以判断是否找到满意解或者用于剪枝。

2、对于原始序列中某个位置的数,其子序列中可以包含这个数,也可以不包含这个数,所以每次有两种选择,即每个节点有两个子节点。

3、flag属性标识了当前节点的子节点遍历情况。若flag=0,表示子节点都没访问过,下一步优先访问左节点,所以将左节点加入堆栈中;flag=1,表示访问过左节点,下一步访问右节点;flag=2,表示访问过左右节点。

4、当没有子节点(now->rank >= deque.size())或者左右节点都访问过时(flag=2),回溯到上一级节点。

5、程序循环中,首先通过now当前节点,找到下一个子节点next,将其加入堆栈中,便于下一步循环。在now节点销毁前,将其存到previous,并加入pre_stk堆栈中。这样在下一轮循环中,previous相对于now就是上一级节点,如果now不能找到其子节点,就要返回上一级,这样previous就可以重新赋给now,达到返回上一级的目的。

6、整个程序的终止条件是pre_stk堆栈为空时截止,说明所有节点都已经遍历过,并且没有再可回溯的节点了。实际运用中,可以通过其他属性(搜索到可行解)来提前终止程序。

递归实现

参考自Coding_Or_Dead的博客

#include<cstdio> #include <iostream> int n, k; __int64 sum = 0; int a[4] = { 2, 3, 5, 7 }, vis[4] = {0, 0, 0, 0}; void DFS(int i, int cnt, int sm)//i为数组元素下标,sm为cnt个数字的乘积 { if (cnt == k) // 解中已包含k个数字 { sum = sum + sm; return; } if (i >= n) return; if (!vis[i]) { // 对第i个数字进行访问 vis[i] = 1; //a[i]被选,优先选择第i个加入到解中,接下来搜索第i+1个数字 DFS(i + 1, cnt + 1, sm*a[i]); //a[i]不选,不选择第i个,相当于右节点,接下来搜素第i+1个数字 DFS(i + 1, cnt, sm); vis[i] = 0; // 回溯 } return; } int main(void) { n = 4, k = 2; DFS(0, 0, 1); printf("%I64d\n", sum); std::system("pause"); return 0; } 程序说明

1、程序目的:给定n个正整数,求出这n个正整数中所有任选k个相乘后的和,这里的数组a[4]存储原序列,vis[4]作为访问标志,k取2,结果输出为101,对应的序列是{2, 3}{2, 5}{2, 7}{3, 5}{3, 7}{5, 7}。

2、对于元素a[i],每次对应两个选择。若选择将a[i]加入到解中,则解中元素个数+1,乘积结果*a[i],所以下一步更新为DFS(i + 1, cnt + 1, sm*a[i])。若不选择a[i],则解中的元素个数和乘积不变,下一步更新为DFS(i + 1, cnt, sm)。

3、回溯时要将标志位重置。

References

深度优先搜索 - 维基百科,自由的百科全书

DFS深度优先搜索(入门) - CSDN博客

广度优先搜索 BFS算法 - 东聃 - 博客园

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

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