首页 > DFS template and summary

DFS template and summary

  最近一直在学习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& 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, stopif(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 returnif(hash_table[input] != hash_table.end()){return;}for(int i = 0; i < n; i++){if(visited[i] == 0){// do something for current level processvisited[i] = 1;// for next level DFSDFS_no_return(solution_set, solution, idx + 1);// return from lower level DFS and restore the status for next DFSvisited[i] = 0;}}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, stopif(idx == n || idx == (vector/string).size()){return 1/0/"";}// 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 returnif(hash_table[input] != hash_table.end()){return hash_table[input];}TYPE ans;for(int i = 0; i < n; i++){if(visited[i] == 0){// normally, here should to add/concatenate the value in current level with the value returned from lower levelans += DFS_no_return(idx + 1);}}// if we need prune// keep current value herehash_table[input] = ans;return ans;
}

总结: 

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

转载于:https://www.cnblogs.com/wdw828/p/6840317.html

更多相关:

  • 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 答案需要取模 1e9+7(100...

  • empty()函数 是用来测试变量是否已经配置。若变量已存在、非空字符串或者非零,则返回 false 值;反之返回 true值。所以,当字符串的值为0时,也返回true,就是执行empty内部的语句。这就是陷阱。     如: 假设 $value = 0; 则empty($value)=false。     劝告各位,千万注意使用...

  • (四)Asp.net web api中的坑-【api的返回值】 原文:(四)Asp.net web api中的坑-【api的返回值】void无返回值IHttpActionResultHttpResponseMessage自定义类型我这里并不想赘述这些返回类型, 可以参考博文http://blog.csdn.net/leonk...

  • 今天碰见个题目,感觉短路表达式很好用。 题目: 定义一个计算圆面积的函数area_of_circle(),它有两个参数:r: 表示圆的半径;pi: 表示π的值,如果不传,则默认3.14function area_of_circle(r, pi) {} 我的写法:  if(arguments.length>=2) { ret...

  • 类型 JavaScript 有七种内置类型:null、undefined、boolean、number、string、object 和symbol,可以使用typeof 运算符来查看typeof返回的都是字符串很多开发人员将undefined 和undeclared 混为一谈, 但在JavaScript 中它们是两码事。undefin...

  • 什么是DOM document object model 的简称,意思为文档对象模型。主要用来对文档中的html节点进行操作。 Dom的操作简单示例:

    草色新雨中, 松声晚窗里。之前我们学习 Power Query 都是用鼠标就完成了很多复杂的操作。虽然 PowerQuery 已经将大部分常用功能内置成到功能区。基本能完成我们大部分的报表自动化功能。但是总有些复杂的或者个性化的问题是开发团队没有预先想到的,这时我们就需要学习 M 语言。一、M 语言在哪里?M语言的函数公式有三个地...

  • 前言从2020年3月份开始,计划写一系列文档--《小白从零开始学编程》,记录自己从0开始学习的一些东西。第一个系列:python,计划从安装、环境搭建、基本语法、到利用Django和Flask两个当前最热的web框架完成一个小的项目第二个系列:可能会选择Go语言,也可能会选择Vue.js。具体情况待定,拭目以待吧。。。基本概念表达式表...

  • 1.1函数1.1.1什么是函数函数就是程序实现模块化的基本单元,一般实现某一功能的集合。函数名:就相当于是程序代码集合的名称参数:就是函数运算时需要参与运算的值被称作为参数函数体:程序的某个功能,进行一系列的逻辑运算return 返回值:函数的返回值能表示函数的运行结果或运行状态。1.1.2函数的作用函数是组织好的,可重复使用的,用来...

  • 原标题:基于Python建立深度神经网络!你学会了嘛?图1 神经网络构造的例子(符号说明:上标[l]表示与第l层;上标(i)表示第i个例子;下标i表示矢量第i项)单层神经网络图2 单层神经网络示例神经元模型是先计算一个线性函数(z=Wx+b),接着再计算一个激活函数。一般来说,神经元模型的输出值是a=g(Wx+b),其中g是激活函数(...

  • 在学习MySQL的时候你会发现,它有非常多的函数,在学习的时候没有侧重。小编刚开始学习的时候也会有这个感觉。不过,经过一段时间的学习之后,小编发现尽管函数有很多,但是常用的却只有那几个。今天小编就把常用的函数汇总一下,为大家能够能好的学习MySQL中的函数。MySQL常使用的函数大概有四类。时间函数、数学函数、字符函数、控制函数。让我...