最近一直在学习Deep Frist Search,也在leetcode上练习了不少题目。从最开始的懵懂,到现在遇到问题基本有了思路。依然清晰的记得今年2月份刚开始刷题的时做subsets的那个吃力劲,脑子就是转不过来到底该如何的递归,甚至试过使用debugger一步步的来看看堆栈到底是如何调用和返回的。经过了几个月的训练后,答题有了一些心得,记录下来,作为总结。

  递归函数的整体结构:

  返回值。进入入递归函数的第一件事就是写出递归终止的条件。通常递归终止的条件分为两类:

  (1)有一个深度的标准,例如N-Queen问题,当我们递归的检测完第N个皇后,就到达来递归返回的条件,此时,将此次递归的结果作为一个解决方案存储。

  (2)到达一个序列(数组/字符串)vector/string.size()的位置,例如subsets,permutation和word break ii的问题,当传递的参数已经无法索引数组/字符串的值时退出递归。

  是否进行prune的操作。这个操作并不是一定会存在的。当需要prune的时候,通常是遇到了效率问题。当输入问题的规模非常大,存储之前已经做过检测或者得出结果的递归位置会大大降低递归的工作量。这个时候我们通常会利用一些数据结构。最简单的就是一个数组,复杂一点的会是一个hash table(word break ii in leetcode)。

  本层的递归处理以及下一层次的递归调用。这一部分是递归的核心,如果这一部分模型处理正确,递归的调用就成功了90%(很像如果能写出动态规划的状态方程的感觉)。

  返回值。最后的return,连同递归的处理和调用,具体内容会这下面详细阐述。

  递归函数的类型:

  总的来说DFS的函数分为两种类型,有返回值和没有返回值。递归的层次都是通过一个参数传递到函数内部,这两种函数的编写有区别,虽然都是DFS,但思维方式却是不一样。

  对于无返回值的DFS函数,通常我们是将需要获取的值作为引用类型的参数传递到函数内部,当到达DFS函数终止的条件时我们获取想要得到的值,例如N-Queen,subsets, permutations等经典的问题。这是一种从上向下的思维模式。这种函数适用于求所有可能的解的问题。通常是先对该层的问题进行处理,然后再进入下一个层次的递归。当到达递归终止的条件时存储结果。然后再逐层的返回,通常会恢复该层这遍历之前的状态,为的是下一个递归。

以下为这种递归的模板:

void DFS_no_return(vector<TYPE>& solution_set, TYPE& solution, int idx){
// DFS terminate condition
// Normally there are two major categories
// One is for some number, already reach this depth according to the question, stop
// get to the end of an array, stop
if(idx == n || idx == (vector/string).size()){
solution_set.push_back(solution);
return;
}
// very seldom only when our program met some efficiency problem
// do prune to reduce the workload
// no need do DFS if we already get the value of current element, just return
if(hash_table[input] != hash_table.end()){
return;
} for(int i = ; i < n; i++){
if(visited[i] == ){
      // do something for current level process
visited[i] = ;
// for next level DFS
DFS_no_return(solution_set, solution, idx + );
// return from lower level DFS and restore the status for next DFS
visited[i] = ;
}
}
return;
}

对于有返回值的DFS来说,我们需要获得的值是递归函数的返回值。与无返回值的递归函数相比,最大的一个区别是该类递归函数在递归调用返回之后处理该层的递归值。将从低层次递归返回的值结合本层的值处理,然后再返回给上一层递归函数调用。这种问题的思路是由上到下,再由下向上。通常是将一个复杂的问题逐层的分解成小问题,等到无法再分则返回,再将小问题拼装,然后逐层向上,返回大问题作为问题的解。通常在使用分治的思想时会使用有返回值的深度递归函数,这个应用在树中非常普遍。

以下为有返回值的递归函数的模板:

TYPE DFS_with_return(int idx){
// DFS terminate condition
// Normally there are two major categories
// One is for some number, already reach this depth according to the question, stop
// get to the end of an array, stop
if(idx == n || idx == (vector/string).size()){
return //"";
}
// very seldom only when our program met some efficiency problem
// do prune to reduce the workload
// no need do DFS if we already get the value of current element, just return
if(hash_table[input] != hash_table.end()){
return hash_table[input];
} TYPE ans;
for(int i = ; i < n; i++){
if(visited[i] == ){
// normally, here should to add/concatenate the value in current level with the value returned from lower level
ans += DFS_no_return(idx + );
}
}
// if we need prune
// keep current value here
hash_table[input] = ans;
return ans;
}

