最近一直在学习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. Android偏好设置(5)偏好设置界面显示多个分组,每个分组也有一个界面

    1.Using Preference Headers In rare cases, you might want to design your settings such that the first ...

  2. 203 Remove Linked List Elements 删除链表中的元素

    删除链表中等于给定值 val 的所有元素.示例给定: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6返回: 1 --& ...

  3. WCF入门大致思路

    WCF服务: 1.IServer.cs(类似接口,WCF接口) 2.Server.svc(实现了WCF接口)右键浏览器运行可以看到WCF服务链接,类似(http://localhost:4609/Us ...

  4. 初学Ajax

    AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX = 异步 JavaScript和 ...

  5. UWP Windows10开发更新磁贴和动态更新磁贴

    下面将介绍两种方式如何在windows10 uwp开发中如何更新应用磁贴: 实际上windows的磁贴就是用xml实现的,你只需要创建相应格式的xml就可以实现动态磁贴了 一,手动更新磁贴 二,轮询更 ...

  6. Spring && 实验IOC

    一.Spring作用 1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!]    2.将其他组件粘合在一起    3.IOC容器和AOP[As ...

  7. cesium primitive方式 ————http://blog.sina.com.cn/s/blog_15e866bbe0102y0ji.html

    Cesium学习笔记-工具篇17-PrimitivePoint自定义渲染-点 (2018-08-28 16:12:06) 转载▼ 标签: cesium primitive 自定义渲染 shader c ...

  8. vue 模板下只能有一个跟节点 根节点一定要是个div

    <template> <div>简单说就是里面只能有一个跟的div button1.vue <template> <div> <Button> ...

  9. 华硕笔记本无法设置U盘启动,快捷启动不能识别

    最近有不少华硕笔记本用户朋友在使用U大侠装系统时,不管是使用快捷键启动还是BIOS查看,都没有发现U盘启动项,这该怎么办呢?   不要急,既然找不到启动项,那就从设置启动项来解决不就可以了. 第一种方 ...

  10. USB设备请求命令详解

    USB设备请求命令 :bmRequestType + bRequest + wValue + wIndex + wLength 编号 值  名称 (0) 0  GET_STATUS:用来返回特定接收者 ...