leetcode on algorithm interview in Dachang 11 Pruning & backtracking

leetcode on algorithm interview in Dachang 11 Pruning & backtracking

Video Explanation (efficient learning): Click to learn

catalog:

1. Introduction

2. Time and space complexity

3. Dynamic planning

4. Greed

5. Binary search

6. Depth first & breadth first

7. Double pointer

8. Sliding window

9. Bit operation

10. Recursion & divide and conquer

11 Pruning & backtracking

12. Reactor

13. Monotone stack

14. Sorting algorithm

15. Linked list

16.set&map

17. Stack

18. Queue

19. Array

20. String

21. Trees

22. Dictionary tree

23. Consolidation

24. Other types of questions

prune

Exclude branches that do not meet the criteria. Improve the running efficiency of the program.

to flash back:

Layer by layer recursion, trying to find prime answers,

  • Find the answer: return the result and try another branch
  • No answer found: go back to the previous level and try another branch

Backtracking template:

result = [];
function backtrack (path, list) {
    if (Meet the conditions) {
        result.push(path);
        return
    }
    
    for () {
        // Single layer logic
        backtrack (path, list)
        // Deselect reset status
    }
}

Retrospective Trilogy:

  • Backtracking parameters
  • Termination conditions
  • Single layer recursive logic
  • Select another branch (deselect reset status)

22. Bracket generation (medium)

Method 1: Violence

Complexity analysis: the time complexity is O(2^2n*n). The length of the string is 2n. There are two options for each position. Select the left or right parenthesis to verify whether the string is valid. The complexity is O(n). It will be optimized after pruning. The worst case is O(2^2n*n). Space complexity O(n), maximum number of recursion 2n

Method 2. Recursive dfs
  • Idea: recursion is adopted. The termination condition is that the length of the string is equal to 2n. The recursive function passes in the constructed string. There are two options for how many left and right parentheses are left. Select the left or right parentheses. Pruning optimization can be carried out here. The right parentheses can be selected only if the holding number of the right parentheses is greater than the holding number of the left parentheses. Otherwise, it must not constitute valid parentheses

Js:

const generateParenthesis = (n) => {
    const res = []; // Output result array

    const generate = (str, left, right) => {
        if (str.length == 2 * n) { // String build complete
            res.push(str);           // Add string to res
            return;                  // End current recursion (end current search branch)
        }
        if (left > 0) {            // As long as the left parenthesis is left, you can choose it and continue to choose recursively
            generate(str + '(', left - 1, right);
        }
        if (right > left) {        // The right bracket can only be selected if the holding quantity of the right bracket is greater than the holding quantity of the left bracket
            generate(str + ')', left, right - 1);
        }
    };

    generate('', n, n); // The entry of recursion. The initial string is an empty string, and the number of initial parentheses is n
    return res;
};

Java:

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        generate(n, n, "");
        return res;
    }

    private void generate(int left, int right, String curStr) {
        if (left == 0 && right == 0) {
            res.add(curStr);
            return;
        }

        if (left > 0) {
            generate(left - 1, right, curStr + "(");
        }
        if (right > left) {
            generate(left, right - 1, curStr + ")");
        }
    }
}
Method 3. Backtracking
  • Idea: when there are too many left parentheses, it means that the number of left parentheses in the string is less than that of right parentheses, which is illegal. Try to add left parentheses to the string, and then backtrack, try to add right parentheses, and then try backtracking

Js:

var generateParenthesis = function(n) {
    if (n == 0) return []
    const res = []
    let track = []
    backtrack(n, n, track, res)
    return res
    function backtrack(left, right, track, res) {
        // Quantity less than 0, illegal
        if (left < 0 || right < 0) return
        // If there are too many left parentheses, it indicates that it is illegal
        if (right < left) return
        // All parentheses are used up to get a legal combination
        if (left == 0 && right == 0) {
            res.push(track.join(''))
            return
        }

        // Try adding an open parenthesis 
        track.push('(')
          //In this place, you must pay attention to copying a track, that is, [... Track], otherwise it will affect other branches
        backtrack(left - 1, right, [...track], res)
        track.pop()

        // Try adding a closing bracket
        track.push(')')
        backtrack(left, right - 1, [...track], res)
        track.pop()
    }
};

