leetcode打卡-回溯II

473. 火柴拼正方形

leetcode题目链接:https://leetcode.cn/problems/matchsticks-to-square

leetcode AC记录:

思路:计算数组总和,如果不能被4整除,则直接返回false。总和除以4作为边长,使用大小为4的数组变量edges保存结果集,遍历每一根火柴放置到4条边上,如果放置火柴后累加和小于等于边长值,则进行回溯,放置下一根火柴,时间复杂度为O(4^n),理由是每个火柴都有4种选择,这是一种解法。还有一种解法是遍历每一个火柴,尝试组成当前的边,组成后边的个数加1,然后用剩余的火柴组成剩余的边,但是时间复杂度太高,大约等于n!。

代码如下:

public boolean makesquare(int[] matchsticks) {
        int sum = 0;
        for(int i = 0;i < matchsticks.length;i++) {
            sum += matchsticks[i];
        }

        if(sum % 4 != 0) {
            return false;
        }

        int edgeLength = sum / 4;

        Arrays.sort(matchsticks);

        return hs(matchsticks, new int[4], matchsticks.length-1, edgeLength);
    }

    public boolean hs(int[] matchsticks, int[] edges, int index, int edgeLength) {
        if(index == -1) {
            return true;
        }

        
        for(int i = 0;i < edges.length;i++) {
            edges[i] += matchsticks[index];

            if(edges[i] <= edgeLength) {
                if(hs(matchsticks, edges, index-1, edgeLength)) {
                    return true;
                }
            }
            
            edges[i] -= matchsticks[index];
        }

        return false;
    }

332. 重新安排行程

leetcode题目链接:https://leetcode.cn/problems/reconstruct-itinerary

leetcode AC记录:

思路:关键在于如何选择数据结构,使用map维护起点和终点的映射,使用treemap维护终点的次数(为了排序),使用回溯法遍历map,然后取出来输出。

代码如下:

public List<String> findItinerary(List<List<String>> tickets) {
        Map<String, TreeMap<String, Integer>> map = new HashMap<>(tickets.size() + 1);
        for(List<String> ticket : tickets) {
            String start = ticket.get(0);
            String end = ticket.get(1);

            TreeMap<String, Integer> countMap = map.getOrDefault(start, new TreeMap<>());
            countMap.put(end, countMap.getOrDefault(end, 0)  + 1);
            map.put(start, countMap);
        }

        List<String> res = new ArrayList<>(tickets.size() + 1);
        res.add("JFK");
        hs(tickets.size() + 1, "JFK", map, res);
        return res;
    }

    public boolean hs(int length, String start, Map<String, TreeMap<String, Integer>> map, List<String> res) {
        if(res.size() == length) {
            return true;
        }
        Map<String, Integer> countMap = map.get(start);
        if(countMap == null) {
            return false;
        }

        for(Map.Entry<String, Integer> countEntry : countMap.entrySet()) {
            Integer count = countEntry.getValue();
            if(count > 0) {
                countEntry.setValue(--count);
                res.add(countEntry.getKey());
                if(hs(length, countEntry.getKey(), map, res)) {
                    return true;
                } else {
                    res.remove(res.size()-1);
                    countEntry.setValue(++count);
                }
            }
        }

        return false;
    }

 51. N 皇后

leetcode题目链接:https://leetcode.cn/problems/n-queens/

leetcode AC记录:

思路:主要是校验当前放置n皇后是否合法,然后使用回溯逐行放置即可。

代码如下:

 public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>(16);
        char[][] path = new char[n][n];
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                path[i][j] = '.';
            }
        }
        hs(res, path, 0, n);
        return res;
    }

    public void hs(List<List<String>> res, char[][] path, int x, int n) {
        if(x >= n) {
            res.add(Arrays.stream(path).map(String::valueOf).collect(Collectors.toList()));
            return;
        }

        for(int j = 0; j < n;j++) {
            path[x][j] = 'Q';
            if(isValid(path, x, j, n)) {
                hs(res, path, x + 1, n);
            }
            path[x][j] = '.';
        }
    }

    /**
     * 判断当前放置的棋子是否合法
     */
    public boolean isValid(char[][] path, int x, int y, int n) {

        //遍历每列
        int num = 0;
        for(int j = 0;j < n; j++) {
            if(path[x][j] == 'Q') {
                num++;
            }
            if(num > 1) {
                return false;
            }
        }

        //遍历每列
        num = 0;
        for(int i = 0;i < n;i++) {
            if(path[i][y] == 'Q') {
                num++;
            }
            if(num > 1) {
                return false;
            }
        }

        //遍历左斜线
        int xbegin = x;
        int ybegin = y;
        while(xbegin > 0 && ybegin > 0) {
            xbegin--;
            ybegin--;
        }

        num = 0;
        while(xbegin < n && ybegin < n) {
            if(path[xbegin][ybegin] == 'Q') {
                num++;
            }
            if(num > 1) {
                return false;
            }
            xbegin++;
            ybegin++;
        }

        //遍历右斜线
        xbegin = x;
        ybegin = y;
        while(xbegin > 0 && ybegin + 1< n) {
            xbegin--;
            ybegin++;
        }

        num = 0;
        while(xbegin < n && ybegin >= 0) {
            if(path[xbegin][ybegin] == 'Q') {
                num++;
            }
            if(num > 1) {
                return false;
            }
            xbegin++;
            ybegin--;
        }

        return true;
    }

 37. 解数独

