LeetCode:回溯总结

一、组合:同一个元素不能重复使用

1.1 组合

77:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

class Solution {
    LinkedList<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n,k,1);
        return result;
    }
    public void backtracking(int n,int k,int startindex){
        if(path.size()==k){
            //不能直接放path,因为传进去是引用,后面path继续修改会影响之前的path结果
            result.add(new LinkedList<>(path));
            return ;
        }
        //减枝优化:还需要在n个元素中再找k-path.size个元素,所以i的位置最多是n-(k-path.size())+1
        for(int i =startindex;i<=n-(k-path.size())+1;i++){
            path.offer(i);
            backtracking(n,k,i+1);
            path.pollLast();
        }
    }
}

1.2 组合之和 

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

与上题相比,多了个sum的限制

class Solution {
    LinkedList<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(k,n,1);
        return result;
    }

    public void backtracking(int k,int sum,int startindex){
        if(sum<0||path.size()>k)
            return ;
        if(sum==0&&path.size()==k){
            result.add(new LinkedList(path));
            return ;
        }

        for(int i=startindex;i<10;i++){
            path.add(i);
            sum -= i;
            backtracking(k,sum,i+1);
            sum += i;
            path.pollLast();
        }
    }
}

二、组合进阶

2.1 组合总和:candidates无重复元素,可以重复拿取同一个元素

39:给你一个无重复元素的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有不同组合 ,并以列表形式返回。你可以按任意顺序返回这些组合。candidates 中的同一个数字可以无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

class Solution {
    LinkedList<Integer> path = new LinkedList<>();
    List<List<Integer>> result = new LinkedList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates,target,0);
        return result;
    }

    public void backtracking(int[] candidates,int target,int startindex){
        if(target<0)
            return;
        if(target==0){
            result.add(new LinkedList(path));
            return ;
        }
        for(int i=startindex;i<candidates.length;i++){
            path.offer(candidates[i]);
            //下一层 下标从i开始,代表可以重复选取相同元素
            backtracking(candidates,target-candidates[i],i);
            path.pollLast();
        }
    }
}

2.2 组合总和:candidates有重复元素,不能重复拿取同一个元素

40:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。

class Solution {

    List<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        boolean[] used = new boolean[candidates.length];
        Arrays.sort(candidates);
        backtracking(candidates,target,0,used);
        return result;
    }

    public void backtracking(int[] candidates,int target,int startindex,boolean[] used){
        if(target<0)
            return ;
        if(target==0){
            result.add(new LinkedList(path));
            return ;
        }
        for(int i=startindex;i<candidates.length;i++){
            //说明是同一层之前的元素
            if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)
                continue;
            //used[i]!=false,说明是上下层关系,
            used[i] = true;
            path.offer(candidates[i]);
            backtracking(candidates,target-candidates[i],i+1,used);
            path.pollLast();
            used[i] = false;
        }
    }
}

三、排列

3.1 全排列

给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。你可以按任意顺序返回答案。

class Solution {

    List<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> permute(int[] nums) {
        boolean[] used = new boolean[nums.length];
        backtracking(nums,used);
        return result;
    }

    public void backtracking(int[] nums,boolean[] used){
        if(path.size()==nums.length){
            result.add(new LinkedList(path));
            return;
        }
        //下标从0-nums.length-1
        for(int i=0;i<nums.length;i++){
            //上一层用过了
            if(used[i]==true)
                continue;
            //这一层要用,让下一层不要用
            used[i]=true;
            path.offer(nums[i]);
            backtracking(nums,used);
            path.pollLast();
            //清除标记,让同一层的兄弟儿子可以用  
            used[i]=false; 
                   
        }
        return ;
    }
}

3.2 全排列II

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

class Solution {

    List<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        backtracking(nums,used);
        return result;
    }

    public void backtracking(int[] nums,boolean[] used){
        if(path.size()==nums.length){
            result.add(new LinkedList(path));
            return ;
        }
        
        //下标从0-nums.length-1
        for(int i=0;i<nums.length;i++){
            //左边兄弟和自己元素相等
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)
                continue;
            //上一层用过这个元素
            if(used[i]==true)
                continue;
            used[i]=true;
            path.offer(nums[i]);
            backtracking(nums,used);
            path.pollLast();
            used[i]=false;

        }
        return ;
    }
}

四、分割字符串

4.1 分割字符串

131:给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文串 。返回 s 所有可能的分割方案。 回文串是正着读和反着读都一样的字符串。

class Solution {
    List<List<String>> result = new LinkedList<>();
    LinkedList<String> path = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backtracking(s,0);
        return result;
    }

    public void backtracking(String s,int startindex){
        //如果已经截取完,加入到结果
        if(startindex>=s.length()){
            result.add(new LinkedList(path));
            return ;
        }

        //从startindex开始截取,找到[startindex,i]是回文,递归往下一层
        for(int i=startindex;i<s.length();i++){
            if(isPalindrome(s,startindex,i)){
                path.offer(s.substring(startindex,i+1));
                backtracking(s,i+1);
                path.pollLast();
            }
        }
    }

    boolean isPalindrome(String s,int start,int end){
        while(start<end){
            if(s.charAt(start)!=s.charAt(end))
                return false;
            start++;
            end--;
        }
        return true;
    }
}

