• 递归与回溯-排立组合


    一、递归与回溯

       回溯是暴力求解的一个主要手段,通常是(回溯 + 剪枝)

    17. Letter Combinations of a Phone Number

    Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.

    A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

    Input: "23"
    Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

    代码如下:
    public class Solution {
            private static final String[] KEYS = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
        
            public List<String> letterCombinations(String digits) {
                List<String> ret = new LinkedList<String>();
                combination("", digits, 0, ret);
                return ret;
            }
        
             //prefix代表digits中offset之前数字所映射的字符串
            private void combination(String prefix, String digits, int offset, List<String> ret) {
                if (offset >= digits.length()) {   //offset其实就充当了一个for循序
                    ret.add(prefix);
                    return;
                }
                String letters = KEYS[(digits.charAt(offset) - '0')];
                for (int i = 0; i < letters.length(); i++) {
                    combination(prefix + letters.charAt(i), digits, offset + 1, ret);
                }
            }
        }
    public List<String> letterCombinations(String digits) {
            LinkedList<String> ans = new LinkedList<String>();
            if(digits.isEmpty()) return ans;
            String[] mapping = new String[] {"0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
            ans.add("");
            for(int i =0; i<digits.length();i++){
                int x = Character.getNumericValue(digits.charAt(i));
                while(ans.peek().length()==i){  //用得非常巧妙 如digits = “23”  ans会先加入abc在移除
                    String t = ans.remove();
                    for(char s : mapping[x].toCharArray())
                        ans.add(t+s);
                }
            }
            return ans;
        }
    93. Restore IP Addresses

    Given a string containing only digits, restore it by returning all possible valid IP address combinations.

    Example:

    Input: "25525511135"
    Output: ["255.255.11.135", "255.255.111.35"]
    代码如下:




    131. Palindrome Partitioning

    iven a string s, partition s such that every substring of the partition is a palindrome.

    Return all possible palindrome partitioning of s.

    Example:

    Input: "aab"
    Output:
    [
      ["aa","b"],
      ["a","a","b"]
    ]

    代码如下:







    使用回溯法解决排列问题(拍立组合问题的思想可以用来寻找数组中选数组之和等于某个数组(如在 1 2 3 4中寻找元素之后为6的组合 允许重复为排立问题的回溯代码,不循序元素重复为组合问题代码写法)
    也可以用动态规划背包问题来解决 某几个元素之和为某一个数


    46. Permutations

    Given a collection of distinct integers, return all possible permutations.

    Example:

    Input: [1,2,3]
    Output:
    [ 
      [1,2,3],
      [1,3,2],
      [2,1,3],
      [2,3,1],
      [3,1,2],
      [3,2,1]
    ]
    代码如下:

    class Solution {
    public List<List<Integer>> permute(int[] nums) {
       List<List<Integer>> list = new ArrayList<>();
       backtrack(list, new ArrayList<>(), nums);
       return list;
    }
    

    //含义:代表以nums[i]为排列开头,再去递归寻找n-1个元素组成全排列
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums){ if(tempList.size() == nums.length){ list.add(new ArrayList<>(tempList)); } else{ for(int i = 0; i < nums.length; i++){ if(tempList.contains(nums[i])) continue; // element already exists, skip tempList.add(nums[i]); backtrack(list, tempList, nums); tempList.remove(tempList.size() - 1); } } } }



    //
     
    class Solution {
        public List<List<Integer>> permute(int[] nums) {
            List<List<Integer>> result = new ArrayList<>();
            List<Integer> currentResult = new ArrayList<>();
            boolean[] used = new boolean[nums.length];
            recurse(currentResult,result,nums,used);
            return result;
        }
        
        private void recurse(List<Integer> current,List<List<Integer>> result,int[] nums,boolean[] used){
            if(current.size()==nums.length){
                result.add(new ArrayList<Integer>(current));
                return;
            }
            
            for(int i=0;i<nums.length;i++){
                if(used[i]) continue;
                current.add(nums[i]);
                used[i] = true;
                recurse(current,result,nums,used);
                used[i] = false;
                current.remove(current.size()-1);
            }
        }
    }
    使用回溯法解决组合问题
    77. Combinations

    Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

    Example:

    Input: n = 4, k = 2
    Output:
    [
      [2,4],
      [3,4],
      [2,3],
      [1,2],
      [1,3],
      [1,4],
    ]

    代码如下:
    class Solution {
        public  List<List<Integer>> combine(int n, int k) {
            List<List<Integer>> combs = new ArrayList<List<Integer>>();
            combine(combs, new ArrayList<Integer>(), 1, n, k);
            return combs;
        }
    //start之前不能取,只能从start之后开始取
    public void combine(List<List<Integer>> combs, List<Integer> comb, int start, int n, int k){ if(k==0) { combs.add(new ArrayList<Integer>(comb)); return; } for(int i=start;i<=n;i++) { comb.add(i); combine(combs, comb, i+1, n, k-1); comb.remove(comb.size()-1); } } }
    39. Combination Sum

    Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

    The same repeated number may be chosen from candidates unlimited number of times.

    Note:

    • All numbers (including target) will be positive integers.
    • The solution set must not contain duplicate combinations
    
    
    Input: candidates = [2,3,5], target = 8,
    A solution set is:
    [
      [2,2,2,2],
      [2,3,3],
      [3,5]
    ]


    代码如下:
    class Solution {
        public List<List<Integer>> combinationSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        backtrack(list, new ArrayList<>(), nums, target, 0);
        return list;
    }
    
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
        if(remain < 0) return;
        else if(remain == 0) list.add(new ArrayList<>(tempList));
        else{ 
            for(int i = start; i < nums.length; i++){
                tempList.add(nums[i]);
                backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
                tempList.remove(tempList.size() - 1);
            }
        }
    }
    }

    以下leetcode链接里面几乎所有在一维平面运用回溯+剪枝来解答的题都有代码:
    https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-(Subsets-Permutations-Combination-Sum-Palindrome-Partitioni





    在二维平面使用回溯法解决问题(也非常重要)
    79. Word Search

    Given a 2D board and a word, find if the word exists in the grid.

    The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

    Example:

    board =
    [
      ['A','B','C','E'],
      ['S','F','C','S'],
      ['A','D','E','E']
    ]
    
    Given word = "ABCCED", return true.
    Given word = "SEE", return true.
    Given word = "ABCB", return false.
    代码如下:

    public class Solution {
        static boolean[][] visited;
        static int[][] d = {{-1,0}, {0,1}, {1,0}, {0,-1}};//从上下左右四个方向寻找
        static int m, n;
        public boolean exist(char[][] board, String word) {     
            m = board.length;
            n = board[0].length;
            visited = new boolean[m][n];
            for(int i = 0; i < board.length; i++){
                for(int j = 0; j < board[i].length; j++){
                    if(search(board, word, i, j, 0)){
                        return true;
                    }
                }
            }
            
            return false;
        }
        
        
        
        //从board[i][j]开始寻找word[index 到 word.length -1]元素
        private boolean search(char[][]board, String word, int i, int j, int index){
            if(index == word.length()-1){
                return board[i][j] == word.charAt(index);
            }//递归出口
            
            if(board[i][j] == word.charAt(index)){
                visited[i][j] = true;
                for(int[] num: d){
                   int newX = i+num[0];
                   int newY = j+num[1];
                   if(isArea(newX, newY) && !visited[newX][newY] &&
                      search(board, word, newX, newY, index+1)){
                       return true;
                }
              } 
               visited[i][j] = false;//想四个方向寻找都没有结果就放弃该位置  
            }
      
           
            return false;
        }
        //检查边界
        private boolean isArea(int x, int y){
            return x>=0 && x<m && y>=0 && y<n;
        }
    }

    200. Number of Islands

    Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

    Example 1:

    Input:
    11110
    11010
    11000
    00000
    
    Output: 1
    

    Example 2:

    Input:
    11000
    11000
    00100
    00011
    
    Output: 3

    代码如下:

    public class Solution {
        private int n;
        private int m;
    
        public int numIslands(char[][] grid) {
            int count = 0;
            n = grid.length;
            if (n == 0) return 0;
            
            m = grid[0].length;
            for (int i = 0; i < n; i++){
                for (int j = 0; j < m; j++)
                    if (grid[i][j] == '1') {
                        DFSMarking(grid, i, j);
                        ++count;
                    }
            }    
            return count;
        }
    
        private void DFSMarking(char[][] grid, int i, int j) {
            if (i < 0 || j < 0 || i >= n || j >= m || grid[i][j] != '1') return;
            grid[i][j] = '0'; //或者不改变原来数组 用一个visited二维数组来进行标记
            DFSMarking(grid, i + 1, j);
            DFSMarking(grid, i - 1, j);
            DFSMarking(grid, i, j + 1);
            DFSMarking(grid, i, j - 1);
        }
    }

    130. Surrounded Regions

    Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'.

    A region is captured by flipping all 'O's into 'X's in that surrounded region.

    Example:

    X X X X
    X O O X
    X X O X
    X O X X
    

    After running your function, the board should be:

    X X X X
    X X X X
    X X X X
    X O X X

    代码如下:







    51. N-Queens

    The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

    Given an integer n, return all distinct solutions to the n-queens puzzle.

    Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

    Example:

    Input: 4
    Output: [
     [".Q..",  // Solution 1
      "...Q",
      "Q...",
      "..Q."],
    
     ["..Q.",  // Solution 2
      "Q...",
      "...Q",
      ".Q.."]
    ]


    代码如下:
    //以行为层数进行dfs
    //https://www.bilibili.com/video/av48349868/?p=32
    
    public class Solution {
        private Set<Integer> col = new HashSet<Integer>();
        private Set<Integer> diag1 = new HashSet<Integer>();
        private Set<Integer> diag2 = new HashSet<Integer>();
        
        public List<List<String>> solveNQueens(int n) {
            List<List<String>> res = new ArrayList<List<String>>();
            dfs(res,new ArrayList<String>(), 0, n);
            return res;
        }
        private void dfs(List<List<String>> res, List<String> list, int row, int n){
            if (row == n){
                res.add(new ArrayList<String>(list));//到达最后一层,dfs终止条件
                return;
            }
            for (int i = 0; i < n; i++){
                if (col.contains(i) || diag1.contains(row + i) || diag2.contains(row - i)) continue;
                
                char[] charArray = new char[n];
                Arrays.fill(charArray, '.');
                charArray[i] = 'Q';
                String rowString = new String(charArray);
                
                list.add(rowString);
                col.add(i);
                diag1.add(row + i);
                diag2.add(row - i);
                
                dfs(res, list, row + 1, n);
                
                list.remove(list.size() - 1);
                col.remove(i);
                diag1.remove(row + i);
                diag2.remove(row - i);
            }
        }
    }
    37. Sudoku Solver
    数独问题

    Write a program to solve a Sudoku puzzle by filling the empty cells.

    A sudoku solution must satisfy all of the following rules:

    1. Each of the digits 1-9 must occur exactly once in each row.
    2. Each of the digits 1-9 must occur exactly once in each column.
    3. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

    Empty cells are indicated by the character '.'.


    A sudoku puzzle...


    代码如下:














  • 相关阅读:
    泛型约束new()的使用
    控制反转-依赖注入
    微服务的六个基本点
    java反编译工具
    Idea中一些常用设置
    JSP内置对象(9个常用的内置对象)
    输出输入流,的应用

    容器集合类
    容器与集合
  • 原文地址:https://www.cnblogs.com/yangcao/p/11655268.html
Copyright © 2020-2023  润新知