Java:

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<String>();
        backtrack(res, new StringBuilder(), 0, 0, n);
        return res;
    }

    public void backtrack(List<String> res, StringBuilder cur, int left, int right, int max) {
        if (cur.length() == max * 2) {
            res.add(cur.toString());
            return;
        }
        if (left < max) {
            cur.append('(');
            backtrack(res, cur, left + 1, right, max);
            cur.deleteCharAt(cur.length() - 1);
        }
        if (right < left) {
            cur.append(')');
            backtrack(res, cur, left, right + 1, max);
            cur.deleteCharAt(cur.length() - 1);
        }
    }
}

51. Queen n (hard)

Method 1. Backtracking

The animation is too large. Click to view it

  • Idea: traverse the chessboard from top to bottom and from left to right, prepare three sets to record the coordinates that can be attacked by the column and two diagonals respectively, try to place the queen in each empty space, update the coordinates of the three sets that can be attacked after placement, and then continue to traverse the next layer. After completing the next layer, try to trace back to the current layer, that is, cancel the queen placed in the current layer, At the same time, undo the three set coordinates that can be attacked, and keep backtracking until the traversal is completed to find all possible solutions.
  • Complexity analysis: time complexity: O(N!), where N is the number of queens. Since each queen must be in a different column, the column of placed queens cannot place other queens. The first queen has N columns to choose from, and the second queen has at most N-1 columns to choose from. Spatial complexity: O(N), where N is the number of queens. The spatial complexity mainly depends on the number of recursive call layers, the array recording the subscripts of Queen columns placed in each row and three sets. The number of recursive call layers will not exceed N, the length of the array is N, and the number of elements in each set will not exceed N.

js:

const solveNQueens = (n) => {
    const board = new Array(n);
    for (let i = 0; i < n; i++) {
        board[i] = new Array(n).fill('.');//Generate board
    }

    const cols = new Set();  // A column set that records the columns where queens have occurred
    const diag1 = new Set(); // Diagonal set
    const diag2 = new Set(); // Inverse diagonal set
    const res = [];//Result array

    const backtrack = (row) => {
        if (row == n) {//Termination conditions
            const stringsBoard = board.slice();
            for (let i = 0; i < n; i++) {//Generate string
                stringsBoard[i] = stringsBoard[i].join('');
            }
            res.push(stringsBoard);
            return;
        }
        for (let col = 0; col < n; col++) {
            // If there is no queen in the column and diagonal of the current point, you can select it. Otherwise, skip
            if (!cols.has(col) && !diag1.has(row + col) && !diag2.has(row - col)) {
                board[row][col] = 'Q';  // Place queen
                cols.add(col);          // The Queen's column is recorded
                diag2.add(row - col);   // The record shows the Queen's diagonal
                diag1.add(row + col);   // The record puts the Queen's negative diagonal
                backtrack(row + 1);
                board[row][col] = '.';  // Undo the queen of the point
                cols.delete(col);       // Delete the corresponding records
                diag2.delete(row - col);
                diag1.delete(row + col);
            }
        }
    };
    backtrack(0);
    return res;
};

java:

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<List<String>>();
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        Set<Integer> cols = new HashSet<Integer>();
        Set<Integer> diag1 = new HashSet<Integer>();
        Set<Integer> diag2 = new HashSet<Integer>();
        backtrack(res, queens, n, 0, cols, diag1, diag2);
        return res;
    }

    public void backtrack(List<List<String>> res, int[] queens, int n, int row, Set<Integer> cols, Set<Integer> diag1, Set<Integer> diag2) {
        if (row == n) {
            List<String> board = generateBoard(queens, n);
            res.add(board);
        } else {
            for (int i = 0; i < n; i++) {
                if (cols.contains(i)) {
                    continue;
                }
                int diagonal1 = row - i;
                if (diag1.contains(diagonal1)) {
                    continue;
                }
                int diagonal2 = row + i;
                if (diag2.contains(diagonal2)) {
                    continue;
                }
                queens[row] = i;
                cols.add(i);
                diag1.add(diagonal1);
                diag2.add(diagonal2);
                backtrack(res, queens, n, row + 1, cols, diag1, diag2);
                queens[row] = -1;
                cols.remove(i);
                diag1.remove(diagonal1);
                diag2.remove(diagonal2);
            }
        }
    }

    public List<String> generateBoard(int[] queens, int n) {
        List<String> board = new ArrayList<String>();
        for (int i = 0; i < n; i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }
}

