算法通过村第十八关-回溯|白银笔记|经典问题

文章目录

  • 前言
  • 组合总和问题
  • 分割回文串
  • 子集问题
  • 排序问题
  • 字母大小写全排列
  • 单词搜索
  • 总结


前言


提示:我不愿再给你写信了。因为我终于感到,我们的全部通信知识一个大大的幻影,我们每个人知识再给自己写信。 --安德烈·纪德

回溯主要解决一些暴力枚举也搞不定的问题,例如组合、分割、子集、排列、棋盘等。这关我们就看看如何接入。

组合总和问题

参考题目地址:39. 组合总和 - 力扣(LeetCode)

在这里插入图片描述
如过不考虑重复的,这个题目和113的那个题目相差无疑,如果可以重复,哪呢是否可以无限制取下去呢?也不会,因为题目也给了说明。每个元素最小为1,因此最多一个target个1.我们画图该怎么做呢,对于序列{2,3,6,7}.target = 7.很明显我们选择1个2,还剩下target = 7 - 2 = 5.然后再选取一个2,还剩下target = target - 2 = 3;之后再选一个2,变成了target = target - 2 = 1,此时最小的为2,不能再选了,就要退出。看看有没有3。ok,有那么第一个结果就是{2,2,3}.

之后我们继续回退到只选1个2的时候,这是2不能选了,从{3,6,7}中选择,如下图所示,没有符合规定的。

依次类推,然后尝试从3和6、7中开始选择。

最终的结果就是{2,2,3}和{2,5}.为了方便,我们可以先对元素做个排序,然后按照上面的过程执行,形成一个树形图:

在这里插入图片描述
这个图横向是针对每个元素做暴力枚举,纵向是递归(向下找)也是纵横问题,实现起来代码也不复杂:

public List<List<Integer>> combinationSum(int[] c, int target) {List<List<Integer>> res = new ArrayList();List<Integer> path = new ArrayList();dfs(c,0,target,path,res);return res;
}public void dfs(int[] c,int u,int target,List<Integer> path,List<List<Integer>> res){if(target < 0){return;}if(target == 0){res.add(new ArrayList(path));return;}for(int i = u; i < c.length; i++){if(c[i] <= target){path.add(c[i]);dfs(c,i,target - c[i],path,res);path.remove(path.size() - 1);}   }
}

分割回文串

参考题目地址:131. 分割回文串 - 力扣(LeetCode)

在这里插入图片描述
字符串如何判断回文本身就是一道算法题,本题在其之上还要解决一个问题:如何分割?如果暴力切割很麻烦,解决起来也很困难,如果从回溯的角度去思考就很清晰:
在这里插入图片描述
我们说回溯本身仍然回进行枚举的,这里也是一样的。切割线(途中的|)切割到字符串的结尾位置,说明找到了一个切割方法。这里就是先试一试,第一次切割’a’,第二次切割’aa‘,第三次切割’aab’。这对应的就是回溯里面的for循环(也就是横向遍历)

我们还要说回溯(需要进行递归操作)第一次切合’a’,剩下的就是‘ab’.递归就是在将其且下一个回文下来,也就是第二个‘a’,剩下的‘b’再交给递归进行一步切割。这就是纵向方面要干的事,依次类推。

至于回溯操作与前面的一样的道理,不再赘述。通过代码就可以发现,切割问题的回溯搜索的过程和嘴和问题的回溯搜索的过程是差不多的。

