当前位置: 首页 > news >正文

DFS算法专题(三)——综合练习之【经典回溯】

目录

1、电话号码的字母组合

1.1 算法原理

1.2 算法代码

2、括号生成

2.1 算法原理

2.2 算法代码

3、组合

3.1 算法原理 

3.2 算法代码

4、目标和

4.1 算法原理

4.2 算法代码【path-->全局变量形式】

4.3 算法代码【path-->dfs参数形式】

5、组合总和

5.1 算法原理【策略一】

5.2 算法代码【策略一】

5.3 算法原理【策略二】

5.4 算法代码【策略二】


1、电话号码的字母组合

. - 力扣(LeetCode)

1.1 算法原理

  • 画出决策树,越详细越好
  • 设计代码  -> 全局变量: List<String> ret;//返回值   StringBuffer path;//路径    String[] reflect;//digits中元素所映射的字符串 
  • 设计代码  -> 函数头:dfs(digits, pos); //pos为下一个要添加进path中的digits元素的下标
  • 设计代码  -> 函数体 ...
  • 设计代码  -> 函数出口:path.length() == digtis.length(); 
  • 设计代码  -> 回溯 -> 恢复现场:path.deleteChatAt(最后一个元素);
  • 设计代码  -> 剪枝:无

1.2 算法代码

class Solution {List<String> ret;StringBuffer path;String[] reflect = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};public List<String> letterCombinations(String digits) {ret = new ArrayList<>();if(digits.equals("")) return ret;path = new StringBuffer();dfs(digits, 0);return ret;}public void dfs(String digits, int pos) {if(path.length() == digits.length()) {ret.add(path.toString());return;}int index = digits.charAt(pos) - '0';for(int i = 0; i < reflect[index].length(); i++) {path.append(reflect[index].charAt(i));dfs(digits, pos + 1);//回溯 -> 恢复现场path.deleteCharAt(path.toString().length()-1);}}
}

2、括号生成

. - 力扣(LeetCode)

2.1 算法原理

合法的字符串:
①:"(" 的数量始终大于等于 ")" 的数量
②:"(" 的个数 <= n
③:最终,( 和 ) 的总数为2n,且各数量相等

代码设计:

  • 全局变量:left //记录"("数量;right //记录")"数量;ret //返回值;path //路径
  • 函数体:dfs(n)
  • 剪枝:当left < n时 -> 可添加"(";当right < left时 -> 可添加")"。
  • 回溯(恢复现场):①:path.delete(最后一个元素);②:left--/right--;
  • 函数出口:left + right == 2*n 或者 right == n

2.2 算法代码

class Solution {List<String> ret;StringBuffer path;int left, right;public List<String> generateParenthesis(int n) {ret = new ArrayList<>();path = new StringBuffer();dfs(n);return ret;}public void dfs(int n) {if ((left + right) == (2 * n)) {ret.add(path.toString());return;}//剪枝if (left < n) {path.append("(");left++;dfs(n);//回溯 -> 恢复现场path.deleteCharAt(path.length() - 1);left--;}//剪枝if(right < left) {path.append(")");right++;dfs(n);//回溯 -> 恢复现场path.deleteCharAt(path.length() - 1);right--;}    }
}

3、组合

. - 力扣(LeetCode)

3.1 算法原理 

  1. 画决策树
  2. 设计代码
  3. 函数头:dfs(n, k)
  4.  函数体:添加当前元素,接着dfs当前元素的下一层(当前位置的下一个位置)
  5.  函数出口:path.size()==k
  6.  回溯:将path恢复现场
  7.  剪枝:从当前位置的下一个位置开始枚举 --> 自动剪枝

3.2 算法代码

class Solution {List<List<Integer>> ret;List<Integer> path;public List<List<Integer>> combine(int n, int k) {ret = new ArrayList<>();path = new ArrayList<>();dfs(n, k, 1);return ret;}public void dfs(int n, int k, int pos) {//函数出口if (path.size() == k) {ret.add(new ArrayList<>(path));return;}//自动剪枝 -> 从当前元素的下一个位置开始枚举for (int i = pos; i <= n; i++) {path.add(i);dfs(n, k, i + 1);//回溯(恢复现场)path.remove(path.size() - 1);}}
}

4、目标和

. - 力扣(LeetCode)

4.1 算法原理

  1. 决策树
  2. 设计代码
  3. 全局变量:ret //返回值;path //记录路径和
  4. 注意:当path为整形时,也可形参传入(处理简单且函数将自动回溯)。但是全局变量形式和形参形式并无好坏之分,两者均可。
  5. 函数头:dfs(pos,...)//pos为要进行操作的元素的下标(nums中)
  6. 函数体:分左右两分支,分别进行nums中元素的加减运算
  7. 函数出口:pos==nums.length,在出口时判断path是否等于target
  8. 回溯:恢复path
  9. 剪枝:无

4.2 算法代码【path-->全局变量形式】

class Solution {int ret;int pathOfSum;public int findTargetSumWays(int[] nums, int target) {dfs(nums, target, 0);return ret;}public void dfs(int[] nums, int target,int pos) {if(pos == nums.length) {if(pathOfSum == target) ret++;return;}//加pathOfSum += nums[pos];dfs(nums, target, pos + 1);//回溯pathOfSum -= nums[pos];//减pathOfSum -= nums[pos];dfs(nums, target, pos + 1);//回溯pathOfSum += nums[pos];}
}

4.3 算法代码【path-->dfs参数形式】

class Solution {int ret;public int findTargetSumWays(int[] nums, int target) {dfs(nums, target, 0, 0);return ret;}public void dfs(int[] nums, int target,int pos, int pathOfSum) {if(pos == nums.length) {if(pathOfSum == target) ret++;return;}//加 //参数形式->函数自动回溯dfs(nums, target, pos + 1, pathOfSum + nums[pos]);//减 //参数形式->函数自动回溯dfs(nums, target, pos + 1, pathOfSum - nums[pos]);}
}

注意:全局变量形式和形参形式并无好坏之分,两者均可。 


5、组合总和

. - 力扣(LeetCode)

5.1 算法原理【策略一】

