LeetCode——回溯算法(Java)

回溯算法

  • 简介
  • [中等] 77. 组合
  • [中等] 216. 组合总和 III
  • [中等] 17. 电话号码的字母组合
  • [中等] 39. 组合总和
  • [中等] 40. 组合总和 II
  • [中等] 131. 分割回文串
  • [中等] 93. 复原 IP 地址
  • [中等] 78. 子集
  • [中等] 90. 子集 II
  • [中等] 491. 非递减子序列
  • [中等] 46. 全排列
  • [中等] 47. 全排列 II
  • [困难] 51. N 皇后
  • [困难] 37. 解数独

简介

记录一下自己刷题的历程以及代码。写题过程中参考了 代码随想录的刷题路线。会附上一些个人的思路,如果有错误,可以在评论区提醒一下。

回溯题模板:

class Solution {public 主方法(参数) {//把递归参数传入递归函数recursion(0);return ans;}public void recursion(int n){if(达到终止条件) {//存放结果ans.add(answer);return;}for(循环调用后续的递归){//递归前处理,比如把当前数加入答案集合recursion(n + 1);//递归后处理,回溯,撤销递归前处理的内容}}
}

[中等] 77. 组合

原题链接

注意每次ans.add()时需要添加nums的深拷贝,而不是单纯把当前nums放入,否则放入的其实是nums数组的引用,最后ans的所有元素实际都指向了同一个数组。

class Solution {public List<List<Integer>> combine(int n, int k) {List<List<Integer>> ans = new ArrayList<>();List<Integer> nums = new ArrayList<>();recursion(ans, nums, 1, n, k);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> nums, int p, int n, int k){//终止条件if(nums.size() == k){ans.add(new ArrayList<>(nums));return;}for(; p <= n; p++){nums.add(p);recursion(ans, nums, p+1, n, k);nums.remove(nums.size() - 1);}return;}
}

剪枝操作: k - nums.size()是还需要多少个元素构成一组答案

for(; p <= n - (k - nums.size()) + 1; p++){nums.add(p);recursion(ans, nums, p+1, n, k);nums.remove(nums.size() - 1);}

[中等] 216. 组合总和 III

原题链接

sum函数直接用参数替代也可以

class Solution {public List<List<Integer>> combinationSum3(int k, int n) {List<Integer> nums = new ArrayList<>();List<List<Integer>> ans = new ArrayList<>();recursion (ans, nums, k, n, 1);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> nums, int k, int n, int p){if(nums.size() == k){if(sum(nums) == n)ans.add(new ArrayList<>(nums));return;}if(sum(nums) < n) {for (; p <= 9; p++) {nums.add(p);recursion(ans, nums, k, n, p + 1);nums.remove(nums.size() - 1);}}}public int sum(List<Integer> nums){int sum = 0;for(Integer i: nums){sum += i;}return sum;}
}

[中等] 17. 电话号码的字母组合

原题链接

使用StringBuilder做字符串操作

class Solution {public List<String> letterCombinations(String digits) {StringBuilder sb = new StringBuilder();List<String> ans = new ArrayList<>();String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};if(digits.length() == 0) return ans;recursion(digits, sb, ans, numString, 0);return ans;}public void recursion(String digits, StringBuilder sb, List<String> ans, String[] numString, int p){if(p == digits.length()){ans.add(sb.toString());return;}for(int i = 0; i < numString[digits.charAt(p) - '0'].length(); i++) {sb.append(numString[digits.charAt(p) - '0'].charAt(i));recursion(digits, sb, ans, numString, p + 1);sb.deleteCharAt(p);}}
}

[中等] 39. 组合总和

原题链接

class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> ans = new ArrayList<>();List<Integer> nums = new ArrayList<>();recursion(ans, nums, candidates, target, 0, 0);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> nums, int[] candidates, int target, int sum, int p){if(sum > target) return;//终止条件if(sum == target){ans.add(new ArrayList<>(nums));return;}for(int i = p; i < candidates.length; i++){nums.add(candidates[i]);recursion(ans, nums, candidates, target, sum+candidates[i], i);nums.remove(nums.size() - 1);}return;}
}

[中等] 40. 组合总和 II

原题链接

对原数组进行排序,相同的数字顺序存放,对相同数字的使用保证从左到右。比如选择[1,1,6] 中的 [1,6] 作为答案,只会选到第一个1