class Solution {List<List<String>> res = new ArrayList<>();Deque<String> path = new LinkedList<>();public List<List<String>> partition(String s) {backTracking(s,0);return res;}private void backTracking(String s, int startIndex) {// 如果起始位置大于s的大小,说明找到了一组分割方案if (startIndex >= s.length()) {res.add(new ArrayList<>(path));return;}for (int i = startIndex; i < s.length(); i++) {// 如果是回文子串,则记录if (isPalindrome(s, startIndex, i)) {String str = s.substring(startIndex, i + 1);path.addLast(str);} else {continue;} // 起始位置后移,保证不重复backTracking(s, i + 1);path.removeLast();}}public boolean isPalindrome(String str,int start,int end){for(int i = start, j = end; i < j; i++,j--){if(str.charAt(i) != str.charAt(j)){return false;}}return true;}
}

子集问题

子集问题也是回溯的经典使用场景。回溯可以画成一种状态树,子集,组合,分割,都可以抽象出来。

但是子集也有它特有的类型:需要找出所有情况。

参考题目:78. 子集 - 力扣(LeetCode)

在这里插入图片描述
在这里插入图片描述
画图详解:
在这里插入图片描述
从图中也可以看出,遍历这颗树的时候,就可以记录下所有的节点,也就是得到子集结果。

那么什么时候停下来呢?起始可以不加终止条件,因为startIdex >= nums.size(),for循环就结束了。

记住:求子集问题,不需要任何的剪枝操作!因为子集就是要遍历整颗树。这样实现起来也比较容易。

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList();public List<List<Integer>> subsets(int[] nums) {// 空集也是if(nums.length == 0 ){res.add(new ArrayList<>());return res;}backTracking(nums,0);return res;} public void  backTracking(int[] nums,int startIndex){// 记住:加进来,再判断res.add(new ArrayList(path));if(startIndex >= nums.length){return;}for(int i = startIndex; i < nums.length; i++){// 今典小连招path.add(nums[i]);backTracking(nums,i+ 1);path.remove(path.size() - 1);}}
}

tips:上面的代码可以通过全局定义res和path,这样简介。这里(也可以转成参数传递的形式)

排序问题

参考题目介绍:46. 全排列 - 力扣(LeetCode)

在这里插入图片描述

排列问题也是经典的问题(每次刷是不是都难)。这个问题和前面的组合等问题的一个区别就是后面的还用再选(可重复),例如:1:开始使用了,但是到了2和3的时候仍然要使用一次。本质上是因为【1,2】和【2,1】从集合的角度看一个问题,单是从排列的角度是两个问题。

元素1在【1,2】中已经使用多了,但是在【2,1】中还要再使用一次,所以就不能使用startIndex了,此时可以使用一个used数组来标记已经选择的元素,完整的过程如图所示:

在这里插入图片描述
开图就知道了,怎么确定终止条件呢?就是到达叶子节点,那么是么时候到达叶子节点呢?

当时收集元素的数组path的大小达到和nums数组一样不就行了,说明就找到了一个全排列,也就是到达叶子节点了。

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();// 是否选boolean[] used ; public List<List<Integer>> permute(int[] nums) {if(nums.length == 0){return res;}used = new boolean[nums.length];permuteHepler(nums);return res;}public  void permuteHepler(int[] nums){if(path.size() == nums.length){res.add(new ArrayList<>(path));return ;}for(int i = 0; i < nums.length; i++){//  解决01 10 问题if(used[i]){continue;}used[i] = true;path.add(nums[i]);permuteHepler(nums);//  经典回溯path.remove(path.size() - 1);used[i] = false;}}
}

字母大小写全排列

参考题目地址:784. 字母大小写全排列 - 力扣(LeetCode)

在这里插入图片描述

如果本题去掉数组,只告诉你两个字母ab,然你每个字母变化大小写,那么就是ab,Ab,aB,AB四种情况。就和上面的处理一样了。这里有数字的干扰,我们就需要作过滤数字,只处理字母。还有就是添加大小写的转换问题了。
在这里插入图片描述

由于每个字符的大小形式刚好相差32,因此再大小写里面可以换成用c^32来进行转换和恢复。这样代码就简洁多了:

class Solution {List<String> res = new ArrayList<>();public List<String> letterCasePermutation(String s) {dfs(s.toCharArray(),0);return res;}public void dfs(char[] arr, int pos){// 过滤数字while(pos < arr.length && Character.isDigit(arr[pos])){pos++;}// 终止条件if(pos == arr.length){res.add(new String(arr));return;}// 转换arr[pos]^=32;dfs(arr,pos + 1);// 撤销arr[pos]^=32;dfs(arr,pos + 1);}
}

单词搜索

参考题目介绍:79. 单词搜索 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述

思路:从上到下,左到右遍历网格,每个坐标递归调用check(i,j,k)函数,i,j表示网格坐标,k表示word的第k个字符,如果能搜索到第k个字符返回true,否则返回false。

check函数的终止条件也很明确:

