本章讨论有关以下算法的问题:
- Backtracing
- Deep first search (DFS)
- Bread first search (BFS)
- Graph related questions
1. Backtracing
所有排列组合的问题几乎都可以用backtracing算法来解决。
一个非常非常非常重要的算法--backtracing,回溯法,一定要掌握!!!这篇文章给了我非常非常大的帮助。
Backtracing是用递归来枚举多维度值(n-tuple)的算法, 一句话总结其核心就是对于每个维度,依次枚举每一个candidates,然后继续下一个维度。下面是其框架:
void backtrack(int dimension)
{
/*递归的终止条件 */
if ( solution[] 符合终止条件 )
{
check and record solution;
return;
}
/* 对于该维度,枚举每一个candidates,并返回下一个维度 */
for ( x = each value of current candidates )
{
solution[dimension] = x;
backtrack( dimension + 1 );
}
}
例如从1~3选任意2个数,右那些种可能?(可重复选),那么终止条件就是solutin长度为2的时候终止,每个位置上都枚举各个候选者。
不同的问题终止条件可能不同,枚举的时候可能也有限制。等我做到的时候再总结。
一个典型的backtracing函数会有如下几个参数:
void backtracing(vector<int> nums, int dim, vector<vector<int> > &records,
vector<int> &solution)
- nums: original input
- dim: the current dimension
- records: all the results so far
- solution: generated from the current dimension
2. DFS
Depth-First search is a specific form of backtracking related to searching tree or graph structures.
Permutation Subsets等问题也是可以用DFS来解的,比backtracing更容易理解,而且因为DFS在graph tree相关问题里面用的也比较多,所以更要掌握,理解,不惧!
这有一篇关于DFS和BFS的文章,讲得很详细。
DFS模板:
/**
* dfs 模板.
* @param[in] input 输入数据指针
* @param[out] path 当前路径,也是中间结果
* @param[out] result 存放最终结果
* @param[inout] cur or gap 标记当前位置或距离目标的距离
* @return 路径长度,如果是求路径本身,则不需要返回长度
*/
void dfs(type &input, type &path, type &result, int cur or gap) {
if (数据非法) return 0; // 终止条件
if (cur == input.size()) { // 收敛条件
// if (gap == 0) {
将path 放入result
}
if (可以剪枝) return; //if(visited[x][y] == true) return; // 判重
for(...) { // 执行所有可能的扩展动作
执行动作,修改path
dfs(input, step + 1 or gap--, result);
恢复path
}
}
3. 图的DFS遍历
非递归用栈实现。 BFS是从queue里面弹出来的时候输出,DFS是push到stack的时候输出
Node structure:
struct DirectedGraphNode {
int val;
vector<DirectedGraphNode*> neighbors;
DirectedGraphNode(int x) {
val = x;
}
};
//给定边的vector,构造Graph
vector<DirectedGraphNode*> CreateDirectedGraph(vector<pair<int, int> > connections) {
unordered_map<int, DirectedGraphNode*> nodes;
vector<DirectedGraphNode*> result;
if (connections.empty()) return result;
for (auto const& conn : connections) {
/*
DirectedGraphNode *node1, *node2;
if(nodes.find(conn.first) != nodes.end()) {
node1 = nodes[conn.first];
}
else {
node1 = new DirectedGraphNode(conn.first);
}
if (nodes.find(conn.second) != nodes.end()) {
node2 = nodes[conn.second];
}
else {
node2 = new DirectedGraphNode(conn.second);
}
node1->neighbors.emplace_back(node2);
*/
//用上面注释的代码更好
nodes.emplace(conn.first, new DirectedGraphNode(conn.first));
nodes.emplace(conn.second, new DirectedGraphNode(conn.second));
auto node1 = nodes[conn.first];
auto node2 = nodes[conn.second];
node1->neighbors.emplace_back(node2);
}
for (auto const& v : nodes) {
result.emplace_back(v.second);
}
return result;
}
//递归写法
void dfsGraphHelper(DirectedGraphNode *node, unordered_set<DirectedGraphNode*> &is_visted) {
cout << node->val << endl;
is_visted.emplace(node);
for (auto const& v : node->neighbors) {
if (is_visted.find(v) == is_visted.end()) {
dfsGraphHelper(v, is_visted);
}
}
}
//iteration Iteration
//用个栈,没被访问过入栈,入栈时输出,所有邻居node都被访问过了pop
void dfsGraphHelperIteration(DirectedGraphNode *node, unordered_set<DirectedGraphNode*> &is_visted) {
stack<DirectedGraphNode*> stk;
stk.push(node);
cout << node->val << endl;
is_visted.emplace(node);
while (stk.empty() == false) {
DirectedGraphNode *cur = stk.top();
bool all_visited = true;
for (auto const& v : cur->neighbors) {
if (is_visted.find(v) == is_visted.end()) {
stk.push(v);
cout << v->val << endl;
is_visted.emplace(v);
all_visited = false;
break;//找到一个没访问过的就要break,不然就不是DFS了
}
}
if (all_visited) {
stk.pop();
}
}
}
void dfsGraph(vector<DirectedGraphNode*> graph) {
unordered_set<DirectedGraphNode*> is_visted;
//可能是不连通图,所以要从每个没被访问的节点开始做DFS
for (auto const& node : graph) {
if (is_visted.find(node) == is_visted.end()) {
//dfsGraphHelper(node, is_visted);
dfsGraphHelperIteration(node, is_visted);
}
}
}
4. 图的BFS遍历
用queue实现。
void bfsGraphHelper(DirectedGraphNode *node, unordered_set<DirectedGraphNode*> &is_visted) {
queue<DirectedGraphNode*> que;
que.emplace(node);
is_visted.emplace(node);
while (que.empty() == false) {
DirectedGraphNode *cur = que.front();
que.pop();
cout << cur->val << endl;//BFS是从queue里面弹出来的时候输出,DFS是push到stack的时候输出
for (auto const& v : cur->neighbors) {
if (is_visted.find(v) == is_visted.end()) {
que.emplace(v);
is_visted.emplace(v);
}
}
}
}
5. 判断图上两个节点是否存在path
从start出发开始DFS或者BFS,把之前输出的地方改成判断是否到达end节点。
//DFS递归实现
bool isReachableHelper(DirectedGraphNode *start, DirectedGraphNode *end, unordered_set<DirectedGraphNode*> is_visited) {
if (start == end) return true;
is_visited.emplace(start);
for (auto const& v : start->neighbors) {
if (is_visited.find(v) == is_visited.end()) {
if (isReachableHelper(v, end, is_visited)) {
return true;
}
}
}
return false;
}
bool isReachable(DirectedGraphNode *start, DirectedGraphNode *end) {
unordered_set<DirectedGraphNode*> is_visited;
return isReachableHelper(start, end, is_visited);
}
bool isReachable(vector<DirectedGraphNode*> &graph, int v_start, int v_end) {
DirectedGraphNode *start = NULL;
DirectedGraphNode *end = NULL;
for (auto const & v : graph) {
if (v->val == v_start) {
start = v;
}
if (v->val == v_end) {
end = v;
}
}
if (start == NULL || end == NULL) return false;
return isReachable(start, end);
}
DFS iteration 实现
bool isReachable(DirectedGraphNode *start, DirectedGraphNode *end) {
unordered_set<DirectedGraphNode*> is_visited;
//return isReachableHelper(start, end, is_visited);
stack<DirectedGraphNode*> stk;
if (start == end) return true;
stk.emplace(start);
while (!stk.empty()) {
DirectedGraphNode *cur = stk.top();
bool all_visited = true;
for (auto const& v : cur->neighbors) {
if (is_visited.find(v) == is_visited.end()) {
if (v == end) return true;
stk.emplace(v);
all_visited = false;
break;
}
}
if (all_visited) stk.pop();
}
return false;
}
BFS 实现
bool isReachable(DirectedGraphNode *start, DirectedGraphNode *end) {
unordered_set<DirectedGraphNode*> is_visited;
//return isReachableHelper(start, end, is_visited);
queue<DirectedGraphNode*> que;
que.emplace(start);
is_visited.emplace(start);
while (!que.empty()) {
DirectedGraphNode *cur = que.front();
que.pop();
if (cur == end) return true;//输出时判断
for (auto const& v : cur->neighbors) {
if (is_visited.find(v) == is_visited.end()) {
is_visited.emplace(v);
que.emplace(v);
}
}
}
return false;
}
6. 输出两个node之间的所有path
上个问题的follow up,在DFS的时候要保存状态is_visited(跟上个问题一样),但同时也要保存path (当前path)和all_paths(所有路径)。
void GetPathsDfs(DirectedGraphNode *start, DirectedGraphNode *end, unordered_set<DirectedGraphNode*> &is_visited,
vector<DirectedGraphNode*> &path, vector<vector<DirectedGraphNode*> > &all_paths) {
if (start == end) {//收敛条件
all_paths.emplace_back(path);
return;
}
is_visited.emplace(start);//更新状态
for (auto const& v : start->neighbors) {//状态转移
if (is_visited.find(v) == is_visited.end()) {
path.emplace_back(v);//记录路径
GetPathsDfs(v, end, is_visited, path, all_paths);//递归
path.pop_back();//back tracing
}
}
}
vector<vector<DirectedGraphNode*> > GetPaths(DirectedGraphNode *start, DirectedGraphNode *end) {
vector<vector<DirectedGraphNode*> > all_paths;
vector<DirectedGraphNode*> path;
unordered_set<DirectedGraphNode*> is_visited;
if (start == NULL || end == NULL) return all_paths;
path.emplace_back(start);
GetPathsDfs(start, end, is_visited, path, all_paths);
return all_paths;
}
7. 判断图是否存在cycle
DFS,记录下recursion stack里面的node,如果当前结点在栈里,说明存在回路了。
bool isCyclicHelper(DirectedGraphNode *node, unordered_set<DirectedGraphNode*> &is_visited,
unordered_map<DirectedGraphNode*, bool> &cur_stk) {
is_visited.emplace(node);//mark node as visisted
cur_stk[node] = true; //node now is in the current recursion stack
for (auto const& neighbor : node->neighbors) {
//if neighbor is not visited, recursively call helper
if (is_visited.find(neighbor) == is_visited.end()) {
if (isCyclicHelper(neighbor, is_visited, cur_stk)) {
return true;
}
}//elese, check if it is in the recursion stack
else if (cur_stk.find(neighbor) != cur_stk.end() && cur_stk[neighbor] == true) {
return true;
}
}
cur_stk[node] = false; //node is NOT in the recursion stack any more
return false;
}
bool isCyclic(vector<DirectedGraphNode*> &graph) {
unordered_set<DirectedGraphNode*> is_visited;
unordered_map<DirectedGraphNode*, bool> cur_stk; //if a given in the curren stack or not
for (auto const& node : graph) {
if (is_visited.find(node) == is_visited.end() && isCyclicHelper(node, is_visited, cur_stk)) {
return true;
}
}
return false;
}