热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深度搜索处理问题的关键做leetcode深度搜索类题目小结

1.深度优先搜索中的关键2.深度优先搜索小结3.深度优先搜索和回溯法的区别4.深度优先搜索与递归的区别1.深度优先搜索中的关键深度搜索算法通常用来解决全排列,

 

 

1.深度优先搜索中的关键

2.深度优先搜索小结

3.深度优先搜索和回溯法的区别

4.深度优先搜索与递归的区别




1.深度优先搜索中的关键

深度搜索算法通常用来 解决 全排列不重复组合分组问题多条路径路径的条数等等

对于深度优先搜索,最重要的参数有两个,第一是 深度搜索的次数 step,可以看做需要将待搜索的结果放入到 step 个盒子中,直到放满为止。

第二个是每一次 深度搜索的开始位置start, 主要控制的是每次深度搜索的范围

比如:

对于全排列问题,求[1,2,3]的全排列问题,此时我们可以将问题转化为 将 1,2,3按不同的次序放入到三个盒子中,我们需要的是控制深度搜索的次数step,这个次数就是我们深度搜索的结束条件(收敛条件)

对于求子集问题,求[1,2,3]的全部子集(不能重复),与全排列不同,为了求出不重复的子集,我们需要使在1右边的元素永远在1的右边,也就是说,在第二次深度搜索时我们的搜索范围应该是[2,3],此时我们并不控制深度搜索的次数,而是控制的是深度搜索开始的位置。