class Solution {public List<List<Integer>> combinationSum2(int[] candidates, int target) {Arrays.sort(candidates);List<List<Integer>> ans = new ArrayList<>();List<Integer> nums = new ArrayList<>();int[] flag = new int[110];recursion(ans, nums, candidates, flag, target, 0, 0);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> nums, int[] candidates, int[] flag, int target, int sum, int p){if(sum > target) return;//终止条件if(sum == target){ans.add(new ArrayList<>(nums));return;}for(int i = p; i < candidates.length; i++){//存在与之相同的前一个数,且前一个数还没被取过,则不会取当前数if(i>0 && flag[i-1] == 0 && candidates[i-1] == candidates[i]) continue;nums.add(candidates[i]);flag[i] = 1;recursion(ans, nums, candidates, flag, target, sum+candidates[i], i+1);flag[i] = 0;nums.remove(nums.size() - 1);}return;}
}

[中等] 131. 分割回文串

原题链接

这个递归自己想还想了挺久的,就是从左往右切割,终止条件就是判断有没有切到最右端
在这里插入图片描述

class Solution {public List<List<String>> partition(String s) {List<List<String>> ans = new ArrayList<>();List<String> group = new ArrayList<>();if(s == null || s.length() == 0) return ans;recursion(ans, group, s, 0);return ans;}private void recursion(List<List<String>> ans, List<String> group, String s, int start) {if(start == s.length()) {ans.add(new ArrayList<>(group));return;}for(int i = start; i < s.length(); i++) {if(isPalindrome(s, start, i)) {group.add(s.substring(start, i + 1));recursion(ans, group, s, i + 1);group.remove(group.size() - 1);}}}private boolean isPalindrome(String s, int begin, int end) {while(begin < end) {if(s.charAt(begin++) != s.charAt(end--)) return false;}return true;}
}

[中等] 93. 复原 IP 地址

原题链接

注意分割段到4时也需要return,往后的递归都是无意义的

class Solution {public List<String> restoreIpAddresses(String s) {List<String> ans = new ArrayList<>();List<String> answer = new ArrayList<>();if(s == null || s.length() == 0) return ans;//p表示目前划分到第几段,ip地址总共四段, index表示s下标recursion(ans, s, 0, answer);return ans;}public void recursion(List<String> ans, String s, int index, List<String> answer){if(answer.size() == 4 && index == s.length()){ans.add(createAnswer(answer));}if(index == s.length() || answer.size() == 4) return;if(s.charAt(index) == '0'){answer.add("0");recursion(ans, s, index + 1, answer);answer.remove(answer.size() - 1);}else {for (int i = index; i < s.length() && i - index <= 3; i++) {String string = s.substring(index, i + 1);int num = Integer.parseInt(string);if (num >= 0 && num <= 255){answer.add(string);recursion(ans, s, i + 1, answer);answer.remove(answer.size() - 1);}}}}public String createAnswer(List<String> answer){StringBuilder sb = new StringBuilder();for(int i = 0; i < answer.size(); i++){if(i != 0){sb.append(".");}sb.append(answer.get(i));}return sb.toString();}
}

[中等] 78. 子集

原题链接

一道标准的子集问题模板题,子集是收集树形结构中树的所有节点的结果。
而组合问题、分割问题是收集树形结构中叶子节点的结果。子集问题不需要在找到一组答案之后就进行回退。

class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> ans = new ArrayList<>();List<Integer> answer = new ArrayList<>();recursion(ans, answer, nums, 0);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> answer, int[] nums, int index){ans.add(new ArrayList<>(answer));for(int i = index; i < nums.length; i++){answer.add(nums[i]);recursion(ans, answer, nums, i + 1);answer.remove(answer.size() - 1);}}
}

[中等] 90. 子集 II

原题链接

class Solution {public List<List<Integer>> subsetsWithDup(int[] nums) {List<List<Integer>> ans = new ArrayList<>();List<Integer> answer = new ArrayList<>();boolean[] used = new boolean[nums.length];Arrays.sort(nums);recursion(ans, answer, used, nums, 0);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> answer, boolean[] used, int[] nums, int index){ans.add(new ArrayList<>(answer));for(int i = index; i < nums.length; i++){if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){continue;}used[i] = true;answer.add(nums[i]);recursion(ans, answer, used, nums, i + 1);answer.remove(answer.size() - 1);used[i] = false;}}
}

[中等] 491. 非递减子序列

原题链接

不能简单的强制类似[1,1,1,1,1]序列从左到右选,这道题不能排序,有可能会有[1,1,2,1,1],这样2之前的1没取的时候2后面的1也可以取,需要用集合set来做去重

