char java 回文_LeetCode_回溯算法刷题笔记(Java)

在刷LeetCode的时候,看到代码随想录上关于回溯算法的总结很到位,并且提供了总结的pdf。所以就下载了该pdf,对照着顺序刷了下题。原作者是用C++刷的,我主要参考了思想,然后用Java写的。我就不写思路,感兴趣的Java开发同学,可以到该公众号上下载对应pdf,然后对照下我的代码看看。在此说明下,本文只是个刷题记录,没有详细的思路讲解。

1d3c5fa34368aaae3cebe84bdde00266.png

模板:

void backtracking(参数){

if(结束条件){

记录结果;

return;

}

for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){

处理节点;

backtracking(参数);

回溯,撤销之前记录的数据;

}

}

复制代码

for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历

组合

组合是无序的,所以不能重复。比如{1,2}、{2,1}在组合中会被认为是相同的,而在排序中却是不同的。

所以在组合的过程中需要去除重复部分,即 剪枝。

难度中等493

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2

输出:

[

[2,4],

[3,4],

[2,3],

[1,2],

[1,3],

[1,4],

]

复制代码

解题

这里使用了个startIndex,来剪枝。

class Solution{

public List> combine(int n, int k) {

List> result=new ArrayList();

if(n==0)return result;

backtracking(n,0,k,1,new ArrayList(k),result);

return result;

}

private void backtracking(int n,int deep,int k,int startIndex,List tempList,List> result){

if(deep==k){

result.add(new ArrayList(tempList));

return ;

}

for(int i =startIndex;i<=n;i++){

tempList.add(i);

//startIndex=i+1;剪枝

//递归

backtracking(n,deep+1,k,i+1,tempList,result);

//回溯

tempList.remove(tempList.size()-1);

}

}

}

复制代码

06cfe5eca7c2962e59bd2cbe7fc614c1.png

优化

如果最终组成的个数是达不到K个,这部分也可以剪去。

如何剪去?即:当前个数+剩余可用元素>K才可继续组合。用上面的代码体现为:

tempList.size()+(n-i+1)>k

复制代码

可转换成

n-k+tempList.size()+1>i

复制代码

所以原先代码的for循环优化为如下:

for(int i =startIndex;i<=n-(k-tempList.size())+1;i++){

...

}

复制代码

优化后代码:

class Solution{

public List> combine(int n, int k) {

List> result=new ArrayList();

if(n==0)return result;

backtracking(n,0,k,1,new ArrayList(),result);

return result;

}

private void backtracking(int n,int deep,int k,int startIndex,List tempList,List> result){

if(deep==k){

result.add(new ArrayList(tempList));

return ;

}

for(int i =startIndex;i<=n-(k-tempList.size())+1;i++){

tempList.add(i);

backtracking(n,deep+1,k,i+1,tempList,result);

tempList.remove(tempList.size()-1);

}

}

}

复制代码

8566870603a00bb3b77107557b783357.png

难度中等261

找出所有相加之和为 n 的 k* 个数的组合**。***组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。

解集不能包含重复的组合。

示例 1:

输入: k = 3, n = 7

输出: [[1,2,4]]

复制代码

示例 2:

输入: k = 3, n = 9

输出: [[1,2,6], [1,3,5], [2,3,4]]

复制代码

解法

class Solution{

final int MAX_INT=9;

public List> combinationSum3(int k, int n) {

List> result=new ArrayList();

backtracking(0,k,0,n,1,new ArrayList(),result);

return result;

}

private void backtracking(int sum,int k,int deep,int n,int startIndex,List temp,List> result){

if(deep==k){

//个数为k,总和是n才符合

if(sum==n){

result.add(new ArrayList(temp));

}

return;

}

for(int i =startIndex;i<=MAX_INT;i++){

//如果总和已经超过了n,后面就没必要了-->剪枝

if(sum+i>n)continue;

sum=sum+i;

temp.add(i);

//startIndex=i+1;避免出现重复元素

backtracking(sum,k,deep+1,n,i+1,temp,result);

//回溯

temp.remove(temp.size()-1);

sum=sum-i;

}

}

}

复制代码

fbc94d66beba76a7ee494637bc5ba04e.png

难度中等1128

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

772ed7ef72dd1089b717a3b28cb2886e.png

示例 1:

输入:digits = "23"

输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

复制代码

示例 2:

输入:digits = ""

输出:[]

复制代码

示例 3:

输入:digits = "2"

输出:["a","b","c"]

复制代码

提示:

0 <= digits.length <= 4

digits[i] 是范围 ['2', '9'] 的一个数字。

解法