52. N Queen II(hard)

Method 1. Bit operation

js:

var totalNQueens = function (n) {
    if (n < 1) return
    let count = 0;
    dfs(n, 0, 0, 0, 0)
    return count

      //n: Number of queens
      //Row: current row
      //cols: where to place the queen
      //diag1: left diagonal that can be attacked
      //diag2: right diagonal that can be attacked
    function dfs(n, row, cols, diag1, diag2) {
        if (row >= n) {//Recursive termination statistical solution
            count += 1;
            return
        }
          //~(cols | diag1 | diag2): after the attack positions are reversed, the position of 1 is the position where the queen can be placed
          //(1 < < n) - 1: from right to left, positions greater than n become 0
          //(~ (cols | diag1 | diag2)) & ((1 < < n) - 1): the queen can be placed from right to left, and the positions greater than n become 0
        let bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1)
        while (bits) {
            let p = bits & -bits//Take the first 1 from right to left
            bits = bits & (bits - 1)//Remove the first 1 from right to left
              //Column and two diagonals are closed with non placeable bits
            dfs(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >>> 1)
              
        }
    }
};

Java:

class Solution {
    public int totalNQueens(int n) {
        return dfs(n, 0, 0, 0, 0);
    }

    public int dfs(int n, int row, int clos, int diag1, int diag2) {
        if (row == n) {
            return 1;
        } else {
            int count = 0;
            int bits = ((1 << n) - 1) & (~(clos | diag1 | diag2));
            while (bits != 0) {
                int position = bits & (-bits);
                bits = bits & (bits - 1);
                count += dfs(n, row + 1, clos | position, (diag1 | position) << 1, (diag2 | position) >> 1);
            }
            return count;
        }
    }
}

36. Effective Sudoku (medium)

Method 1: backtracking

  • Idea: prepare row, column, 3 * 3 small blocks, three hash tables or two-dimensional arrays of set or 9. As long as you can judge repetition, cycle from top to bottom and from left to right, check whether there are duplicate numbers in row, column and 3 * 3 small blocks in turn, if so, return false, and then update the hash table or set.
  • Complexity analysis: time complexity: O(1). Sudoku has 81 cells, and each cell can be traversed once. Space complexity: O(1). The size of Sudoku is fixed, so the space of hash table is also fixed.

Js:

var isValidSudoku = function(board) {
    // Direction weight judgment
    let rows = {};//that 's ok
    let columns = {};//column
    let boxes = {};//3 * 3 small square
    // Ergodic Sudoku
    for(let i = 0;i < 9;i++){
        for(let j = 0;j < 9;j++){
            let num = board[i][j];
            if(num != '.'){//A valid number was encountered
                let boxIndex = parseInt((i/3)) * 3 + parseInt(j/3);// Sub Sudoku serial number
                if(rows[i+'-'+num] || columns[j+'-'+num] || boxes[boxIndex+'-'+num]){//Repeated detection
                    return false;
                }
                // Direction + number form a unique key value. If it occurs for the second time, it is a repetition
                  // Update three objects
                rows[i+'-'+num] = true;
                columns[j+'-'+num] = true;
                boxes[boxIndex+'-'+num] = true;
            }
        }
    }
    return true;
};

