【LeetCode 手撕算法】(回溯)全排列DFS、子集、电话号码字母组合 九键、组合总和、括号生成、单词搜索、分割回文数

回溯模版

java 复制代码
void backtracking(参数) {
    if (终止条件) {
        存放结果; //将path存入res
        return;
    }
    for (选择 : 本层集合中的元素) {
        处理节点;
        backtracking(路径, 选择列表); // 递归
        撤销处理; // 回溯
    }
}
java 复制代码
class Solution {
    List <List<格式>> res=new ArrayList<>();
    public List<List<String>> 名字(String s) {
        List<格式> path=new ArrayList<>();
        dfs(s,path,0);
        return res;
    }
    public void dfs(String s,List<String> path,int start){
        //终止条件
        if(start==s长度){
            res.add(new ArrayList<>(path));
            return;
        }
        //极端边界条件
        if(xxx){xxxx;} 

        //递归遍历
        可能的参数替换、截取、选字母
        for(int i=start;i<s长度;i++){
            path.add(temp);
            dfs(s,path,i+1);
            path.remove(path.size()-1);
        }
    }
  
}

46-全排列

**思路:**全排列回溯 选用DFS递归;结果要全部情况,返回用二维动态列表; 每次存一个结果,并做使用记录,递归条件path满了就return做下一个;path里遍历每一个数字 判断增删 ,递归下一层入口也在这里。

**注意:**存整体结果用二维动态列表,且全局;

使用记录uesd默认false;

path满了,给res复制一份,不能直接传地址,必须new ArrayList复制一份;

java 复制代码
class Solution {
    List <List<Integer>> res =new ArrayList<>();//存返回结果动态二维列表,全局dfs要用
    public List<List<Integer>> permute(int[] nums) {
        List <Integer> path =new ArrayList<>();//存单个的
        boolean[] used =new boolean[nums.length];  //使用记录,默认false
        dfs(nums,path,used);
        return res;
    }
    public void dfs(int[]nums,List<Integer> path,boolean[] used){
        //递归终止条件
        if(path.size()==nums.length){  //当前path满了,存入一种结果
            res.add(new ArrayList<>(path));//将path存入res,path动态改变 直接传的话是地址,需要重新复制一份
            return;
        }
        //遍历每一个数字
        for(int i=0;i<nums.length;i++){
            if(used[i]){continue;}   //用过就跳过,used初始值默认false
            path.add(nums[i]);//未用过就添加
            used[i]=true;  //标记已使用
            dfs(nums,path,used);
            path.remove(path.size()-1);//移除path的最后一个,动态用size.()
            used[i]=false;  
        }

    }
}

78-子集

**思路:**回溯DFS和上一题一样;使用start参数来控制下一层起点;不需要记录used值

注意: 注意传参,i从start开始算,递归时start走下一个

java 复制代码
class Solution {
    List <List<Integer>> res =new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        List<Integer> path=new ArrayList<>();
        dfs(nums,0,path);  //传入参数start为0,来控制下一层起点,避免回头重复
        return res;        
    }
    public void dfs(int[] nums,int start,List<Integer> path){
        res.add(new ArrayList(path)); //无需判断,直接先加上
        for(int i=start;i<nums.length;i++){ //看起点从start开始
            path.add(nums[i]);
            dfs(nums,i+1,path); //注意参数增一,代表下一层
            path.remove(path.size()-1);
        }
    }
}

17-电话号码字母组合

**思路:**回溯DFS和模板一样,传入是字符串的数字"23" ,所以依次取字符 并变成数字,用数字取其对应的按键字符串,开始遍历字符串,选a------选d------删d------选e------删e------选f------删f------删a,两层for循环。

注意:res用一维String列表即可;

判断特殊条件输入为空,即digits长度为0;

path类型为StringBuilder,string为拆开的'a' 'd',而输出为整体字符串'ad',且可变;

数组用 .length 字符串用 .length() 集合用size.() ;

添加: List用add( ) , StringBuilder用append.( )

删除: List用remove( ) , StringBuilder用deleteCharAt.( )

path的格式是StringBuildes,统一转成string用toString( )

charAt.( i ) 取字符串的第i个字母;

取完字母 - '0' 就能变成数字;

java 复制代码
class Solution {
    List <String> res=new ArrayList<>();
    // 数字对应字母
    String[] map = {
            "",     // 0
            "",     // 1
            "abc",  // 2
            "def",  // 3
            "ghi",  // 4
            "jkl",  // 5
            "mno",  // 6
            "pqrs", // 7
            "tuv",  // 8
            "wxyz"  // 9
    };
    public List<String> letterCombinations(String digits) {
        if(digits.length()==0){return res;} //特殊条件为空时
        StringBuilder path = new StringBuilder();//为完整字符,不能拆开,string是拆开单独的
        dfs(digits,0,path);
        return res;
    }
    public void dfs(String digits,int index,StringBuilder path){
        if(index==digits.length()){//终止条件,数组.length ,字符串.length()
            res.add(path.toString());//将path存入res,注意两者格式不一样需统一
            return;
        }
        //传入的是字符串,需要变成数字取出来
        char digit=digits.charAt(index); //charAt()取第i个字符为digit
        String letters=map[digit-'0']; // 取该位置的map的字母内容,存为letters
        for(int i=0;i<letters.length();i++){ //开始遍历letters
            path.append(letters.charAt(i)); //添加第i个字母
            dfs(digits,index+1,path);//走下一按钮字母串,再执行for循环
            path.deleteCharAt(path.length()-1);//撤回
        }
    }
}

39-组合总和

**思路:**和以前一样,只是递归传参包含了i本身 不需要+1 , 使用start做起始值 保证不能回头找 只能向下一步走(包括本身)

