算法学习 | 回溯算法之深度优先搜索常见题型练习

目录

岛屿的最大面积

电话号码的字母组合 

二进制手表 

组合总数 

活字印刷


岛屿的最大面积

题目链接:leetcode-695.岛屿的最大面积

示例 

输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]

输出:6

题目分析 

由题意知,在这个二维数组中,有很多陆地,因此维护一个最大面积,一个临时面积,一个标记数组。遍历二维数组的每一个位置,当遇到陆地且该陆地未被标记过时,就进行搜索,搜索的方式为,从该陆地开始,遍历其上下左右,再遍历每个位置的上下左右,直至遇到边界或海洋或该陆地已被标记过为止。 

class Solution {
    int ret;
    int tmpArea;
    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid[0].length == 0) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        int[][] book = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1 && book[i][j] == 0) {
                    tmpArea = 0;
                    dfs(grid, book, i, j);
                    ret = Math.max(ret, tmpArea);
                }
            }
        }
        return ret;
    }
    private void dfs(int[][] grid, int[][] book, int row, int col) {
        if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == 0) {
            return ;
        }
        if (book[row][col] == 1) {
            return ;
        }
        book[row][col] = 1;
        tmpArea++;
        dfs(grid, book, row + 1, col);
        dfs(grid, book, row - 1, col);
        dfs(grid, book, row, col + 1);
        dfs(grid, book, row, col - 1);
    }
}

电话号码的字母组合 

题目链接:leetcode-17.电话号码的字母组合

示例 

输入:digits = "23"

输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

题目分析

根据题意,每个数字表示几个字符,因此,定义一个字符串数组来记录每个数字所代表的几个字符

String[] mapString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

 维护一个深度和当前的可能的字母组合,当输入的数字长度等于深度时,就将当前的字母组合加入到结果集中,结束该路径并继续向上回溯;根据深度找到当前的数字,再根据这个数字得到该数字所代表的字符,依次遍历每一种可能,直至所有字符遍历结束。

class Solution {
    String[] mapString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    List<String> ret = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return new ArrayList<>();
        }
        StringBuilder curStr  = new StringBuilder("");
        dfs(digits, curStr, 0);
        return ret;
    }
    private void dfs(String digits, StringBuilder curStr, int curDepth) {
        if (curDepth == digits.length()) {
            ret.add(curStr.toString());
            return ;
        }
        int curIndex = digits.charAt(curDepth) - '0';
        String curMap = mapString[curIndex];
        for (int i = 0; i < curMap.length(); i++) {
            dfs(digits, curStr.append(curMap.charAt(i)), curDepth + 1);
            curStr.deleteCharAt(curStr.length() - 1);
        }
    }
}

二进制手表 

题目链接:leetcode-401.二进制手表

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。

给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。

小时不会以零开头:例如,"01:00" 是无效的时间,正确的写法应该是 "1:00" 。

分钟必须由两位数组成,可能会以零开头:例如,"10:2" 是无效的时间,正确的写法应该是 "10:02" 。

 

 例如

输入:turnedOn = 1

输出:["0:01","0:02","0:04","0:08","0:16","0:32","1:00","2:00","4:00","8:00"]

题目分析

表示时的有1,2,4,8,表示分的有1,2,4,8,16,32,分别定义两个数组用来存储时的可能数字和分的可能数字。由于题目中要求小时0~11,分钟0~59,因此表示时的灯最多亮三个,表示分的灯最多亮5个,因此给定的数字需在0~8之间。依次从每一个时和分进行深度优先搜索,当分钟和大于59或小时和大于11就返回,当深度达到给定的数字时,拼接时和分将其加入到结果集中。

class Solution {
    int[] hours = {1, 2, 4, 8};
    int[] minutes = {1, 2, 4, 8, 16, 32};
    List<String> ret = new ArrayList<>(); 
    public List<String> readBinaryWatch(int turnedOn) {
        if (turnedOn < 0 || turnedOn >= 9) {
            return new ArrayList<>();
        }
        dfs(turnedOn, 0, 0, 0, 0, 0);
        return ret;
    }
    private void dfs(int turnedOn, int hour, int min, int i, int j, int curDepth) {
        if (min > 59 || hour > 11) {
            return ;
        }
        if (curDepth == turnedOn) {
            StringBuilder cur = new StringBuilder();
            cur.append(hour);
            if (min < 10) {
                cur.append(":0" + min);
            } else {
                cur.append(":" + min);
            }
            ret.add(cur.toString());
            return ;
        }
        for (; i < hours.length; i++) {
            hour += hours[i];
            dfs(turnedOn, hour, min, i + 1, j, curDepth + 1);
            hour -= hours[i];
        }
        for (; j < minutes.length; j++) {
            min += minutes[j];
            dfs(turnedOn, hour, min, i, j + 1, curDepth + 1);
            min -= minutes[j];
        }
    }
}

组合总数 

题目链接:leetcode-39.组合总数

示例

输入:candidates = [2,3,6,7], target = 7

输出:[[2,2,3],[7]]

题目分析:

由于同一个数字可以无限制重复被选取,因此每次从上一个位置开始搜索,边界值的设定:当目前的累加值大于给定的值就返回,当累加值等于给定的值就将其加入结果集中。

class Solution {
    List<List<Integer>> ret = new ArrayList<>();
    List<Integer> curRet = new ArrayList<>();
    int curSum;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        dfs(candidates, target, 0);
        return ret;
    }
    private void dfs(int[] candidates, int target, int pre) {
        if (curSum > target) {
            return ;
        }
        if (curSum == target) {
            ret.add(new ArrayList<>(curRet));
            return ;
        }
        for (int i = pre; i < candidates.length; i++) {
            if (candidates[i] > target) {
                continue;
            }
            curSum += candidates[i];
            curRet.add(candidates[i]);
            dfs(candidates, target, i);
            curSum -= curRet.get(curRet.size() - 1);
            curRet.remove(curRet.size() - 1);
        }
    }
}

活字印刷

题目链接:leetcode-1079.活字印刷

 

示例

输入:"AAB"

输出:8 

题目分析

 按照题意tiles中每一个位置的字符在组合中只能出现一次,所以可以用一个标记辅助当去组合新的组合时,可以与tiles中的每一个位置组合,但是如果当前位置已经在当前组合中出现过,则跳过 虽然此题中每一个位置的字符在组合中只能出现一次,但是tiles中可能有相同的字符,所以需要考虑重复的组合,使用HashSet可以实现去重

class Solution {
    public int numTilePossibilities(String tiles) {
        Set<String> ret = new HashSet<>();
        List<Integer> usedIndex = new ArrayList<>();
        for (int i = 0; i < tiles.length(); i++) {
            usedIndex.add(0);
        }
        StringBuilder curStr = new StringBuilder("");
        dfs(tiles, curStr, usedIndex, ret);
        return ret.size();
    }
    private void dfs(String tiles, StringBuilder curStr, List<Integer> usedIndex, Set<String> ret) {
        if (curStr.length() != 0) {
            ret.add(curStr.toString());
        }
        for (int i = 0; i < tiles.length(); i++) {
            if (usedIndex.get(i) == 1) {
                continue;
            }
            usedIndex.set(i, 1);
            dfs(tiles, curStr.append(tiles.charAt(i)), usedIndex, ret);
            usedIndex.set(i, 0);
            curStr.deleteCharAt(curStr.length() - 1);
        }
    }
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值