Java:

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[][] rows = new int[9][9];//The same is true with arrays
        int[][] columns = new int[9][9];
        int[][][] boxes = new int[3][3][9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                char c = board[i][j];
                if (c != '.') {
                    int index = c - '0' - 1;
                    rows[i][index]++;
                    columns[j][index]++;
                    boxes[i / 3][j / 3][index]++;
                    if (rows[i][index] > 1 || columns[j][index] > 1 || boxes[i / 3][j / 3][index] > 1) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
}

37. Solving Sudoku(hard)

  • Idea: cycle rows and columns, try to place 1-9 in each position, and test the legitimacy, including the legitimacy of rows, columns and 3 * 3 blocks. If it is legal, continue to cycle until a legal solution is found. If it is not legal, go back to the state and continue to try other possibilities
  • Complexity analysis: the same as question 36

js:

var solveSudoku = function(board) {
    function isValid(row, col, val, board) {
        let len = board.length
        // The number in the row cannot be repeated
        for(let i = 0; i < len; i++) {
            if(board[row][i] === val) {
                return false
            }
        }
        // The number in the column cannot be repeated
        for(let i = 0; i < len; i++) {
            if(board[i][col] === val) {
                return false
            }
        }
        let startRow = Math.floor(row / 3) * 3
        let startCol = Math.floor(col / 3) * 3

        //The number in the box cannot be repeated
        for(let i = startRow; i < startRow + 3; i++) {
            for(let j = startCol; j < startCol + 3; j++) {
                if(board[i][j] === val) {
                    return false
                }
            }
        }

        return true
    }

    function backTracking() {//Backtracking function
        for(let i = 0; i < board.length; i++) {
            for(let j = 0; j < board[0].length; j++) {//Loop rows and columns
                if(board[i][j] !== '.') continue
                for(let val = 1; val <= 9; val++) {//Try placing 1-9 in the current cell
                    if(isValid(i, j, `${val}`, board)) {//Judge the legitimacy of placed numbers
                        board[i][j] = `${val}`//Place number
                        if (backTracking()) {//Legal return
                            return true
                        }
                        
                        board[i][j] = `.`//Illegal backtracking status
                    }
                }
                return false//The numbers from 1 to 9 are illegal, and false is returned
            }
        }
        return true//All possibilities are tried to complete. Returning true indicates that there is a solution
    }
    backTracking()
    return board
    
};

Java:

class Solution {
    public void solveSudoku(char[][] board) {
        backTracking(board);
    }

    private boolean backTracking(char[][] board){
        for (int i = 0; i < 9; i++){ // Traversal row
            for (int j = 0; j < 9; j++){ // Traversal column
                if (board[i][j] != '.'){ 
                    continue;
                }
                for (char k = '1'; k <= '9'; k++){ //Try placing 1-9 in the current position
                    if (isValid(i, j, k, board)){
                        board[i][j] = k;//Place number
                        if (backTracking(board)){ //Legal return
                            return true;
                        }
                        board[i][j] = '.';
                    }
                }
                return false;//The numbers from 1 to 9 are illegal, and false is returned
            }
        }
        return true;//All possibilities are tried to complete. Returning true indicates that there is a solution
    }

    
    private boolean isValid(int row, int col, char val, char[][] board){
        // Whether peers repeat
        for (int i = 0; i < 9; i++){
            if (board[row][i] == val){
                return false;
            }
        }
        // Is the same column repeated
        for (int j = 0; j < 9; j++){
            if (board[j][col] == val){
                return false;
            }
        }
        // Whether the elements in the small box are repeated
        int startRow = (row / 3) * 3;
        int startCol = (col / 3) * 3;
        for (int i = startRow; i < startRow + 3; i++){
            for (int j = startCol; j < startCol + 3; j++){
                if (board[i][j] == val){
                    return false;
                }
            }
        }
        return true;
    }
}

79. Word search(medium)

  • Idea: traverse the grid from top to bottom and left to right. Each coordinate recursively calls the check (i,j, K) function. i,j represents the grid coordinates and K represents the k-th character of word. If the k-th character can be searched, return true, otherwise return false. There are two termination conditions for the check function

    1. If the characters at positions i and j are not equal to the characters at position k of the string, the search path fails and returns false
    2. If the search reaches the end of the string, a path in the grid is found, and the characters on this path can just form the string s

    If both conditions are not satisfied, the current grid node is added to the visited array. Visited indicates that the node has been accessed, and then continue to try along the four directions of the current grid coordinates. If the substring starting from k is not found, the backtracking status visited[i] [j] = false and continue the subsequent attempt.

  • Complexity analysis: the time complexity O(MN ⋅ 3^L), m and N are the length and width of the grid, and l is the length of the string word. When the check function is called for the first time, it is checked in four directions. The nodes of other coordinates are checked in three directions, and the branches that come will not go back in the opposite direction. Therefore, the time complexity of the check function is 3^L, while the grid has M*N coordinates, And pruning exists, so the time complexity in the worst case is O(MN ⋅ 3^L). The space complexity is O(MN), the visited array space is O(MN), and the maximum depth of the check recursive stack is O(MN) in the worst case
Method 1: backtracking

Js:

var exist = function(board, word) {
    const h = board.length, w = board[0].length;//Length and width of grid
    const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];//Direction array
    const visited = new Array(h);//Indicates whether the array has been accessed
    for (let i = 0; i < visited.length; ++i) {//Initialize visited array
        visited[i] = new Array(w).fill(false);
    }
    const check = (i, j, s, k) => {//Check whether the substring composed of 0-k characters can be searched from grids i and j
          //If the characters at positions i and j are not equal to the k-th character, the search path fails and returns false
        if (board[i][j] != s.charAt(k)) {
            return false;
         //If the search reaches the end of the string, a path in the grid is found, and the characters on this path can just form the string s
        } else if (k == s.length - 1) {
            return true;
        }
        visited[i][j] = true;//Tags i, j have been accessed
        let result = false;
        for (const [dx, dy] of directions) {//Continue to try to find in the four directions of i and j
            let newi = i + dx, newj = j + dy;
            if (newi >= 0 && newi < h && newj >= 0 && newj < w) {//Legal check of new coordinate position
                if (!visited[newi][newj]) {//New coordinates cannot exist in visited, that is, they cannot be accessed
                    const flag = check(newi, newj, s, k + 1);//Continue checking for new coordinates
                    if (flag) {//If a string is found in the grid, the loop is skipped
                        result = true;
                        break;
                    }
                }
            }
        }
        visited[i][j] = false;//Backtracking status
        return result;//Return results
    }

    for (let i = 0; i < h; i++) {
        for (let j = 0; j < w; j++) {
            const flag = check(i, j, word, 0);
            if (flag) {
                return true;
            }
        }
    }
    return false;
};