**注意:**和78-子集一样

java 复制代码
class Solution {
    List <List<Integer>> res=new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List <Integer> path=new ArrayList<>();
        dfs(candidates,target,0,path);
        return res; 
    }
    public void dfs(int[] candidates, int target,int start, List <Integer> path){
        if(target==0){    //target减为0时
            res.add(new ArrayList(path));
            return ;
        }
        if(target<0){return;}    //target不匹配时 即减到底,无反应
        //i从start开始,保证不重复223,322,232
        for(int i=start;i<candidates.length;i++){
            path.add(candidates[i]);
            dfs(candidates,target-candidates[i],i,path); //传参target要减,起始位置变成i 代表本身可重复用
            path.remove(path.size()-1);
        }
    }
}

22-括号生成

**思路:**只有左右括号,终止条件是左右之和为2n, 左的执行条件是 <n , 右的执行条件是<左 ,左右单独加1

注意:写法StringBuilder path=new StringBuilder();

字符串用**" "** ,字符用**' '** ,**(**是字符串;

path类型为StringBuilder,string为拆开的'a' 'd',而输出为整体字符串'ad',且可变;

数组用 .length 字符串用 .length() 集合用size.() ;

添加: List用add( ) , StringBuilder用append.( )

删除: List用remove( ) , StringBuilder用deleteCharAt.( )

path的格式是StringBuildes,统一转成string用toString( )

java 复制代码
class Solution {
    List <String> res=new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        StringBuilder path=new StringBuilder();//由于是字符 则需要ListBilder
        dfs(n,path,0,0);
        return res;
    }
    //需要左右分开操作
    public void dfs(int n,StringBuilder path,int left,int right){
        //终止条件 当左右的和为2n时,代表可结束
        if((left+right)==2*n){
            res.add(path.toString());
            return;
        }
        //若左<n,代表可继续
        if(left<n){
            path.append("(");//字符串用"" ,字符用'',(是字符串
            dfs(n,path,left+1,right);
            path.deleteCharAt(path.length()-1); //
        }
        //若右<左,代表可继续
        if(right<left){
            path.append(")");
            dfs(n,path,left,right+1);
            path.deleteCharAt(path.length()-1);        }
    }
}

79-单词搜索

**思路:**要找单词 所以要遍历所有点,在每个点进行DFS, 判断当前字符是否=word [index] ,用完标记,新建found遍历 下一层,然后回溯

注意: 字符串用length.() , 字符用 length;

边界写>= ,必须有=;

字符串里选字母 用charAt ;

用完必须标记

java 复制代码
class Solution {

    public boolean exist(char[][] board, String word) {
        for(int i=0;i<board.length;i++){//遍历所有的点,都进行遍历
            for(int j=0;j<board[0].length;j++){
                if(dfs(board,i,j,word,0)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean dfs(char[][]board,int i,int j,String word,int index){
        if(index==word.length()){return true;}//递归终止条件
        if(i<0||i>=board.length||j<0||j>=board[0].length){return false;}//超出边界条件
        if(board[i][j]!=word.charAt(index)){return false;}//不匹配条件,取字符串某字母用charAt
        char temp=board[i][j];//暂存字符 方便回溯
        board[i][j]='#';//用完标记
        boolean found=  dfs(board,i-1,j,word,index+1)//迭代
                      ||dfs(board,i+1,j,word,index+1)
                      ||dfs(board,i,j-1,word,index+1)
                      ||dfs(board,i,j+1,word,index+1);
        board[i][j]=temp;//回溯
        return found;//返回
    }
   
}

131-分割回文数

**思路:**这题不需要理解,死记硬背就行,模版和其他的一样,内部裁剪切割有点复杂

**注意:**传的参数

java 复制代码
class Solution {
    List <List<String>> res=new ArrayList<>();
    public List<List<String>> partition(String s) {
        List<String> path=new ArrayList<>();
        dfs(s,path,0);
        return res;
    }
    public void dfs(String s,List<String> path,int start){
        //终止条件
        if(start==s.length()){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i=start;i<s.length();i++){
            String temp=s.substring(start,i+1);//截取子串判断
            if(isHui(temp)){
                path.add(temp);
                dfs(s,path,i+1);
                path.remove(path.size()-1);
            }

        }
    }
    //判断是否是回文数
    public boolean isHui(String temp){
        int left=0;
        int right=temp.length()-1;
        while(left<right){
            if(temp.charAt(left)!=temp.charAt(right)){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}
相关推荐
极客先躯1 小时前
高级java每日一道面试题-2025年12月06日-实战篇[Dockerj]-如何配置 Docker 的镜像加速器?国内有哪些常用加速源?
java·docker·配置docker的镜像加速器·国内有哪些常用加速源·镜像加速器的本质与配置原理·镜像拉取流程对比·加速前后架构差异
隐退山林1 小时前
JavaEE进阶:SpringIoC&DI
java·开发语言·java-ee
水煮白菜王1 小时前
Claude Code 全方位使用手册
java·开发语言·网络
kiku18181 小时前
Docker高级管理--Dockerfile镜像制作
java·docker·eureka
风筝在晴天搁浅1 小时前
快手/腾讯 CodeTop LeetCode 43.字符串相乘
算法·leetcode
ooseabiscuit1 小时前
Laravel10.x重磅发布:新特性全解析
android·java·开发语言·mysql
0xDevNull1 小时前
Tomcat 运行原理与架构深度解析
java·架构·tomcat
ch.ju1 小时前
Java程序设计(第3版)第三章——数组
java·开发语言
_waylau1 小时前
“Java+AI全栈工程师”问答01:Spring MVC登录页面错误提示
java·开发语言·vue.js·后端·spring·mvc·springcloud