4.2 复原IP地址

93:给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你不能重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

class Solution {

    List<String> result = new LinkedList<>();
    public List<String> restoreIpAddresses(String s) {
        backtracking(s,0,"",0);
        return result;
    }
    public void backtracking(String s,int startindex,String path,int count){
        if(count>4)
            return ;

        if(startindex>=s.length()&&count==4){
            result.add(path.substring(0,path.length()-1));
            return ;
        }

        for(int i=startindex;i<s.length();i++){
            if(isValid(s,startindex,i)){
                backtracking(s,i+1,path + s.substring(startindex,i+1)+".",count+1);
            }
        }
    }
    public boolean isValid(String s,int start,int end){
        if(end-start>2)
            return false;
        if(end-start>0&&s.charAt(start)=='0')
            return false;
        if(Integer.parseInt(s.substring(start,end+1))>255)
            return false;
        return true;
    }
}

4.3 单词拆分II

140:给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

class Solution {
    List<String> result = new LinkedList<>();
    List<String> wordDict = new LinkedList();
    public List<String> wordBreak(String s, List<String> wordDict) {
        this.wordDict = wordDict;
        backtracking(s,0,"");
        return result;
    }

    public boolean isValid(String s,int start,int end){
        String tmp = s.substring(start,end+1);
        return wordDict.contains(tmp);
    }

    public void backtracking(String s,int startindex,String path){
        if(startindex==s.length()){
            path = path.substring(0,path.length()-1);
            result.add(path);
        }

        for(int i=startindex;i<s.length();i++){
            if(isValid(s,startindex,i)){
                backtracking(s,i+1,path+s.substring(startindex,i+1)+" ");
            }
        }
    }

}

五、子集

78:给你一个整数数组 nums ,数组中的元素互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

与组合类似,不同的是没有终止条件,沿途的记录都放入结果,for循环结束就终止

class Solution {
    List<List<Integer>> result = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> subsets(int[] nums) {
        backtracking(nums,0);
        return result;
    }
    public void backtracking(int[] nums,int startindex){
        //没有写终止,沿途的记录都放入结果,for循环结束就终止
        result.add(new LinkedList(path));
        for(int i=startindex;i<nums.length;i++){
            path.offer(nums[i]);
            backtracking(nums,i+1);
            path.pollLast();
        }
        return ;
    }
}

六、深度遍历

6.1 岛屿数量

200:给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的四条边均被水包围。

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        int m = grid.length;
        int n = grid[0].length;
        for(int i =0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]=='1'){//遇到陆地开始深度遍历
                    dfs(grid,i,j);
                    count++;
                }                    
            }
        }
        return count;
    }

    public void dfs(char[][] grid,int row,int col){
        if(!inArea(grid,row,col))
            return ;
        //以及遍历过的地方,或者是水域直接返回
        if(grid[row][col]=='0'||grid[row][col]=='2')
            return ;
        //深度遍历到的地方标记为2
        grid[row][col] = '2';
        dfs(grid,row+1,col);
        dfs(grid,row-1,col);
        dfs(grid,row,col-1);
        dfs(grid,row,col+1);
    }

    public boolean inArea(char[][] grid,int row,int col){
        return row>=0&&row<grid.length&&col>=0&&col<grid[0].length;
    }
}

6.2 岛屿的最大面积

695:根据上一题求,岛屿的最大面积

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int max = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]==1){
                    max =Math.max(max,dfs(grid,i,j));
                }
            }
        }
        return max;
    }


    public int dfs(int[][]grid,int row,int col){
        if(row<0||col<0||row>grid.length-1||col>grid[0].length-1)
            return 0;
        if(grid[row][col]==0||grid[row][col]==2)
            return 0;
        //染色,标记为岛屿
        grid[row][col]=2;
        return 1+dfs(grid,row+1,col)+dfs(grid,row-1,col)+dfs(grid,row,col+1)+dfs(grid,row,col-1);
    }
}

6.3 单词搜索

79:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

class Solution {
    public boolean exist(char[][] board, String word) {
        int[][] visited = new int[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(dfs(board,i,j,word,0,visited))
                    return true;
            }
        }
        return false;
    }

    public boolean dfs(char[][] board,int row,int col,String word,int k,int[][] visited){
        if(row<0||row>board.length-1||col<0||col>board[0].length-1||visited[row][col]==1){
            return false;
        }
        if(board[row][col]!=word.charAt(k))
            return false;
        if(board[row][col]==word.charAt(k)&&k==word.length()-1){
            return true;
        }
        //标记这个点已经访问过,下一层不能访问
        visited[row][col] = 1;
        boolean up = dfs(board,row-1,col,word,k+1,visited);
        boolean down = dfs(board,row+1,col,word,k+1,visited);
        boolean left = dfs(board,row,col-1,word,k+1,visited);
        boolean right = dfs(board,row,col+1,word,k+1,visited);
        //擦除标记,自己走完让别的路径方法可以走
        visited[row][col] = 0;
        return up||down||left||right;
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值