class Solution {public List<List<Integer>> findSubsequences(int[] nums) {List<List<Integer>> ans = new ArrayList<>();List<Integer> answer = new ArrayList<>();recursion(ans, answer, nums, 0);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> answer, int[] nums, int index){if(answer.size() > 1)ans.add(new ArrayList<>(answer));HashSet<Integer> hs = new HashSet<>();for(int i = index; i < nums.length; i++){if(hs.contains(nums[i]) || (answer.size() > 0 && nums[i] < answer.get(answer.size() - 1)))continue;hs.add(nums[i]);answer.add(nums[i]);recursion(ans, answer, nums, i + 1);answer.remove(answer.size() - 1);}}
}

[中等] 46. 全排列

原题链接

开一个数组标记不重复取数,每次循环都从头开始即可

class Solution {public List<List<Integer>> permute(int[] nums) {List<List<Integer>> ans = new ArrayList<>();List<Integer> answer = new ArrayList<>();boolean[] used = new boolean[nums.length];recursion(ans, answer, used, nums);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> answer, boolean[] used, int[] nums){if(answer.size() == nums.length) {ans.add(new ArrayList<>(answer));return;}for(int i = 0; i < nums.length; i++){if(used[i]) continue;used[i] = true;answer.add(nums[i]);recursion(ans, answer, used, nums);answer.remove(answer.size() - 1);used[i] = false;}}
}

[中等] 47. 全排列 II

原题链接

对数组排序,并对相同数字,保证从左到右取数,同时不重复取数

class Solution {public List<List<Integer>> permuteUnique(int[] nums) {List<List<Integer>> ans = new ArrayList<>();List<Integer> answer = new ArrayList<>();boolean[] used = new boolean[nums.length];Arrays.sort(nums);recursion(ans, answer, used, nums);return ans;}public void recursion(List<List<Integer>> ans, List<Integer> answer, boolean[] used, int[] nums){if(answer.size() == nums.length) {ans.add(new ArrayList<>(answer));return;}for(int i = 0; i < nums.length; i++){if(used[i]) continue;if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;used[i] = true;answer.add(nums[i]);recursion(ans, answer, used, nums);answer.remove(answer.size() - 1);used[i] = false;}}
}

[困难] 51. N 皇后

原题链接