  1. 如果i,j的位置和字符上的字符位置k不相符,也就是说这个方面的路径有问题,直接返回false
  2. 如果搜索到了字符串的结尾,则找到了网格中的一条路径,这条路径上的字符正好可以组成字符串s,返回true。

两种情况都不满足,就把当前网格加入visited数组,visited表示该位置已经访问过了,然后顺着当前网格的坐标的四个方向继续尝试,如果没有找到k开始的子串,则返回回溯状态visited[i ] [j] = false,继续向后面访问。

分析一波复杂度:时间复杂度为O(ML*3^L),M,N 为网格的长度和宽度,L 为字符串word的长度,第一次电泳check函数的时候,回向4个方向进行测试,其余坐标的节点都是3个方向检车,走过的分支不会反方向回去,左移check函数的复杂度为3^L,而网格上面有MN个坐标,且存在剪枝,最坏的情况下是O(MN * 3 ^L).空间复杂符是O(MN) visited数组的空间是O(MN) ,check递归栈的最大深度在最坏的情况下是O(MN)

class Solution {public boolean exist(char[][] board, String word) {char[] words = word.toCharArray();for(int i = 0; i < board.length; i++){for(int j  = 0; j < board[i].length;j++){if(dfs(board,words,i,j,0)){return true;}}}return false;}public boolean dfs(char [][] board,char[] words,int i,int j, int k){// 边界+条件if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != words[k]){return false;}// 终止条件if(k ==  words.length - 1){return true;}// 防止重复board[i][j] = '\0';boolean res = dfs(board,words,i+1,j,k+1) || dfs(board,words,i - 1,j,k+1) || 							dfs(board,words,i,j - 1,k+1)  ||  dfs(board,words,i,j+ 1,k+1);// 恢复board[i][j] = words[k];return res;}
}

总结

提示:组合总和问题;分割问题;子集问题;排列问题;搜索问题


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/136181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

13 # 手写 concat 方法

concat 的使用 concat() 方法用于合并两个或多个数组。此方法不会更改现有数组&#xff0c;而是返回一个新数组。如果省略了所有参数&#xff0c;则 concat 会返回调用此方法的现存数组的一个浅拷贝。 <script>var arr1 ["k", "a", "i"…

2023年眼镜行业分析(京东眼镜销量数据分析):市场规模同比增长26%,消费需求持续释放

随着我国经济的不断发展&#xff0c;电子产品不断普及&#xff0c;低龄及老龄人口的用眼场景不断增多&#xff0c;不同年龄阶段的人群有不同的视力问题&#xff0c;因此&#xff0c;视力问题人口基数也随之不断加大&#xff0c;由此佩戴眼镜的人群也不断增多。 同时&#xff0c…

【凡人修仙传】预计开播倒计时,线下举办超前观影活动,隆重期待

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料凡人最新资讯&#xff0c;《凡人修仙传》这部备受期待的动漫作品&#xff0c;终于在新年之际宣布了定档日期。据悉&#xff0c;该动漫将于11月25日&#xff0c;也就是周六上午11点&#xff0c;与广大…

Linux搭建我的世界MC服务器 【Minecraft外网联机教程】

文章目录 前言1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 前言 Li…

通过创建自定义标签来扩展HTML

使用HTML时&#xff0c;例如&#xff0c;使用<b>标记显示粗体文本。 如果需要列表&#xff0c;则对每个列表项使用<ul>标记及其子标记<li> 。 标签由浏览器解释&#xff0c;并与CSS一起确定网页内容的显示方式以及部分内容的行为。 有时&#xff0c;仅使用一…

什么是前台、中台、和后台?

前台&#xff1a;即包括与用户直接交互的界面&#xff0c;如&#xff1a;web页、app&#xff1b;也包括服务端各种实时响应用户请求的业务逻辑&#xff0c;如&#xff1a;商品查询、订单系统等。 后台&#xff1a;面向内部运营人员的管理系统、配置系统&#xff0c;如&#xf…

Leetcode---370周赛

题目列表 2923. 找到冠军 I 2924. 找到冠军 II 2925. 在树上执行操作以后得到的最大分数 2926. 平衡子序列的最大和 一、找到冠军I 第一题模拟题&#xff0c;简单来说是看每一行(列)是否全是1&#xff0c;当然不包括自己比自己强的情况&#xff0c;需要特判 代码如下 …

SpringCloudAlibaba——Sentinel

Sentinel也就是我们之前的Hystrix&#xff0c;而且比Hystrix功能更加的强大。Sentinel是分布式系统的流量防卫兵&#xff0c;以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级等多个维度保护服务的稳定性。 Sentinel采用的是懒加载&#xff0c;这个接口被访问一次&a…

react 实现chatGPT的打印机效果 兼容富文本,附git地址

1、方式一 &#xff1a;使用插件 typed.js typed.js 网站地址&#xff0c;点我打开 1.1、核心代码如下&#xff1a; //TypeWriteEffect/index.tsx 组件 import React, { useEffect, useRef } from react; import Typed from typed.js; import { PropsType } from ./index.d;…

算法进阶指南图论 道路与航线

其实再次看这题的时候。想法就是和强连通分量有关&#xff0c;我们很容易发现&#xff0c;题目中所说的双向边&#xff0c;就构成了一个强连通分量&#xff0c;而所谓的单向边&#xff0c;则相当于把强连通分量进行缩点&#xff0c;然后整个图成为了一个DAG&#xff0c;众所周知…

MongoDB副本集特点验证

MongoDB副本集特点验证 mogodb副本集概述副本集搭建副本集结构验证结果源码地址 mogodb副本集概述 MongoDB副本集是将数据同步在多个服务器的过程。 复制提供了数据的冗余备份&#xff0c;并在多个服务器上存储数据副本&#xff0c;提高了数据的可用性&#xff0c; 并可以保证…

2022ICPC济南站

K Stack Sort 题意&#xff1a;给你一个长度为n的排列&#xff0c;设有m个栈&#xff0c;你需要将这n个数按出现顺序入栈&#xff0c;每次入栈操作从m个栈中选择一个栈从栈顶入栈。当所有元素入栈完成后&#xff0c;需要不断选择栈&#xff0c;将栈中元素弹空。需满足出栈顺序…

【不正经操作】百度深度学习框架paddlepaddle本地运行-Python环境配置笔记

百度深度学习框架PaddlePaddle 百度深度学习框架PaddlePaddle是一个支持深度学习和机器学习的开源框架。它由百度公司于2016年开发并发布&#xff0c;现在已经成为中国最受欢迎的深度学习框架之一&#xff0c;并且在国际上也获得了不少关注。 特点与功能 易于使用 PaddlePa…

ARMday01(计算机理论、ARM理论)

计算机理论 计算机组成 输入设备、输出设备、运算器、控制器、存储器 1.输入设备&#xff1a;将编写好的软件代码以及相关的数据输送到计算机中&#xff0c;转换成计算机能够识别、处理和存储的数据形式 键盘、鼠标、手柄、扫描仪、 2.输出设备&#xff1a;将计算机处理好的数…

解决 github.com 或者 raw.githubusercontent.com 打开慢、打不开问题

系列文章目录 文章目录 系列文章目录前言一、Windows 系统1.1 以管理员身份运行记事本1.2 打开 hosts 文件1.3 写入 IP 地址 二、Linux2.1 hosts 文件位置 三、Android 系统总结 前言 连接手机热点&#xff0c;将流量关闭马上打开&#xff0c;进入的成功率更高 一、Windows 系…

Netty入门指南之NIO 粘包与半包

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言问题产…

深度学习读取txt训练数据绘制参数曲线图的方法

有一些深度学习模型是并不像yolo系列那样最终输出相应的参数图&#xff0c;有很多训练形成了一个训练log文件&#xff0c;于是需要读取log文件中的内容并绘制成曲线图。 如下实例&#xff0c;有一个log文件的部分截图&#xff0c;需要将其读取出来并绘制曲线图 废话不多说&…

前端之Bootstrap框架

目录 【一】Bootstrap介绍 【二】Bootstrap引入 【1】CDN加速链接 【2】注意 【三】布局容器 【四】栅格系统 【五】栅格参数 【六】列偏移 【七】排版 标题 内联文本元素 对齐 改变大小写 引用 列表 【八】表格 基本实例 条纹状表格 带边框的表格 鼠标悬停…

汽车工业生产线数字孪生可视化管理平台,赋予工厂车间数字化智慧化管理

在工业4.0 的时代背景下&#xff0c;随着企业数字化进程的推进&#xff0c;数字孪生可视化技术逐渐在汽车行业得到广泛应用&#xff0c;数字孪生智慧工厂的建设也成为了汽车行业数字化转型的趋势之一。汽车制造业属于典型的离散制造行业&#xff0c;汽车生产包含冲压、焊接、涂…

19.13 Boost Asio 发送TCP流数据

Boost框架中默认就提供了针对TCP流传输的支持&#xff0c;该功能可以用来进行基于文本协议的通信&#xff0c;也可以用来实现自定义的协议。一般tcp::iostream会阻塞当前线程&#xff0c;直到IO操作完成。 首先来看服务端代码&#xff0c;如下所示在代码中首先通过GetFileSize…