Java:

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

    public boolean check(char[][] board, boolean[][] visited, int i, int j, String s, int k) {
        if (board[i][j] != s.charAt(k)) {
            return false;
        } else if (k == s.length() - 1) {
            return true;
        }
        visited[i][j] = true;
        int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        boolean result = false;
        for (int[] dir : directions) {
            int newi = i + dir[0], newj = j + dir[1];
            if (newi >= 0 && newi < board.length && newj >= 0 && newj < board[0].length) {
                if (!visited[newi][newj]) {
                    boolean flag = check(board, visited, newi, newj, s, k + 1);
                    if (flag) {
                        result = true;
                        break;
                    }
                }
            }
        }
        visited[i][j] = false;
        return result;
    }
}

46. Full arrangement (medium)

  • Idea: prepare the path array, store the number arrangement in each backtracking recursive branch, call the backtracking function, pass in num, Num length, used array, used represents the number that has been used, loop the number in num in the backtracking function, add the elements in num to the path in each layer of loop, and then call the backtracking function recursively. After the call is completed, the state before backtracking, When the length of the path array is the same as that of nums, an arrangement is found.
  • Complexity: time complexity O(n*n!). Space complexity O(n), recursive stack depth

js:

var permute = function(nums) {
    const res = [], path = [];
    backtracking(nums, nums.length, []);//Call the backtracking function to pass in num, Num length, and used array
    return res;
    
    function backtracking(n, k, used) {
        if(path.length === k) {//Recursive termination condition
            res.push(Array.from(path));
            return;
        }
        for (let i = 0; i < k; i++ ) {
            if(used[i]) continue;//Skip this cycle after it has been used
            path.push(n[i]);
            used[i] = true; 
            backtracking(n, k, used);//recursion
            path.pop();//Backtracking pop s the element push ed in, then marks it as unused and continues to other branches
            used[i] = false;
        }
    }
};

java:

class Solution {

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

    private void permuteHelper(int[] nums){
        if (path.size() == nums.length){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < nums.length; i++){
            if (used[i]){
                continue;
            }
            used[i] = true;
            path.add(nums[i]);
            permuteHelper(nums);
            path.removeLast();
            used[i] = false;
        }
    }
}