总结:

  1. 递归函数的整体构造:
  2. 递归函数的分类:
    • 无返回值:适用于求所有可能的解。

      • 是一种由上而下,将大问题分解,在每一层提取出需要处理的小问题,然后向下递归,遇到递归终止的条件时,就得到一个问题的解,此时返回。
    • 有返回值:使用与求个数/数量的问题。
      • 是一种从上向下,将大问题分解到无法再分,然后再将分解后的小问题返回,逐层进行加工处理,然后再返回给上一层调用。

DFS template and summary的更多相关文章

  1. Binary Search Tree DFS Template

    Two methods: 1. Traverse 2. Divide & Conquer // Traverse: usually do not have return value publi ...

  2. C#设计模式系列:模板方法模式(Template Method)

    你去银行取款的时候,银行会给你一张取款单,这张取款单就是一个模板,它把公共的内容提取到模板中,只留下部分让用户来填写.在软件系统中,将多个类的共有内容提取到一个模板中的思想便是模板方法模式的思想. 模 ...

  3. 设计模式系列13:模板方法模式(Template Method Pattern)

    定义 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.    --<设计模式GoF> UML类图 使用场景 有 ...

  4. 【DP】【CF31E】 TV Game

    传送门 Description 给你一个长度为\(2n\)的数字,每次可以从左侧选一个数字,加入连接到一个数字\(A\)或另一个数字\(B\)后面.\(A,B\)初始为\(0\).\(A\)与\(B\ ...

  5. t4 加载文件到解决方案

    Use EnvDTE add/remove multiple files to project By admin | décembre 17, 2013 Un commentaire Project ...

  6. winrt 上的翻书特效组件 源码分享 转载请说明

    http://blog.csdn.net/wangrenzhu2011/article/details/10207413 (转) [TemplatePart(Name = A_PARTNAME, Ty ...

  7. Jmeter html 报告中添加90% line time

    转载南风_real博客园:http://www.cnblogs.com/jaychang/p/5784882.html 首先上效果图: 其次明白几个原理: 90% Line的意思是:一组数由小到大进行 ...

  8. C# 使用C/S模式操作小票机打印

    此方式适用于市场上大多数的小票机 佳博.POS58 等,不适用于有些标签打印机 比如斑马打印机等 直接贴代码: private FileStream fs = null; [DllImport(&qu ...

  9. 【JMeter】ant+jmeter生成html报告

    源博文来自于  http://my.oschina.net/hellotest/blog/517518 主要应用于接口的回归或者性能的简单查看功能.操作为先在jmeter中写好测试计划,保存为jmx文 ...

随机推荐

  1. 题解报告:hdu 1212 Big Number(大数取模+同余定理)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1212 Problem Description As we know, Big Number is al ...

  2. lock to deteck in oracle

    0,5,10 0-23 * * * /home/oracle/utility/blocker/detect_blocker.sh db 120 > /home/oracle/utility/tr ...

  3. PHP autoload实践

    本文目的 本文简要的描述了PHP提供的autoload机制,以及在scake中使用实践.用于减少不必要的文件包含,提高php系统性能. 什么是__autoload php是脚本语言,不同于c++只需要 ...

  4. [转]Monkey测试简介

    转自:http://www.cnblogs.com/manuosex/p/3215270.html 在android手机上做自动化测试,monkey比cts,Android UnitTest 好用多了 ...

  5. 转】Cassandra单集群实验2个节点

    原博文出自于: http://blog.fens.me/category/%E6%95%B0%E6%8D%AE%E5%BA%93/page/3/ 感谢! Cassandra单集群实验2个节点 前言 A ...

  6. jacaScript数组

    1.var arr=['1','2','3'] typeof arr (判断数组类型)    print(arr)打印数组内容 2.arr[100]='x',  数组中间自动添加,alert(arr. ...

  7. jquery基础知识点总结

    Jquery是一个优秀的js库,它简化了js的复杂操作,不需要关心浏览器的兼容问题,提供了大量实用方法. Jquery的写法 方法函数化 链式操作 取值赋值合体] $(“p”).html();   取 ...

  8. CF919D Substring

    思路: 拓扑排序过程中dp.若图有环,返回-1. 实现: #include <bits/stdc++.h> using namespace std; ; vector<int> ...

  9. 学习笔记 第十一章 CSS3布局基础

    第11章   CSS3布局基础 [学习重点] 了解CSS2盒模型. 设计边框样式. 设计边界样式. 设计补白样式. 了解CSS3盒模型. 11.1  CSS盒模型基础 页面中所有元素基本显示形态为方形 ...

  10. Unity笔记(4)自学第四、五天

    主要是移动脚本和2个技能的脚本编写. 首先是移动的脚本: using System.Collections; using System.Collections.Generic; using Unity ...