class Solution{

private char[][] lettersArray=

{

{'a','b','c'},{'d','e','f'},

{'g','h','i'},{'j','k','l'},{'m','n','o'},

{'p','q','r','s'},{'t','u','v'},{'w','x','y','z'}

};

public List letterCombinations(String digits){

List result=new ArrayList();

if(digits.length()==0) return result;

char[] chars=digits.toCharArray();

backtracking(chars,0,new char[chars.length],result);

return result;

}

private void backtracking(char[] digits,int deep,char[] temp,List result){

if(deep==temp.length){

result.add(new String(temp));

return;

}

for(char item :lettersArray[digits[deep]-'2']){

temp[deep]=item;

backtracking(digits,deep+1,temp,result);

//不用回溯,会被覆盖

}

}

}

复制代码

1683e52ba6d1fe5fc76cc78d8c7b8df9.png

难度中等1158

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。

解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,

所求解集为:

[

[7],

[2,2,3]

]

复制代码

示例 2:

输入:candidates = [2,3,5], target = 8,

所求解集为:

[

[2,2,2,2],

[2,3,3],

[3,5]

]

复制代码

提示:

1 <= candidates.length <= 30

1 <= candidates[i] <= 200

candidate 中的每个元素都是独一无二的。

1 <= target <= 500

解法

class Solution{

public List> combinationSum(int[] candidates, int target) {

List> result=new ArrayList();

backtracking(0,0,candidates,target,new ArrayList(),result);

return result;

}

private void backtracking(int sum,int startIndex,int[] candidates,int target,List temp,List> result){

if(sum>target){

return;

}

if(sum==target){

result.add(new ArrayList(temp));

return;

}

for(int i=startIndex;i

if(sum+candidates[i]>target) continue;

sum=sum+candidates[i];

temp.add(candidates[i]);

backtracking(sum,i,candidates,target,temp,result);

sum=sum-candidates[i];

temp.remove(temp.size()-1);

}

}

}

复制代码

8763df92e251d38f86163684d05b8a23.png

难度中等491

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。

解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,

所求解集为:

[

[1, 7],

[1, 2, 5],

[2, 6],

[1, 1, 6]

]

复制代码

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,

所求解集为:

[

[1,2,2],

[5]

]

复制代码

解法

本题与38题的区别:

- **提供的数组中的元素可能会重复**

- **提供的数组中的元素只能使用一次**

复制代码

所以 startIndex需要+1,其次是 去重

class Solution{

public List> combinationSum2(int[] candidates, int target) {

List> result =new ArrayList();

//排序,方便后面去重

Arrays.sort(candidates);

backtracking(0,0,target,candidates,new ArrayList(),result);

return result;

}

private void backtracking(int sum,int startIndex,int target,int[] candidates,List temp,List> result){

if(sum==target){

result.add(new ArrayList(temp));

return;

}

for(int i =startIndex;i

if(sum+candidates[i]>target) continue;

//元素去重

if(i!=startIndex&&candidates[i]==candidates[i-1]) continue;

temp.add(candidates[i]);

sum=sum+candidates[i];

//startIndex=i+1,不用之前的元素

backtracking(sum,i+1,target,candidates,temp,result);

temp.remove(temp.size()-1);

sum=sum-candidates[i];

}

}

}

复制代码

分割

难度中等485

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"

输出:

[

["aa","b"],

["a","a","b"]

]

复制代码

解法

class Solution{

public List> partition(String s) {

List> result= new ArrayList();

backtracking(s,0,new ArrayList(),result);

return result;

}

private void backtracking(String s,int startIndex,List path,List> result){

int length=s.length();

if(startIndex>=length){

result.add(new ArrayList(path));

return;

}

for(int i=startIndex;i

if(!isHuiwen(startIndex,i,s)) continue;

path.add(s.substring(startIndex,i+1));

backtracking(s,i+1,path,result);

path.remove(path.size()-1);

}

}

private boolean isHuiwen(int startIndex,int endIndex,String s){

for(int i=startIndex, j=endIndex;i

if(s.charAt(i)!=s.charAt(j))return false;

}

return true;

}

}

复制代码

3eece6083b3a91e9d835d65855aaf543.png

132. 分割回文串 II 也可以通过上述类似方法解决,不过会超时。参考动态规划解决。

难度中等501

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.'分隔。

例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效的 IP 地址。

示例 1:

输入:s = "25525511135"

输出:["255.255.11.135","255.255.111.35"]

复制代码

示例 2:

输入:s = "0000"

输出:["0.0.0.0"]

复制代码

示例 3:

输入:s = "1111"

输出:["1.1.1.1"]

复制代码

示例 4:

输入:s = "010010"

输出:["0.10.0.10","0.100.1.0"]

复制代码