  • 画决策树【决策树并非只有一种!!!】
  • 算法思想:DFS回溯搜索即暴力枚举。

注意:为避免结果中数据的重复,每次枚举都应从pos位置开始向后枚举(包含pos位置,因为每一个元素可以重复使用)。
解释:因为2节点得到的[2,3,...]和3节点得到的[3,2,...]的数据组,属于重复枚举,
在选2节点时就已经完成了[2,3,...]的选择,
故在选3节点时,可直接从下一层的3节点处开始选择,即[3,3,...],
故"每次枚举都应从pos位置开始向后枚举"。 

 5.2 算法代码【策略一】

class Solution {List<List<Integer>> ret;int sum;List<Integer> path;public List<List<Integer>> combinationSum(int[] candidates, int target) {ret = new ArrayList<>();path = new ArrayList<>();dfs(candidates, target, 0);return ret;}public void dfs(int[] candidates, int target, int pos) {if(sum >= target) {if(sum == target) ret.add(new ArrayList<>(path));return;}for(int i = pos; i < candidates.length; i++) {sum += candidates[i];path.add(candidates[i]);//从当前位置开始枚举(因为一个数据可以多次),不能枚举前面的元素,否则重复dfs(candidates, target, i);//回溯 -> 清理现场sum -= candidates[i];path.remove(path.size() - 1);}}
}

5.3 算法原理【策略二】

  • 策略二思想:从一个位置开始枚举,然后1个、2个、....的疯狂枚举,只要 <= target,就继续枚举,当 == target时,就添加这个结果,然后递归下层。
  • 细节问题:回溯。注意回溯时,本层数据枚举完成后,再进行恢复现场操作。

5.4 算法代码【策略二】

class Solution {List<List<Integer>> ret;List<Integer> path;int sum;public List<List<Integer>> combinationSum(int[] candidates, int target) {ret = new ArrayList<>();path = new ArrayList<>();dfs(candidates, target, 0);return ret;}public void dfs(int[] candidates, int target, int pos) {if(sum >= target || pos >= candidates.length) {if(sum == target) ret.add(new ArrayList<>(path));return;}for(int k = 0; candidates[pos] * k <= target; k++) {if(k != 0) { path.add(candidates[pos]);sum += candidates[pos];}dfs(candidates, target, pos + 1);}//回溯 -> 恢复现场for(int k = 1; candidates[pos] * k <= target; k++) {sum -= candidates[pos];path.remove(path.size() - 1);}}
}

END

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 一次反射型XSS漏洞发现的过程
  • 把iconfont 图标导出为json
  • Flutter 进阶:绘制加载动画
  • (每日一问)操作系统:常见的 Linux 指令详解
  • 人机交互与现代战争
  • 顺序表之创建,判满,插入,输出
  • 设计模式之状态模式 (C++ 实现)
  • 等级保护学习
  • 掏耳勺买哪种效果好?五大可视掏耳勺测评总汇
  • 前端:HTML、CSS、JS、Vue
  • 网络层ip协议
  • 单例的饿汉式,懒汉式的线程安全问题
  • 智能代码编辑器:Visual Studio Code的深度剖析
  • k8s--关于pod方面问题的排错思路与方法
  • .NetCore+vue3上传图片 Multipart body length limit 16384 exceeded.
  • 网络传输文件的问题
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • ES6语法详解(一)
  • python 学习笔记 - Queue Pipes,进程间通讯
  • redis学习笔记(三):列表、集合、有序集合
  • Vue小说阅读器(仿追书神器)
  • 从tcpdump抓包看TCP/IP协议
  • 力扣(LeetCode)965
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 你不可错过的前端面试题(一)
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 一个JAVA程序员成长之路分享
  • raise 与 raise ... from 的区别
  • 阿里云重庆大学大数据训练营落地分享
  • #Ubuntu(修改root信息)
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • $.each()与$(selector).each()
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (3)(3.5) 遥测无线电区域条例
  • (6)添加vue-cookie
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (pycharm)安装python库函数Matplotlib步骤
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (第一天)包装对象、作用域、创建对象
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (十二)Flink Table API
  • (算法)大数的进制转换
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .Net Winform开发笔记(一)
  • .net 中viewstate的原理和使用
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .vue文件怎么使用_我在项目中是这样配置Vue的
  • ??javascript里的变量问题
  • @antv/x6 利用interacting方法来设置禁止结点移动的方法实现。
  • @ModelAttribute使用详解