算法题目 搜索

目录

    • BFS
    • DFS
        • 连通块的最大面积
        • 连通块的数目
        • 好友关系连通分量数目
        • 能到达的太平洋和大西洋的区域
        • 填充内部封闭区域
    • 回溯法
      • 排列问题
        • 不含重复数字的全排列
        • 含有重复数字的全排列
      • 组合问题
        • 数字键盘组合
        • N个数中选出K个数的组合
        • 从N个位置中选出K个位置
        • 可重复使用元素的和为定值的组合
        • 含有重复元素的和为定值的组合
        • N个数中取K个数的和为定值的组合
        • 不含重复元素的子集
        • 含有重复元素的子集
      • 二维空间
        • 字符矩阵中进行字符串搜索
        • 9x9 数独
        • N皇后问题
      • 字符串分割
        • 分割字符串使得其符合IP地址格式
        • 分割字符串使得其每部分都是回文串
      • 二叉树
        • 打印二叉树的所有路径


BFS


DFS


广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。

而深度优先搜索在得到一个新节点时立即对新节点进行遍历,直到没有新节点了,此时返回,然后继续以上步骤。

从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。

DFS 与回溯法会省去一个回退的过程

在程序实现 DFS 时需要考虑以下问题:

  • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。

连通块的最大面积

leetcode 695 岛屿的最大面积(中等)
给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

示例 1:

[
 [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。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。

示例 2:

[[0,0,0,0,0,0,0,0]]

对于上面这个给定的矩阵, 返回 0。

注意: 给定的矩阵grid 的长度和宽度都不超过 50。

题解:

class Solution {
    private int res;
    private int m, n;
    private boolean[][] used;
    private int[][] nextStep = {{1,0}, {-1,0}, {0,1}, {0,-1}};
    
    public int maxAreaOfIsland(int[][] grid) {
        if(grid.length==0 || grid[0].length==0){
            return 0;
        }
        
        m = grid.length;
        n = grid[0].length;
        used = new boolean[m][n];
               
        for(int i=0; i=0 && i=0 && j

连通块的数目

leetcode 200 岛屿数量(中等)
给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
 11110
 11010
 11000
 00000
输出: 1

示例 2:

输入:
 11000
 11000
 00100
 00011
输出: 3

题解:

class Solution {
    private int res;
    private int m, n;
    private boolean[][] used;
    private int[][] dist = {{1,0}, {-1,0}, {0,1}, {0,-1}};
    
    public int numIslands(char[][] grid) {
        if(grid.length == 0 || grid[0].length==0){
            return 0;
        }
        m = grid.length;
        n = grid[0].length;
        used = new boolean[m][n];
        
        for(int i=0; i=0 && i=0 && j

好友关系连通分量数目

leetcode 547 朋友圈(中等)

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 1:

输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。

示例 2:

输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。

注意:

  • N 在[1,200]的范围内。
  • 对于所有学生,有M[i][i] = 1。
  • 如果有M[i][j] = 1,则有M[j][i] = 1。

题解:

class Solution {
    private int res;
    private int n;
    private boolean[] used;
    
    public int findCircleNum(int[][] M) {
        if(M.length == 0){
            return 0;
        }    
        
        n = M.length;
        used = new boolean[n];
        
        // 寻找没有建立盆友圈的人
        for(int i=0; i

能到达的太平洋和大西洋的区域

leetcode 417 太平洋大西洋水流问题(中等)

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

提示:

  • 输出坐标的顺序不重要
  • m 和 n 都小于150

示例:

给定下面的 5x5 矩阵:
 太平洋 ~ ~ ~ ~ ~
   ~ 1 2 2 3 5 *
   ~ 3 2 3 4 4 *
   ~ 2 4 5 3 1 *
   ~ 6 7 1 4 5 *
   ~ 5 1 1 2 4 *
    * * * * * 大西洋
返回:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中加粗的单元).

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    private int m, n;
    private int[][] dist = {{0,1}, {0,-1}, {1,0}, {-1,0}};
    
    public List> pacificAtlantic(int[][] matrix) {
        if(matrix.length==0 || matrix[0].length==0){
            return res;
        }
        
        m = matrix.length;
        n = matrix[0].length;
        
        boolean[][] canReachA = new boolean[m][n];
        boolean[][] canReachP = new boolean[m][n];
        
        for(int i=0; i list = new ArrayList<>();
                    list.add(i);
                    list.add(j);
                    
                    res.add(list);
                }
            }
        }
        
        return res;
    }
    
    private boolean inArea(int i, int j){
        return i>=0 && i=0 && j=matrix[i][j]){
                dfs(matrix, nextI, nextJ, canReach);
            }
        }
    }
}

填充内部封闭区域

leetcode 130 被围绕的区域(中等)
给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。

找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例:

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

运行你的函数后,矩阵变为:

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

解释:

被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

题解:

class Solution {
    private int m, n;
    private int[][] dist = {{1,0},{-1,0},{0,1},{0,-1}};
    
    public void solve(char[][] board) {
        if(board.length==0 || board[0].length == 0){
            return;
        }    
        
        m = board.length;
        n = board[0].length;
        
        for(int i=0; i=0 && i=0 && j

回溯法

Backtracking(回溯)属于 DFS。

  • 普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
  • Backtracking 主要用于求解 排列组合 问题,例如有 { ‘a’,‘b’,‘c’ } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。

因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:

  • 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
  • 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

排列问题

不含重复数字的全排列

leetcode 46 全排列(中等)

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
 [1,2,3],
 [1,3,2],
 [2,1,3],
 [2,3,1],
 [3,1,2],
 [3,2,1]
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    private boolean[] used;
    
    public List> permute(int[] nums) {
        int n = nums.length;
        if(n==0){
            return res;
        }
        
        used = new boolean[n];
        LinkedList list = new LinkedList<>();
        sub(nums, 0, list);
        return res;        
    }
    
    private void sub(int[] nums, int index, LinkedList list){
        if(index == nums.length){
            res.add(new LinkedList(list));
            return;
        }
        
        for(int i=0; i

含有重复数字的全排列

leetcode 47 全排列II(中等)
给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
 [1,1,2],
 [1,2,1],
 [2,1,1]
]

题解:
数组元素含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。

在实现上,要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。

class Solution {
    private ArrayList> res = new ArrayList>();   
    private boolean[] used;
    
    public List> permuteUnique(int[] nums) {
        int n = nums.length;
        if(n==0){
            return res;
        }
        
        used = new boolean[n];
        
        // 去重操作,需要先排个序
        Arrays.sort(nums);
        LinkedList list = new LinkedList<>();
        sub(nums, 0, list);
        return res;
    }
    
    private void sub(int[] nums, int index, LinkedList list){
        if(index == nums.length){
            res.add(new LinkedList(list));
            return;
        }
        
        for(int i=0; i0 && nums[i]==nums[i-1] && !used[i-1]){
                    continue;
                }
                used[i] = true;
                list.addLast(nums[i]);
                sub(nums, index+1, list);
                used[i] = false;
                list.removeLast();
            }
        }
        return;
    }   
}

组合问题

401

数字键盘组合

leetcode 17 电话号码的组合(中等)
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

算法题目 搜索_第1张图片

示例:

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

题解:

class Solution {
    private static final String[] numMap = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; 
    private ArrayList res = new ArrayList<>();
    
    public List letterCombinations(String digits) {
        if(digits.length()==0){
            return res;
        }
        
        sub(digits, 0, "");      
        return res;
    }
    
    // 处理digits的第index个字符,0 ~ index-1 的结果存放在subStr中
    private void sub(String digits, int index, String subStr){
        if(index == digits.length()){
            res.add(subStr);
            return;
        }
        
        String s = numMap[digits.charAt(index)-'2'];
        for(int i=0; i

N个数中选出K个数的组合

leetcode 77 组合(中等)

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
 [2,4],
 [3,4],
 [2,3],
 [1,2],
 [1,3],
 [1,4],
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    
    public List> combine(int n, int k) {
        if(k>n || k==0 || n==0){
            return res;
        }
        
        LinkedList list = new LinkedList<>();
        sub(n, k, 1, 0, list);
        return res;
    }
    
    private void sub(int n, int k, int start, int index, LinkedList list){
        if(index == k){
            res.add(new LinkedList(list));
            return;
        }
        
        // 剪枝 i<=n  --->  i<=n-(k-index)+1
        for(int i=start; i<=(n-(k-index)+1); i++){
            list.addLast(i);
            sub(n, k, i+1, index+1, list);  // 注意,这里start=i+1 而不是start+1
            list.removeLast();
        }
            
        return;    
    }
}

从N个位置中选出K个位置

leetcode 401 二进制手表 (简单)

二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)。

每个 LED 代表一个 0 或 1,最低位在右侧。
算法题目 搜索_第2张图片
例如,上面的二进制手表读取 “3:25”。

给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。

案例:

输入: n = 1
返回: [“1:00”, “2:00”, “4:00”, “8:00”, “0:01”, “0:02”, “0:04”, “0:08”, “0:16”, “0:32”]

注意事项:

  • 输出的顺序没有要求。
  • 小时不会以零开头,比如 “01:00” 是不允许的,应为 “1:00”。
  • 分钟必须由两位数组成,可能会以零开头,比如 “10:2” 是无效的,应为 “10:02”。

题解:
“n个数中选k个数” 的变形问题 可以理解为从10个位置中取num个位置的组合问题

class Solution {
    private List res = new ArrayList();
    
    public List readBinaryWatch(int num) {
        int[] stat = new int[10];
        sub(num, 0, 0, stat);
        return res;        
    }
    
    // “n个数中选k个数” 的变形问题
    // 可以理解为从10个位置中取num个位置的组合问题
    // int[] stat 的前四位表示小时,后六位表示分钟
    // 通过回溯选出所有的组合
    private void sub(int num, int start, int cnt, int[] stat){
        if(cnt == num){
            int hour = stat[0]*8+stat[1]*4+stat[2]*2+stat[3];
            int minu = stat[4]*32+stat[5]*16+stat[6]*8+stat[7]*4+stat[8]*2+stat[9];
            if(hour<12 && minu<60){
                String s = String.format("%d:%02d", hour, minu);
                res.add(s);
            }
            
            return;
        }
        
        // 剪枝  i<=9 ----> i<=9-(num-cnt)+1
        for(int i=start; i<=(9-(num-cnt)+1); i++){
            stat[i] = 1;
            sub(num, i+1, cnt+1, stat);
            stat[i] = 0;
        }
        
        return;
    }
}

可重复使用元素的和为定值的组合

leetcode 39 组合总和(中等)
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    
    public List> combinationSum(int[] candidates, int target) {
        if(candidates.length == 0){
            return res;
        }
        
        LinkedList list = new LinkedList<>();
        sub(candidates, target, 0, 0, list);
        return res;
    }
    
    private void sub(int[] nums, int target, int cur, int start, LinkedList list){
        if(cur == target){
            res.add(new LinkedList(list));
            return;
        }
        
        for(int i=start; i

含有重复元素的和为定值的组合

leetcode 40 组合总和II (中等)
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
 [1, 7],
 [1, 2, 5],
 [2, 6],
 [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
 [1,2,2],
 [5]
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    private boolean[] used;
    
    public List> combinationSum2(int[] candidates, int target) {
        if(candidates.length == 0){
            return res;
        }
        
        used = new boolean[candidates.length];
        // 有重复数字 去重, 先排个序
        Arrays.sort(candidates);
        LinkedList list = new LinkedList<>();
        sub(candidates, target, 0, 0, list);
        return res;
    }
    
    private void sub(int[] arr, int target, int cur, int start, LinkedList list){
        if(cur == target){
            res.add(new LinkedList(list));
            return;
        }
        
        for(int i=start; i0 && arr[i]==arr[i-1] && !used[i-1]){
                    continue;
                }
                
                used[i] = true;
                list.addLast(arr[i]);
                sub(arr, target, cur+arr[i], i+1, list);
                list.removeLast();
                used[i] = false;
            }
        }
        
        return;
    }
}

N个数中取K个数的和为定值的组合

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

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    
    public List> combinationSum3(int k, int n) {
        if(k==0 || n==0 || n list = new LinkedList<>();
        sub(k, n, 0, 0, 1, list);
        return res;
    }
    
    private void sub(int k, int n, int index, int cur, int start, LinkedList list){
        if(index == k){
            if(cur == n){
                res.add(new LinkedList(list));
            }
            
            return;
        }
        
        // 剪枝1: i<=9 ---> i<=9-(k-index)+1
        for(int i=start; i<=(9-(k-index)+1); i++){
            if(cur+i<=n){  // 剪枝2
                
                list.addLast(i);
                sub(k, n, index+1, cur+i, i+1, list);
                list.removeLast();
            }
        }
        
        return;
    }
}

不含重复元素的子集

leetcode 78 子集(中等)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明: 解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
 [3],
 [1],
 [2],
 [1,2,3],
 [1,3],
 [2,3],
 [1,2],
 []
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    
    public List> subsets(int[] nums) {
        int n = nums.length;
        if(n==0){
            res.add(new LinkedList());
            return res;
        }
        
        LinkedList list = new LinkedList<>();
        sub(nums, 0, list);
        res.add(new LinkedList());
        return res;
        
    }
    
    private void sub(int[] nums, int start, LinkedList list){
        
        for(int i=start; i(list));
            sub(nums, i+1, list);
            list.removeLast();
        }
        
        return;
    }
}

含有重复元素的子集

leetcode 90 子集II(中等)
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明: 解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
 [2],
 [1],
 [1,2,2],
 [2,2],
 [1,2],
 []
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    private boolean[] used;
    
    public List> subsetsWithDup(int[] nums) {
        int n = nums.length;
        if(n==0){
            res.add(new LinkedList());
            return res;
        }
        
        used = new boolean[n];
        
        // 含有重复元素的去重,先排个序
        Arrays.sort(nums);
        
        LinkedList list = new LinkedList<>();     
        sub(nums, 0, list);
        res.add(new LinkedList());
        return res;
    }
    
    private void sub(int[] nums, int start, LinkedList list){
        
        for(int i=start; i0 && nums[i]==nums[i-1] && !used[i-1]){
                continue;
            }
            
            used[i] = true;
            list.addLast(nums[i]);
            res.add(new LinkedList(list));
            sub(nums, i+1, list);
            used[i] = false;
            list.removeLast();
        }
        
        return;
    }   
}

二维空间

字符矩阵中进行字符串搜索

leetcode 79 单词搜索(中等)
给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:

board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
给定 word = “ABCCED”, 返回 true.
给定 word = “SEE”, 返回 true.
给定 word = “ABCB”, 返回 false.

题解:

class Solution {
    private int m,n;
    private boolean[][] used;
    private int[][] dir = {{1,0}, {-1,0}, {0,1},{0,-1}};
    
    public boolean exist(char[][] board, String word) {
        if(board.length==0 || board[0].length==0){
            return false;
        }
        
        m = board.length;
        n = board[0].length;
        
        used = new boolean[m][n];
        
        for(int i=0; i=0 && i=0 && j

9x9 数独

leetcode 37 解数独(困难)
编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

  • 数字 1-9 在每一行只能出现一次。
  • 数字 1-9 在每一列只能出现一次。
  • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 ‘.’ 表示。
算法题目 搜索_第3张图片
一个数独。

算法题目 搜索_第4张图片
答案被标成红色。

Note:

  • 给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

题解:

class Solution {
    private boolean[][] rowUsed = new boolean[9][10];  // rowUsed[i][j]=true 表示第i行已经出现过数字j
    private boolean[][] colUsed = new boolean[9][10];
    private boolean[][] blockUsed = new boolean[9][10]; // 通过 i/3*3+j/3 将(i,j)映射到 9 个块中
    
    public void solveSudoku(char[][] board) {      
        // 初始化 rowUsed, colUsed, blockUsed
        for(int i=0; i<9; i++){
            for(int j=0; j<9; j++){
                if(board[i][j] != '.'){
                    int num = board[i][j] - '0';
                    rowUsed[i][num] = true;
                    colUsed[j][num] = true;
                    blockUsed[i/3*3+j/3][num] = true;
                }
            }
        }
        
        sub(board, 0, 0);
    }
    
    private boolean sub(char[][] board, int i, int j){
        //根据传入的位置(i,j)开始 从左到右,从上到下 寻找未填充的位置 直至填充完
        while(board[i][j]!='.'){
            if(++j>8){
                j=0;
                i++;
            }
            if(i>8){
                return true;
            }
        }
        
        // 尝试给该空位填充1~9,不成功则回退
        for(int k=1; k<=9; k++){
            if(!rowUsed[i][k] && !colUsed[j][k] && !blockUsed[i/3*3+j/3][k]){
                board[i][j] = (char)('0'+k);
                rowUsed[i][k] = true;
                colUsed[j][k] = true;
                blockUsed[i/3*3+j/3][k] = true;
                
                // 若下一个空位可以填充,则当前空位不需回退
                if(sub(board, i, j)){
                    return true;
                }else{
                    // 回退
                    board[i][j] = '.';
                    rowUsed[i][k] = false;
                    colUsed[j][k] = false;
                    blockUsed[i/3*3+j/3][k] = false;
                }
            }
        }
        
        return false;
    }
}

N皇后问题

leetcode 51 N皇后(困难)
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
算法题目 搜索_第5张图片
上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例:

输入: 4
输出:
[
  [".Q…", // 解法 1
  “…Q”,
  “Q…”,
  “…Q.”],
  ["…Q.", // 解法 2
  “Q…”,
  “…Q”,
  “.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。

题解:

class Solution {
    private boolean[] rowUsed, colUsed, diag1Used, diag2Used;
    private ArrayList> res = new ArrayList>();
    private int n;
    
    public List> solveNQueens(int n) {
        this.n = n;
        
        // 同一条主对角线上的位置坐标和i+j相等, 同一条反对角线上的位置坐标差i-j相等,为了保证为正(用作数组下标)需加上(n-1)
        // 当尝试在第i行,第j列放置皇后时,需要保证在第i行,第j列, 坐标和为i+j的主对角线,以及坐标差为i-j+n-1的反对角线上不存在皇后
        rowUsed = new boolean[n];
        colUsed = new boolean[n];
        diag1Used = new boolean[2*n-1];  //主对角线 长度为n的方阵, 有(2*n-1)条对角线
        diag2Used = new boolean[2*n-1];  //反对角线
        
        
        char[][] grid = new char[n][n];
        for(int i=0; i list = new ArrayList<>();
            for(int i=0; i

leetcode 52 N皇后II(困难)
将51号问题中返回的具体解法改为返回解法个数
解法:

class Solution {
    private boolean[] rowUsed, colUsed, diag1Used, diag2Used;
    private int res = 0;
    private int n;
    
    public int totalNQueens(int n) {
        this.n = n;
        
        // 同一条主对角线上的位置坐标和i+j相等, 同一条反对角线上的位置坐标差i-j相等,为了保证为正(用作数组下标)需加上(n-1)
        // 当尝试在第i行,第j列放置皇后时,需要保证在第i行,第j列, 坐标和为i+j的主对角线,以及坐标差为i-j+n-1的反对角线上不存在皇后
        rowUsed = new boolean[n];
        colUsed = new boolean[n];
        diag1Used = new boolean[2*n-1];  //主对角线 长度为n的方阵, 有(2*n-1)条对角线
        diag2Used = new boolean[2*n-1];  //反对角线
                
        char[][] grid = new char[n][n];
        for(int i=0; i

字符串分割

分割字符串使得其符合IP地址格式

leetcode 93 复原ip地址(中等)
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: “25525511135”
输出: [“255.255.11.135”, “255.255.111.35”]

题解:

class Solution {
    private ArrayList res = new ArrayList<>();
    
    public List restoreIpAddresses(String s) {
        
        ArrayList ip = new ArrayList<>();        
        dfs(s, 0, ip);        
        return res;       
    }
    
    private void dfs(String s, int index, ArrayList ip){
        if(index==s.length() && ip.size() == 4){            
            res.add(String.join(".", ip));
            return;
        }
        
        for(int i=1; i<=3; i++){
            if((index+i) > s.length()){
                break;
            }
            
            String next = s.substring(index, index+i);
            // 一个合法的ip段要满足范围为[0,255]且值为非零时不可有前导0
            if((next.length()>1 && next.charAt(0)!='0' && Integer.valueOf(next)<=255) || next.length() ==1){
                // ip多有4个字段
                if(ip.size()<4){
                    ip.add(next);
                    dfs(s, index+i, ip);   // 注意:这里是 index+i
                    ip.remove(ip.size()-1);
                }
            }
        }        
        return;
    } 
}

分割字符串使得其每部分都是回文串

leetcode 131 分割回文串(中等)
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: “aab”
输出:
[
 [“aa”,“b”],
 [“a”,“a”,“b”]
]

题解:

class Solution {
    private ArrayList> res = new ArrayList>();
    
    public List> partition(String s) {
        int n = s.length();
        if(n==0){
            return res;
        }
        
        LinkedList list = new LinkedList<>();
        sub(s, 0, list);
        return res;        
    }
    
    private void sub(String s, int index, LinkedList list){
        if(index == s.length()){
            res.add(new LinkedList(list));
            return;
        }
        
        for(int i=1; i<=s.length(); i++){
            if(index+i > s.length()){
                break;
            }
            
            String cur = s.substring(index, index+i);
            if(isPalindrome(cur, 0, cur.length()-1)){
                list.addLast(cur);
                sub(s, index+i, list);
                list.removeLast();
            }
        }
        
        return;
    }
    
    private boolean isPalindrome(String s, int l, int r){
        while(l

二叉树

打印二叉树的所有路径

257 二叉树的所有路径(中等)
给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

示例:

输入:
  1
  /   \
 2   3
  \
  5
输出: [“1->2->5”, “1->3”]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

题解:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List binaryTreePaths(TreeNode root) {
        
        List l = new ArrayList<>();
        
        if(root == null){            
            return l;
        }
        
        if(root.left == null && root.right == null){
            l.add(root.val+"");
            return l;
        }
        
        List ll = binaryTreePaths(root.left);
        for(String  e : ll){
            l.add(root.val+"->"+e);
        }
        
        List rl = binaryTreePaths(root.right);
        for(String e : rl){
            l.add(root.val+"->"+e);
        }
        
        return l;                
    }
}

题解2:

class Solution {
    private ArrayList res = new ArrayList<>();   
    public List binaryTreePaths(TreeNode root) {
        if(root == null){
            return res;
        }
        
        dfs(root, "");
        return res;
    }
    
    private void dfs(TreeNode node, String s){
        if(node.left == null && node.right == null){
            s+=node.val;
            res.add(s);
            s = s.substring(0, s.length()-1);
            return;
        }
        
        s = s + node.val;
        if(node.left != null){
            dfs(node.left, s+"->");      
        }
        
        if(node.right != null){
            dfs(node.right, s+"->");            
        }
        
        s = s.substring(0, s.length()-1);
        return;
    }    
}

你可能感兴趣的:(算法基础)