class Solution {public List<List<String>> solveNQueens(int n) {List<List<String>> ans = new ArrayList<>();//初始化棋盘,声明一个flag数组,标记每一行填充的元素在哪一列,初始化为-1int[] flag = new int[n];for(int i = 0; i < n; i++){flag[i] = -1;}recursion(ans, flag, 0);return ans;}public void recursion(List<List<String>> ans, int[] flag, int row){if(row == flag.length){ans.add(createAnswer(flag));return;}for(int i = 0; i < flag.length; i++){if(isFree( flag, row, i)){flag[row] = i;recursion(ans, flag, row + 1);flag[row] = -1;}}}//可否放置皇后棋子的判定public boolean isFree(int[] flag, int row, int p){//对角线判定 以及 列未判定for(int i = 0; i < row; i++ ) {if(flag[i] == p) return false;if (flag[i] - i == p - row || flag[i] + i == row + p) {return false;}}return true;}//创建答案public List<String> createAnswer(int[] flag) {List<String> answer = new ArrayList<>();for (int i = 0; i < flag.length; i++) {StringBuilder sb = new StringBuilder();for (int j = 0; j < flag.length; j++) {if (flag[i] == j) {sb.append("Q");} else {sb.append(".");}}answer.add(sb.toString());}return answer;}
}

[困难] 37. 解数独

原题链接

代码随想录给到的是一种暴力二维搜索,是比较容易想到的,每次递归都去判断行、列、九宫格里是否重复,这样效率会比较低,可以牺牲空间来简化这部分的判断,分别开设三个二维数组,记录每一行每一列每一个九宫格,1-9的数字是否被使用过,后续比较直接从数组里去读取信息就可以。

class Solution {public void solveSudoku(char[][] board) {//行、列、九宫格是否被占用boolean[][] rowOccupy = new boolean[9][9];boolean[][] colOccupy = new boolean[9][9];boolean[][] nineOccupy = new boolean[9][9];char a = board[1][2];for(int i = 0; i < 9; i++){for(int j = 0; j < 9; j++){char ch = board[i][j];if(ch != '.') {rowOccupy[i][ch - '1'] = true;colOccupy[j][ch - '1'] = true;nineOccupy[i / 3 * 3 + j / 3][ch - '1'] = true;}}}recursion(rowOccupy, colOccupy, nineOccupy, board, 0, 0);}public boolean recursion(boolean[][] rowOccupy, boolean[][] colOccupy, boolean[][] nineOccupy, char[][]board, int i, int j){if(i > 8 || j > 8) return true;if(board[i][j] != '.'){if(j == 8) {if (recursion(rowOccupy, colOccupy, nineOccupy, board, i + 1, 0)) {return true;}}else if (recursion(rowOccupy, colOccupy, nineOccupy, board, i, j + 1)) {return true;}}else {for (int k = 1; k <= 9; k++) {if (!rowOccupy[i][k - 1] && !colOccupy[j][k - 1] && !nineOccupy[i / 3 * 3 + j / 3][k - 1]) {board[i][j] = String.valueOf(k).charAt(0);rowOccupy[i][k - 1] = true;colOccupy[j][k - 1] = true;nineOccupy[i / 3 * 3 + j / 3][k - 1] = true;if (j == 8) {if (recursion(rowOccupy, colOccupy, nineOccupy, board, i + 1, 0)) {return true;}}else if (recursion(rowOccupy, colOccupy, nineOccupy, board, i, j + 1)) {return true;}board[i][j] = '.';rowOccupy[i][k - 1] = false;colOccupy[j][k - 1] = false;nineOccupy[i / 3 * 3 + j / 3][k - 1] = false;}}}return false;}
}

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

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

相关文章

【学习笔记】Vue3源码解析:第二部分-实现响应式(4)

课程地址&#xff1a;【已完结】全网最详细Vue3源码解析&#xff01;&#xff08;一行行带你手写Vue3源码&#xff09; 第二部分-实现响应式&#xff08;4&#xff09;&#xff1a;&#xff08;对应课程的第15-17节&#xff09; 第15节&#xff1a;《处理数组修改或者新增》 …

Qt 使用QListView实现简约美观的聊天窗口

今天和大家分享一个使用QListView来展现聊天窗口的历史记录的例子, 因为聊天记录可能会有很多, 所以使用试图-模型的方式更加合理 这是最终效果: ChatHistoryModel继承自QAbstractListModel , ChatHistoryViewDelegate继承自QStyledItemDelegate, 这个例子最关键的就是在QSty…

【Java JVM】对象回收判断

Java 对象回收判断是程序设计中至关重要的一环。在面向对象的编程中, 合理的对象回收策略直接影响着程序的性能和内存利用效率。 因此, 深入了解和准确判断 Java 对象的回收时机, 不仅可以优化程序的运行性能, 还能有效避免内存泄漏和资源浪费。 本文将简单的分析一下 JVM 中对…

JRT缓存协议测试

JRT由DolerGet提供可信的缓存数据获取&#xff0c;在OMR修改和删除数据后会更新缓存的数据&#xff0c;这样的话本Web下通过DolerGet取的数据是可信的。在多个Web之间要保证缓存数据的可信度&#xff0c;需要同步修改的数据到其他Web&#xff0c;为此仿照了缓存协议的效果&…

MySQL的三大范式

文章目录 简介第一范式第二范式第三范式&#xff1a; 简介 在MySQL的使用中&#xff0c; 要根据实际灵活设计表&#xff0c;一般来说我们通常遵循三大范式&#xff08;啥是范式&#xff1a;是一些约束、规范、规则&#xff0c; 来优化数据库表的设计和存储&#xff09;,三大范…

Stable Diffusion 模型分享:3D Animation Diffusion(3D动漫)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 3D Animation Diffusion 是 Lykon 大神的 3D 动漫模型。 作者述&#xff1a;在迪士尼、皮…

停止Tomcat服务的方式

运行脚本文件停止 运行Tomcat的bin目录中提供的停止服务的脚本文件 关闭命令 # sh方式 sh shutdown.sh# ./方式 ./shutdown.sh操作步骤 运行结束进程停止 查看Tomcat进程&#xff0c;获得进程id kill进程命令 # 执行命令结束进程 kill -9 65358 操作步骤 注意 kill命令是…

ffmpeg使用vaapi解码后的视频如何基于x11或EGL实现0-copy渲染?

技术背景 对于ffmpeg硬解码后渲染常见的做法是解码后通过av_hwframe_transfer_data方法将数据从GPU拷贝到CPU&#xff0c;然后做一些转换处理用opengl渲染&#xff0c;必然涉及到譬如类似glTexImage2D的函数将数据上传到GPU。而这样2次copy就会导致CPU的使用率变高&#xff0c…

智能物联时代下RFID技术在汽车零部件智能制造中的引领作用

RFID&#xff08;Radio Frequency Identification&#xff0c;射频识别&#xff09;技术在汽车零部件加工中有广泛的应用&#xff0c;其工作原理是通过无线电频率进行自动识别。在汽车零部件加工中&#xff0c;RFID技术可以发挥重要作用&#xff0c;提高生产效率、降低成本和减…

js形参传递特殊字符

在前端我们给其他页面传值或者传数据到后台的时候&#xff0c;字符串经常将一些特殊符号识别成字符集。这种情况下会将数据打断或者打乱&#xff0c;比如字符串里面包含*/&这些符号的时候就会错误。 我们可以通过将字符中的特殊字符替换成十六进制的字符&#xff0c;一些特…

【Linux从青铜到王者】进程信号

——————————————————————————————————————————— 信号入门 在了解信号之前有许多要理解的相关概念 我们可以先通过一个生活例子来初步认识一下信号 1.生活角度的信号 你在网上买了很多件商品&#xff0c;再等待不同商品快递的到来…

从事测绘地信,你需要这些插件、软件、小工具、图源...

特别声明&#xff0c;本篇是来自公众号GIS前沿的资源&#xff0c;看着比较好&#xff0c;特别给大家推荐。加粗样式 今天&#xff0c;我们又来汇总了一些工作中实用的插件、小工具、数据等等&#xff0c;小助手又来帮你提高工作效率了****。 因为小助手每年都会总结一次&…

15.Django总结

文章目录 1.Django创建项目的命令2.MVC,MVT的理解3.Django中间件的使用4.WSGI,uWSGI服务器 和 uwsgi协议5.nginx和uWISG 服务器之间如何配合工作的6.django开发中数据库做过什么优化7.Python中三大框架各自的应用场景8.django如何提升性能(高并发)9. 什么是restful api谈谈你的…

MySQL性能优化-数据库调优有哪些维度可以选择

数据库调优的目标 简单来说&#xff0c;数据库调优的目的就是要让数据库运行得更快&#xff0c;也就是说响应的时间更快&#xff0c;吞吐量更大。 不过随着用户量的不断增加&#xff0c;以及应用程序复杂度的提升&#xff0c;我们很难用“更快”去定义数据库调优的目标&#…

Stable Diffusion ———LDM、SD 1.0, 1.5, 2.0、SDXL、SDXL-Turbo等版本之间关系现原理详解

前言 2021年5月&#xff0c;OpenAI发表了《扩散模型超越GANs》的文章&#xff0c;标志着扩散模型&#xff08;Diffusion Models&#xff0c;DM&#xff09;在图像生成领域开始超越传统的GAN模型&#xff0c;进一步推动了DM的应用。 然而&#xff0c;早期的DM直接作用于像素空…

cmd模式下启动mysql

1.打开cmd输入services.msc&#xff0c;找到MYSQL&#xff0c;右击属性&#xff0c;找到可执行文件路径&#xff0c;加载到环境变量。 2.打开cmd&#xff0c;启动MYSQL&#xff1a;输入net start mysql; 3.登陆MYSQL&#xff0c;需要管理权限&#xff1b; 输入&#xff1a;my…

Docker容器化解决方案

什么是Docker&#xff1f; Docker是一个构建在LXC之上&#xff0c;基于进程容器的轻量级VM解决方案&#xff0c;实现了一种应用程序级别的资源隔离及配额。Docker起源于PaaS提供商dotCloud 基于go语言开发&#xff0c;遵从Apache2.0开源协议。 Docker 自开源后受到广泛的关注和…

数据链路层----滑动窗口协议的相关计算

目录 1.窗口大小的相关计算 •停等协议&#xff1a; •后退N帧协议&#xff1a; •选择重传协议&#xff1a; 2.信道利用率相关计算 •停等协议的信道利用率&#xff1a; •连续ARQ&#xff08;后退N帧协议&#xff0c;选择重传协议&#xff09;的信道利用率&#xff1a;…

工具函数模板题(蓝桥杯 C++ 代码 注解)

目录 一、Vector容器&#xff1a; 二、Queue队列 三、Map映射 四、题目&#xff08;快递分拣 vector&#xff09;&#xff1a; 代码&#xff1a; 五、题目&#xff08;CLZ银行问题 queue&#xff09;&#xff1a; 代码&#xff1a; 六、题目&#xff08;费里的语言 map&…

每日学习总结20240301

20240301 1. strchr VS strrchr strchr和strrchr是C语言标准库中的字符串处理函数&#xff0c;用于在字符串中查找特定字符的位置。 1.1 strchr函数 strchr函数用于在字符串中查找第一次出现指定字符的位置&#xff0c;并返回该位置的指针。函数原型如下&#xff1a; char…