示例 5:

输入:s = "101023"

输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]

复制代码

提示:

0 <= s.length <= 3000

s 仅由数字组成

解法

class Solution{

private static final int SEG_COUNT=4;

public List restoreIpAddresses(String s){

List result= new ArrayList();

int length=s.length();

if(length<4||length>12) return result;

backtracking(s,length,0,0,new int[SEG_COUNT],result);

return result;

}

private void backtracking(String s,int length,int segId,int startIndex,int[] temp,List result){

if(segId==SEG_COUNT){

if(startIndex==length){

StringBuffer sb=new StringBuffer();

for(int i=0;i

sb.append(temp[i]);

if(i!=SEG_COUNT-1){

sb.append('.');

}

}

result.add(sb.toString());

}

return;

}

if(startIndex==length){

return;

}

if(s.charAt(startIndex)=='0'){

temp[segId]=0;

backtracking(s,length,segId+1,startIndex+1,temp,result);

}else{

int ip=0;

for(int i=startIndex;i

ip=ip*10+(s.charAt(i)-'0');

if(ip>0xFF||ip<0)break;

temp[segId]=ip;

backtracking(s,length,segId+1,i+1,temp,result);

}

}

}

}

复制代码

8dcbe6163914cedb176a5f776bb2bfd0.png

子集

难度中等995

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]

输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

复制代码

示例 2:

输入:nums = [0]

输出:[[],[0]]

复制代码

提示:

1 <= nums.length <= 10

-10 <= nums[i] <= 10

nums 中的所有元素 互不相同

解法

class Solution{

public List> subsets(int[] nums) {

List> result =new ArrayList();

backtracking(nums,0,new ArrayList(),result);

return result;

}

private void backtracking(int[] nums,int startIndex,List path,List> result){

result.add(new ArrayList(path));

if(startIndex==nums.length)return;

for(int i=startIndex;i

path.add(nums[i]);

//startIndex=i+1; 去重

backtracking(nums,i+1,path,result);

path.remove(path.size()-1);

}

}

}

复制代码

难度中等383

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

**说明:**解集不能包含重复的子集。

示例:

输入: [1,2,2]

输出:

[

[2],

[1],

[1,2,2],

[2,2],

[1,2],

[]

]

复制代码

解法

class Solution{

public List> subsetsWithDup(int[] nums) {

List> result= new ArrayList();

//排序

Arrays.sort(nums);

backtracking(nums,0,new ArrayList(),result);

return result;

}

//竖向不去重,横向去重

private void backtracking(int[] nums,int startIndex,List path,List> result){

result.add(new ArrayList(path));

if(startIndex==nums.length) return;

for(int i =startIndex;i

//nums得是有序的才行

if(i!=startIndex&&nums[i]==nums[i-1])continue;

path.add(nums[i]);

backtracking(nums,i+1,path,result);

path.remove(path.size()-1);

}

}

}

复制代码

057403620e4a468292ae9635e2a291b8.png

难度中等

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

示例:

输入: [4, 6, 7, 7]

输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

复制代码

说明:

给定数组的长度不会超过15。

数组中的整数范围是 [-100,100]。

给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

解法

需要注意的点,是要 递增的,所以不能使用90. 子集 II的排序后去重的方法了。

class Solution{

public List> findSubsequences(int[] nums) {

List> result=new ArrayList();

backtracking(nums,0,new ArrayList(),result);

return result;

}

private void backtracking(int[] nums,int startIndex,List path,List> result){

//递增子序列的长度至少是2

if(path.size()>1){

result.add(new ArrayList(path));

}

//存储当前层已经使用过的数据了,为了去重

List used=new ArrayList();

for(int i =startIndex;i

if(

//保持递增,第一次写成了nums[i]

( !path.isEmpty()

&& nums[i]

||

//当前层去重

used.contains(nums[i])

)continue;

used.add(nums[i]);

path.add(nums[i]);

//startIndex=i+1,竖直方向使用过的元素不再使用

backtracking(nums,i+1,path,result);

//回溯,

path.remove(path.size()-1);

}

}

}

复制代码

dda2a4143a1653d9fce10d71d232565b.png

有个命名相似的题目:300. 最长递增子序列,不过这道题使用回溯法来求肯定跟 132. 分割回文串 II类似,会超时。这类求最长什么的用动态规划更合适。

排列

难度中等1135

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]

输出:

[

[1,2,3],

[1,3,2],

[2,1,3],

[2,3,1],

[3,1,2],

[3,2,1]

]

复制代码

解法

这道题在竖直方向上需要排重