class Solution {
public:vector> permute(vector& nums) {vector book(nums.size(),false);vector tmp;vector> res;dfs(nums,0,book,tmp,res);return res;}private:void dfs(vector& nums,int step,vector&book,vector& tmp,vector>& res){if(step == nums.size()){res.push_back(tmp);return;}for(int i = 0; i };

class Solution {
public:vector> subsets(vector& nums) {vector> res;vector path;dfs(nums, 0, path, res);return res;}
private:void dfs(vector& nums, int start, vector& path, vector>& res){res.push_back(path);for(int i = start; i

当然,有些情况下,我们即需要控制 深搜的次数 又需要控制每一次深搜的范围,这种问题典型特征是 分配出了要求的盒子数,

而且是组合问题

较简单的:

class Solution {
public:vector> combine(int n, int k) {vector> res;vector path;dfs(n, k,1, 0, path, res);return res;}private:void dfs(int n, int k,int start, int step, vector& path, vector>& res){if(step == k){res.push_back(path);return;}for(int i = start; i <= n; i++){path.push_back(i);dfs(n,k,i+1,step+1,path,res);path.pop_back();}}
};

较难的:

这一题目中,虽然没有明确说拆分为的盒子数,但是根据IP的特征,应该拆分为4个盒子

同时,这也是一个组合问题(只不过是字符串写在了一起而已),需要控制每次深度搜索的初始位置

class Solution {
public:vector restoreIpAddresses(string s) {vector res;string path;if(s.size() > 12){return res;}dfs(s, 0,0,path,res);return res;}
private:void dfs(string s, int start,int step,string path, vector& res){if(start == s.size() && step == 4){res.push_back(path);return;}for(int i = 1; i <= s.size();i++){if(start + i > s.size()){return;}string section = s.substr(start,i);if(section.size() > 1 && section[0] == &#39;0&#39; || convert(section) > 255){return;}//同时这里用了一个小技巧,因为是字符串,所以不能pop掉后面过来的字符串,所以选择下面这一种方式,来进行回溯dfs(s,start+i,step+1,step == 0 ? section : path + "." + section,res);}}int convert(string section){int sum = 0;for(int i = 0; i

对于二维空间的深度搜索,如果起点已经定下,则由此点开始向上下左右扩展即可,如果起点没有定下来,则在主调函数中进行遍历起点,并对每一个起点进行深度优先搜素,同时,如果深度搜索函数有返回值,则对其进行组合处理即可

起点确定下的二维深度搜索:

class Solution {
public:int uniquePaths(int m, int n) {vector> book(m+1,vector(n+1,0));return dfs(m,n,0,0,book); }private:int dfs(int m, int n,int i,int j,vector>& book){if( i == m-1 && j == n-1)//收敛条件{return 1;}if(i >= m || j >= n){return 0;}return getbook(m,n,i+1,j,book) + getbook(m,n,i,j+1,book); }int getbook(int m,int n,int i,int j,vector>& book){if(book[i][j] > 0){return book[i][j];}else{return book[i][j] = dfs(m,n,i,j,book);}}};

起点不确定条件下的二维深度搜索:

class Solution {
public:bool exist(vector>& board, string word) {int m = board.size();int n = board[0].size();vector> book(m,vector(n,false));for(int i = 0; i private:bool exist(vector>& board,int i,int j,int step,string word,vector>& book){if(step == word.size() )//注意:这个step必须为连续不断的step,不能断{return true;}if(i <0 || j <0 || i >= board.size() || j >= board[0].size()){return false;}if(book[i][j]){return false;}if(board[i][j] != word[step]){return false;}book[i][j] = true;bool res = exist(board,i+1,j,step+1,word,book) || exist(board,i,j+1,step+1,word,book) ||exist(board,i-1,j,step+1,word,book) || exist(board,i,j-1,step+1,word,book);book[i][j] = false;return res;}
};

2.深度优先搜索小结

深度优先搜索适用:

                         如果是递归数据结构,比如单链表,二叉树,集合,则一定可以用深度搜索,如果是非递归数据结构,如一维数组,二维数组,字符串,图等则概率小一些

                         明确深搜是求路径本身还是路径条数,前者则用一个数组 path来进行记录存储,后者不用存储路径

                        终止条件是什么?终止条件只的是不能扩展的末端节点,对于树,则是叶子节点

                        收敛条件是什么?收敛条件是指找到了一个合法解,很多时候终止条件和收敛条件是合二为一的,但两者代表的含义是不同的

                       剪枝加速,深度搜索的优点 是能在答案生成一半时就进行判断,舍弃不满足要求的答案,减少冗余搜索;利于题设中的各种信息,一旦判断此时的答案不会满足要求,则提前返回,避免冗余搜索

                     深度搜索代码模板:

void dfs(type *input,type* path,int step(int start),type *result)
{if(数据非法) return;if(step == input.size() (or start == input.size()))//收敛条件{将path放入 res中return;}if(可以剪枝) return;for(...) //执行所有可能的扩展操作{执行动作,修改pathdfs(input,path,step+1(or start+1),res);恢复path; }
}

3.深度优先搜索和回溯法的区别

回溯法= 深度搜索  + 剪枝

深度搜索 准确的来讲,在搜索过程中会保留下来完整的树结构,即使遍历结果不满足答案,也会继续搜素下去,

而回溯法 是在深度搜索的过程中进行判断中间结果,如果中间结果不满足条件要求,则进行返回,不再搜索下去,

但是由于现在我们在深度搜索的过程中,或多或少会进行剪枝,所以这样来看两者并没有什么区别

 


4.深度优先搜索与递归的区别

深搜常常用递归来实现,二者常常同时出现,但是两者并不是同一个东西。

深搜,是一种算法,而递归是一种物理意义上的实现,递归和迭代是对应的。深搜可以用递归来实现,也可以用栈来实现,而递归,一般总用来实现深搜。

可以说,递归一定是深搜,深搜不一定用递归

递归有两种加速策略:一种是剪枝,对中间结果进行判断,提前返回(回溯),一种是加缓存,缓存中间结果,防止重复计算(动态规划),当然动态规划也可以用迭代来实现

还有一个问题:既然递归一定是深搜,那为什么要有两种术语呢?一般而言,在递归味道更浓的地方,一般用递归,比如在单链表,二叉树等递归数据结构上,而在图,数组等数据结构上,递归的比重不大,深搜的味道更浓,所以用深搜,但两者大部分情况下指同一回事。


推荐阅读
  • 贡献转移在计算每个元素的作用的时候,我们可以通过反向枚举作用效果,添加到作用元素的身上,这种方法叫做贡献转移。更正式的说, ... [详细]
  • 本文探讨了异步编程的发展历程,从最初的AJAX异步回调到现代的Promise、Generator+Co以及Async/Await等技术。文章详细分析了Promise的工作原理及其源码实现,帮助开发者更好地理解和使用这一重要工具。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • Go从入门到精通系列视频之go编程语言密码学哈希算法(二) ... [详细]
  • 本文回顾了作者在求职阿里和腾讯实习生过程中,从最初的迷茫到最后成功获得Offer的心路历程。文中不仅分享了个人的面试经历,还提供了宝贵的面试准备建议和技巧。 ... [详细]
  • 深入解析 C++ 中的 String 和 Vector
    本文详细介绍了 C++ 编程语言中 String 和 Vector 的使用方法及特性,旨在帮助开发者更好地理解和应用这两个重要的容器。 ... [详细]
  • 本文详细介绍了在Luat OS中如何实现C与Lua的混合编程,包括在C环境中运行Lua脚本、封装可被Lua调用的C语言库,以及C与Lua之间的数据交互方法。 ... [详细]
  • 本文深入探讨了动态赋值的概念及其在编程实践中的应用,特别是通过Java代码示例来展示如何利用循环结构动态地为数组分配值。 ... [详细]
  • 本文将探讨一个经典算法问题——最大连续子数组和。我们将从问题定义出发,逐步深入理解其背后的逻辑,并通过实例分析加深理解。 ... [详细]
  • MySQL InnoDB 存储引擎索引机制详解
    本文深入探讨了MySQL InnoDB存储引擎中的索引技术,包括索引的基本概念、数据结构与算法、B+树的特性及其在数据库中的应用,以及索引优化策略。 ... [详细]
  • 本篇文章详细探讨了微机原理实验中的指令系统,特别是第三章的内容。对于希望深入了解微机工作原理和技术实现的朋友来说,这是一篇不可多得的技术指南。文章不仅涵盖了基础概念,还深入讲解了指令格式、操作数类型以及各种寻址方式,旨在帮助读者更好地掌握微机指令系统的应用。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
  • Spring Security基础配置详解
    本文详细介绍了Spring Security的基础配置方法,包括如何搭建Maven多模块工程以及具体的安全配置步骤,帮助开发者更好地理解和应用这一强大的安全框架。 ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
  • 本文探讨了如何将Python对象转换为字节流,以实现文件保存、数据库存储或网络传输的需求。主要介绍了利用pickle模块进行序列化的具体方法。 ... [详细]
author-avatar
梨依籽_852
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有