77. Portfolio (medium)

  • Idea: the backtracking function passes in n, K and the selected element position startIndex. In each layer of recursion, start the cycle from startIndex to the position of n - (k - path.length) + 1, add these numbers to the path, then startIndex plus 1, continue the recursive function to enter the next branch, complete the backtracking state after the call, and terminate this layer of branch when the length of path is equal to K, Add to the results.
  • Complexity: time complexity: O(C(n, k) * k). The total number of enumeration results is C(n, k). It takes O(k) time to get one result each time. Space complexity: O(n), the maximum is n-layer recursive stack.

js:

const combine = (n, k) => {
  const res = [];

  const helper = (startIndex, path) => { //startIndex indicates the starting position of the search (path is a combination of each branch)
    if (path.length == k) {
      res.push(path.slice());       //A copy is required to avoid being affected by other branches
      return;                       
    }
    for (let i = startIndex; i <= n - (k - path.length) + 1; i++) {//prune
      path.push(i);                    //Join path
      helper(i + 1, path);             //Next level recursion
      path.pop();                      //Backtracking status
    }
  };

  helper(1, []); //Recursive entry
  return res;
}

java:

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

    private void combineHelper(int n, int k, int startIndex){
        if (path.size() == k){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){
            path.add(i);
            combineHelper(n, k, i + 1);
            path.removeLast();
        }
    }
}

17. Letter combination of telephone number (medium)

Method 1.dfs + backtracking

  • Idea: depth first traversal. The traversal function passes in the string formed by each layer and a position pointer pointing to the character. When the position of the pointer reaches the end of the string, the formed string will be added to the result array. Each recursive layer traverses the characters corresponding to the numbers of this layer, and then passes in new characters. The pointer moves back once and recurses continuously
  • Complexity: time complexity O(3^m * 4^n), m and N are the numbers corresponding to three letters and four letters respectively. Space complexity O(m+n), depth of recursive stack, max. m+n

js:

//Input: digits = "23"
//Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
var letterCombinations = (digits) => {
    if (digits.length == 0) return [];
    const res = [];
    const map = {//Establish the mapping relationship between telephone numbers and letters
        2: "abc",
        3: "def",
        4: "ghi",
        5: "jkl",
        6: "mno",
        7: "pqrs",
        8: "tuv",
        9: "wxyz",
    };

    const dfs = (curStr, i) => {//curStr is the string of each recursive layer, and i is the scanned pointer
        if (i > digits.length - 1) {//Boundary conditions, recursive exit
            res.push(curStr); //The solution of one branch is pushed into res
            return; //End the recursive branch and enter another branch
        }
        const letters = map[digits[i]]; //Take out the letter corresponding to the number
        for (const l of letters) {
            //Branch into different letters
            dfs(curStr + l, i + 1); //Parameter, pass in a new string, i shift right and continue recursion
        }
    };
    dfs("", 0); // Recursive entry, passing in an empty string, i the initial position of 0
    return res;
};

java:

class Solution {
    String[] map = { " ", "*", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };

    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return new ArrayList<>();
        }
        dfs(digits, new StringBuilder(), 0);
        return res;
    }

    List<String> res = new ArrayList<>();

    void dfs(String digits, StringBuilder curStr, int index) {
        if (index == digits.length()) {
            res.add(curStr.toString());
            return;
        }
        char c = digits.charAt(index);
        int pos = c - '0';
        String map_string = map[pos];
        for (int i = 0; i < map_string.length(); i++) {
            curStr.append(map_string.charAt(i));
            dfs(digits, curStr, index + 1);
            curStr.deleteCharAt(curStr.length() - 1);
        }
    }
}

Method 2.bfs

  • Idea: use queue breadth first traversal. First cycle the number array, then take out the corresponding letters, form a new string with the string of the current layer, and add it to the queue. After traversal, the last layer of the queue is the solution.
  • Complexity: time complexity O(3^m * 4^n), m and N are the number of arrays corresponding to three characters and four letters respectively. Space complexity O(3^m * 4^n). The maximum space size of the queue is 3^m * 4^n

js:

var letterCombinations = (digits) => {
    if (digits.length == 0) return [];
    const map = {
        2: "abc",
        3: "def",
        4: "ghi",
        5: "jkl",
        6: "mno",
        7: "pqrs",
        8: "tuv",
        9: "wxyz",
    };

    const queue = [];
    queue.push("");
    for (let i = 0; i < digits.length; i++) {//Loop each character of the number
        const levelSize = queue.length; //Number of nodes in the current layer
        for (let j = 0; j < levelSize; j++) {
            const curStr = queue.shift(); //String of the current layer
            const letters = map[digits[i]];//Gets the alphanumeric character corresponding to the number
            for (const l of letters) {
                queue.push(curStr + l); //The newly generated string is listed
            }
        }
    }
    return queue; //The string generated by the last layer is the solution
};

java:

class Solution {
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return new ArrayList<String>();
        }
        String[] map = { " ", "*", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
        List<String> res = new ArrayList<>();

        res.add("");
        for (int i = 0; i < digits.length(); i++) {
            String letters = map[digits.charAt(i) - '0'];
            int levelSize = res.size();
            for (int j = 0; j < levelSize; j++) {
                String tmp = res.remove(0);
                for (int k = 0; k < letters.length(); k++) {
                    res.add(tmp + letters.charAt(k));
                }
            }
        }
        return res;
    }
}

78. Subset (medium)

  • Idea: the backtracking function passes in the startIndex at the beginning of the character and recurses continuously. The startIndex of each layer is increased by 1. When one branch ends, it starts backtracking and enters another branch.
  • Complexity: time complexity O(n*2^n). As shown in the figure, the recursive states are 2^n states, and the complexity of constructing path array for each state is O(n). Space complexity O(n), that is, the space of recursive stack

js:

//Example: num = [1,2,3]
var subsets = function(nums) {
    let result = []//Storage results
    let path = []//Store the results of a branch
    function backtracking(startIndex) {//The position at which the startIndex character recursion begins
        result.push(path.slice())//path.slice() breaks the reference relationship with path
        for(let i = startIndex; i < nums.length; i++) {//Recursion from startIndex
            path.push(nums[i])//Current character push path
            backtracking(i + 1)//startIndex moves backward one position to continue recursion
            path.pop()//Backtracking status
        }
    }
    backtracking(0)
    return result
};

java:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> subsets(int[] nums) {
        if (nums.length == 0){
            result.add(new ArrayList<>());
            return result;
        }
        backtracking(nums, 0);
        return result;
    }

    private void backtracking(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length){
            return;
        }
        for (int i = startIndex; i < nums.length; i++){
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.removeLast();
        }
    }
}

473. Match square (medium)

  • Idea: sort the nums array to reduce the number of backtracking. Keep trying to put the elements in nums into four buckets. If they can all be put down, they can be assembled into a square

js:

//Example: [1,2,2,2,1]
var makesquare = function (nums) {
    function backtrack(i, nums, edge, bucket) {
        if (i >= nums.length) {//Recursive end condition
            return true;
        }
        for (let j = 0; j < 4; j++) {//Cycle 4 barrels
            if (bucket[j] + nums[i] > edge) {//This bucket can't hold. Keep looking for the next bucket
                continue;
            }
            bucket[j] += nums[i];//Adds the current element to the bucket
            if (backtrack(i + 1, nums, edge, bucket)) {//Index i plus 1 continues to recurse the elements in the next nums
                return true;//The next element can be put into the bucket
            }
            bucket[j] -= nums[i];//Backtracking status
        }
        return false;//If you don't put the proper bucket at the end of the cycle, it can't form a square
    }

    if (nums.length < 4) {//nums is less than 4 in length and cannot form a square directly
        return false;
    }
    let sum = 0;
    for (let i = 0; i < nums.length; i++) {
        sum += nums[i];
    }
    if (sum % 4) {//The sum of nums cannot be divided by 4 and cannot form a square row
        return false;
    }
    nums.sort((a, b) => b - a);//Sort nums
    let bucket = Array(4).fill(0);//Prepare 4 barrels
    return backtrack(0, nums, sum / 4, bucket);//The index i, num, a side length, and bucket of the passed in num element
};

Tags: Algorithm leetcode

Posted on Mon, 29 Nov 2021 23:54:28 -0500 by punk3d