leetcode题目链接:https://leetcode.cn/problems/sudoku-solver/

leetcode AC记录:

思路:这个题比较难,二维回溯,两个for循环遍历数独数组,遇到字符“.”放置1-9数字,然后进行回溯,如果合法直接返回true,如果1-9用完了没有合适的,返回false。

代码如下:

 public void solveSudoku(char[][] board) {
        hs(board);
    }

    public boolean hs(char[][] board) {
        for(int i = 0;i < 9;i++) {
            for(int j = 0;j < 9;j++) {
                if(board[i][j] != '.') {
                    continue;
                } else {
                    for(int k = 1;k <= 9;k++) {
                        board[i][j] = (char)(k + '0');
                        if(isValid(board, i, j)) {
                            if(hs(board)) {
                                return true;
                            }
                        }
                        board[i][j] = '.';
                    }
                    return false;
                }
            }
        }

        return true;
    }

    public boolean isValid(char[][] board, int x, int y) {
        int[] nums = new int[9];
        //判断所在行是否有效
        for(int j = 0;j < 9;j++) {
            if(board[x][j] >= '1' && board[x][j] <= '9') {
                nums[board[x][j] - '1']++;
                if(nums[board[x][j] - '1'] > 1) {
                    return false;
                }
            }
        }

        //判断所在列是否有效
        Arrays.fill(nums, 0);
        for(int i = 0;i < 9;i++) {
            if(board[i][y] >= '1' && board[i][y] <= '9') {
                nums[board[i][y] - '1']++;
                if(nums[board[i][y] - '1'] > 1) {
                    return false;
                }
            }


        }

        //判断所在方块是否有效
        Arrays.fill(nums, 0);
        int beginX = 0, endX = 3, beginY = 0 , endY = 3;
        if(x >= 3 && x < 6) {
            beginX = 3;
            endX = 6;
        } else if(x >= 6 && x < 9) {
            beginX = 6;
            endX = 9;
        }

        if(y >= 3 && y < 6) {
            beginY = 3;
            endY = 6;
        } else if(y >= 6 && y < 9) {
            beginY = 6;
            endY = 9;
        }

        for(int i = beginX;i < endX;i++) {
            for(int j = beginY;j < endY;j++) {
                if(board[i][j] >= '1' && board[i][j] <= '9') {
                    nums[board[i][j] - '1']++;
                    if(nums[board[i][j] - '1'] > 1) {
                        return false;
                    }
                }


            }
        }

        return true;
    }

 842. 将数组拆分成斐波那契序列

leetcode题目链接:力扣

leetcode AC记录:

代码如下:

public List<Integer> splitIntoFibonacci(String num) {
        List<Integer> res = new ArrayList<>(16);
        boolean flag = hs(res, num, 0);
        return flag ? res : new ArrayList<>();
    }

    public boolean hs(List<Integer> res, String num, int beginIndex) {
        if(res.size() > 2 && beginIndex == num.length()) {
            return true;
        }

        for(int i = 1;beginIndex + i <= num.length();i++) {
            String str = num.substring(beginIndex, beginIndex + i);
            if(str.length() > 1 && str.charAt(0) == '0') {
                break;
            }

            long valueLong = Long.parseLong(str);
            if(valueLong > Integer.MAX_VALUE) {
                break;
            }
            int value = (int) valueLong;
            if(res.size() < 2) {
                res.add(value);
                if(hs(res, num, beginIndex + i)) {
                    return true;
                }
                res.remove(res.size()-1);
            } else {
                int lastSum = res.get(res.size()-1) + res.get(res.size()-2);
                if(value == lastSum) {
                    res.add(value);
                    if(hs(res, num, beginIndex + i)) {
                        return true;
                    }
                    res.remove(res.size()-1);
                } else if(value > lastSum) {
                    break;
                }
            }
        }

        return false;
    }