class Solution{

public List> permute(int[] nums) {

List> result =new ArrayList();

backtracking(nums,0,new int[nums.length],result);

return result;

}

private List used =new ArrayList();

private void backtracking(int[] nums,int deep,int[] path,List> result){

if(deep==nums.length){

ArrayList temp=new ArrayList();

for(int item:path){

temp.add(item);

}

result.add(new ArrayList(temp));

return ;

}

for(int i=0;i

if(used.contains(nums[i])) continue;

used.add(nums[i]);

path[deep]=nums[i];

backtracking(nums,deep+1,path,result);

used.remove(used.size()-1);

}

}

}

复制代码

be997316af28f0673b5f8584dfeb0853.png

优化

上述是正常写法,但是效率有点低,下面写法不容易被想到。但是效率高些

class Solution{

private List> result;

private int[] nums;

public List> permute(int[] nums) {

if(nums==null)return null;

result =new ArrayList();

if(nums.length==0)return result;

this.nums=nums;

dfs(0);

return result;

}

private void dfs(int dp){

if(dp==nums.length){

List tempList=new ArrayList();

for(int i:nums){

tempList.add(i);

}

result.add(tempList);

return;

}

for(int i=dp;i

swap(dp,i);

dfs(dp+1);

swap(i,dp);

}

}

private void swap(int i,int j){

int temp =nums[i];

nums[i]=nums[j];

nums[j]=temp;

}

}

复制代码

8d54465e7fda96986590e12c19d2e445.png

难度中等592

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]

输出:

[[1,1,2],

[1,2,1],

[2,1,1]]

复制代码

示例 2:

输入:nums = [1,2,3]

输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

复制代码

提示:

1 <= nums.length <= 8

-10 <= nums[i] <= 10

解法

class Solution{

public List> permuteUnique(int[] nums) {

List> result= new ArrayList();

used=new boolean[nums.length];

Arrays.sort(nums);

backtracing(nums,0,new ArrayList(),result);

return result;

}

private boolean[] used;

private void backtracing(int[] nums,int deep,List path,List> result){

if(deep==nums.length){

result.add(new ArrayList(path));

return;

}

for(int i=0;i

//nums[i]与nums[i-1]比较时,必须确保nums[i-1]已经用过了.

if(i>0&&used[i-1]==false&&nums[i]==nums[i-1])continue;

if(used[i])continue;

used[i]=true;

path.add(nums[i]);

backtracing(nums,deep+1,path,result);

path.remove(path.size()-1);

used[i]=false;

}

}

}

复制代码

eb418a4ce05cc6bfdaf086468a1e7de4.png

棋盘

难度困难749

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

62b58e82bcd738e7a43bbbb554c235c2.png

输入:n = 4

输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]

解释:如上图所示,4 皇后问题存在两个不同的解法。

复制代码

示例 2:

输入:n = 1

输出:[["Q"]]

复制代码

class Solution{

//存储放皇后的位置

int[] queens;

char[] strings;

public List> solveNQueens(int n) {

List> result=new ArrayList();

if(n<1) return result;

queens=new int[n];

strings=new char[n];

StringBuffer sb=new StringBuffer();

for(int i =0;i

strings[i]='.';

}

placeQueens(0,result);

return result;

}

private void placeQueens(int row,List> result){

if(row==queens.length){

List temp=new ArrayList();

//找到成功结果了

for(int item:queens){

strings[item]='Q';

temp.add(new String(strings));

strings[item]='.';

}

result.add(temp);

return;

}

for(int col=0;col

if(isVaild(row,col)){

queens[row]=col;

placeQueens(row+1,result);

queens[row]=0;

}

}

}

private boolean isVaild(int row,int col){

for(int i=0;i

if(queens[i]==col)return false;

if(row-i==Math.abs(queens[i]-col))return false;

}

return true;

}

}

复制代码

难度困难234

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

示例 1:

467d44362977df19f8d369eaf0ad6ea8.png

输入:n = 4

输出:2

解释:如上图所示,4 皇后问题存在两个不同的解法。

复制代码

示例 2:

输入:n = 1

输出:1

复制代码

提示:

1 <= n <= 9

皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

class Solution{

int ways=0;

public int totalNQueens(int n){

if(n<1)return ways;

int[] cols=new int[n];

placeQueens(0,cols);

return ways;

}

private void placeQueens(int row,int[] cols){

if(row==cols.length){

ways++;

return;

}

for(int i=0;i

if(isVaild(row,i,cols)){

cols[row]=i;

placeQueens(row+1,cols);

cols[row]=0;

}

}

}

private boolean isVaild(int row,int col,int[] cols){

for(int i=0;i

if(cols[i]==col)return false;

if(row-i==Math.abs(cols[i]-col)) return false;

}

return true;

}

}

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值