306. 累加数

leetcode题目链接:https://leetcode.cn/problems/additive-number/

leetcode AC记录:

思路:使用回溯算法,需要注意的是判断是否是斐波纳切数列,case里字符串转为int后会超出限制,所以使用大数相加进行判断,java里采用BigInteger。

代码如下:

public boolean isAdditiveNumber(String num) {
        if(num.length() < 3) {
            return false;
        }
        List<BigInteger> res = new ArrayList<>(num.length());
        return hs(res, num, 0);
    }

    public boolean hs(List<BigInteger> res, String num, int begin) {
        if(begin == num.length() && res.size() > 2) {
            return true;
        }

        for(int i = begin + 1; i <= num.length();i++) {
            String str = num.substring(begin, i);
            if(str.length() > 1 && str.charAt(0) == '0') {
                continue;
            }

            BigInteger value = new BigInteger(str);

            if(res.size() < 2) {
                res.add(value);
                if(hs(res, num, i)) {
                    return true;
                }
                res.remove(res.size() - 1);
            } else {
                BigInteger lastOneEle = res.get(res.size()-1);
                BigInteger lastTwoEle = res.get(res.size()-2);
                if(lastOneEle.add(lastTwoEle).equals(value)) {
                    res.add(value);
                    if(hs(res, num, i)) {
                        return true;
                    }
                    res.remove(res.size()-1);
                }
            }
        }

        return false;
    }

980. 不同路径 III

leetcode题目链接:https://leetcode.cn/problems/unique-paths-iii/

leetcode AC记录:

思路:使用回溯法,找到并记录开始和结束的下标、空格的数量。

结束条件为:开始位置下标和结束位置下标重合,并且空格的数量等于空格的总数,则进行结果收集;如果空格的数量小于空格总数,开始结束下标重合不收集结果并且直接返回。

从开始位置进行回溯,如果开始位置的下标超出范围或者开始位置的值为-1,直接返回;如果符合条件,则将该位置记录为已访问,并且回溯上下左右四个方向。

回溯的方法需要的参数:结果集,原数组,是否访问数组,开始位置,结束位置,空格数量,空格总数。

代码如下:

public int uniquePathsIII(int[][] grid) {
        //找到开始和结束的下标
        int beginx = 0;
        int beginy = 0;

        int endx = 0;
        int endy = 0;
        int blankTotal = 0;
        for(int i = 0;i < grid.length;i++) {
            for(int j = 0;j < grid[0].length;j++) {
                if(grid[i][j] == 1) {
                    beginx = i;
                    beginy = j;
                } else if(grid[i][j] == 2) {
                    endx = i;
                    endy = j;
                } else if(grid[i][j] == 0) {
                    blankTotal++;
                }
            }
        }

        int[] res = new int[1];
        boolean[][] used = new boolean[grid.length][grid[0].length];

        hs(res, beginx, beginy, endx, endy, 0, blankTotal, used, grid);
        return res[0];
    }

    public void hs(int[] res, int beginx, int beginy, int targetx, int targety, int blankCount, int blankTotal, boolean[][] used, int[][] grid) {
        if(beginx == targetx && beginy == targety && blankCount == blankTotal) {
            res[0]++;
            return;
        } else if(beginx == targetx && beginy == targety && blankCount < blankTotal) {
            return;
        }

        if(beginx < 0 || beginx >= grid.length || beginy < 0 || beginy >= grid[0].length || used[beginx][beginy] || grid[beginx][beginy] == -1) {
            return;
        }
        
        used[beginx][beginy] = true;
        int currentBlankCount =  grid[beginx][beginy] == 0 ? blankCount + 1 : blankCount;
        hs(res, beginx-1, beginy, targetx, targety, currentBlankCount, blankTotal, used, grid);
        hs(res, beginx+1, beginy, targetx, targety, currentBlankCount, blankTotal, used, grid);
        hs(res, beginx, beginy-1, targetx, targety, currentBlankCount, blankTotal, used, grid);
        hs(res, beginx, beginy+1, targetx, targety, currentBlankCount, blankTotal, used, grid);
        used[beginx][beginy] = false;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值