LEETCODE DFS中等篇

1

  1. 从前序与中序遍历序列构造二叉树
    根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

3

/
9 20
/
15 7

思路:因为前序遍历为根左右。 中序遍历为左根右。
所以前序遍历的第一个元素为重建的二叉树的根节点的值。
遍历中序遍历,直到找到和根节点值相同的位置。则此元素左边的都是根节点的左子树的元素,右边的都是根节点右子树的元素。
通过递归很容易求出解。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || preorder.length == 0 || inorder == null || inorder.length == 0 || preorder.length != inorder.length) {
            return null;
        }
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        return help(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1,map);
    }

    private TreeNode help(int[] preorder, int pStart, int pEnd, int[] inorder, int iStart, int iEnd, Map<Integer,Integer> map) {
        //递归的第一步:递归终止条件,避免死循环
        if (pStart > pEnd || iStart > iEnd) {
            return null;
        }
        //重建根节点
        TreeNode treeNode = new TreeNode(preorder[pStart]);
        int index = 0;  //index找到根节点在中序遍历的位置
        index= map.get(preorder[pStart])-iStart;
        //重建左子树
        treeNode.left = help(preorder, pStart + 1, pStart + index, inorder, iStart, iStart + index - 1,map);
        //重建右子树
        treeNode.right = help(preorder, pStart + index + 1, pEnd, inorder, iStart + index + 1, iEnd,map);
        return treeNode;

    }

}

2

  1. 朋友圈
    班上有 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。

思路:该题类似于图的遍历,每次从一个节点开始,进行深度搜索,直到走到尽头,利用一个数组来记录已经加入的小朋友,直到所以小朋友都已被遍历到,结束搜索

class Solution {
    public int findCircleNum(int[][] M) {
        int[] visited=new int[M.length];
        int cnt=0;
        for(int i=0;i<M.length;i++){
            if(visited[i]==0){
                dfs(visited,M,i);
                cnt++;
            }
        }
        return cnt;
    }

    public void dfs(int[] visited,int[][] M,int i){
        for(int j=0;j<M.length;j++){
            if(visited[j]==0&&M[i][j]==1){
                visited[i]=1;
                dfs(visited,M,j);
            }
        }
    }
}

3

  1. 01 矩阵
    给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。

两个相邻元素间的距离为 1 。

示例 1:
输入:

0 0 0
0 1 0
0 0 0
输出:

0 0 0
0 1 0
0 0 0
示例 2:
输入:

0 0 0
0 1 0
1 1 1
输出:

0 0 0
0 1 0
1 2 1

class Solution {
    public int[][] updateMatrix(int[][] matrix) {
        // 首先将所有的 0 都入队,并且将 1 的位置设置成 -1,表示该位置是 未被访问过的 1
        Queue<int[]> queue = new LinkedList<>();
        int m = matrix.length, n = matrix[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    queue.offer(new int[] {i, j});
                } else {
                    matrix[i][j] = -1;
                } 
            }
        }
        
        int[] dx = new int[] {-1, 1, 0, 0};
        int[] dy = new int[] {0, 0, -1, 1};
        while (!queue.isEmpty()) {
            int[] point = queue.poll();
            int x = point[0], y = point[1];
            for (int i = 0; i < 4; i++) {
                int newX = x + dx[i];
                int newY = y + dy[i];
                // 如果四邻域的点是 -1,表示这个点是未被访问过的 1
                // 所以这个点到 0 的距离就可以更新成 matrix[x][y] + 1。
                if (newX >= 0 && newX < m && newY >= 0 && newY < n 
                        && matrix[newX][newY] == -1) {
                    matrix[newX][newY] = matrix[x][y] + 1;
                    queue.offer(new int[] {newX, newY});
                }
            }
        }

        return matrix;
    }
}

4

  1. 扫雷游戏
    让我们一起来玩扫雷游戏!

给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。

现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:

如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。

示例 1:

输入:

[[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘M’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’]]

Click : [3,0]

输出:

[[‘B’, ‘1’, ‘E’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘M’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘1’, ‘1’, ‘B’],
[‘B’, ‘B’, ‘B’, ‘B’, ‘B’]]

class Solution {
    int[] dx={-1,-1,-1,0,1,1,1,0};
    int[] dy={-1,0,1,1,1,0,-1,-1};
    public char[][] updateBoard(char[][] board, int[] click) {
        int x=click[0];
        int y=click[1];
        if(board[x][y]=='M')
        {
            board[x][y]='X';
            return board;
        }
        dfs(board,x,y);
        return board;
    }

    public void dfs(char[][] board,int x,int y){
        if(board[x][y]!='E')
        return;
        int cnt=0;
        for(int i=0;i<8;i++){
            int a=x+dx[i];
            int b=y+dy[i];
            if(a>=0&&a<board.length&&b>=0&&b<board[0].length&&board[a][b]=='M'){
                cnt++;
            }
        }
        if(cnt==0){
            board[x][y]='B';
            for(int i=0;i<8;i++){
              int a=x+dx[i];
               int b=y+dy[i];
               if(a>=0&&a<board.length&&b>=0&&b<board[0].length&&board[a][b]=='E'){
                dfs(board,a,b);
            }
            }
        }else{
            String tmp=""+cnt;
            board[x][y]=tmp.charAt(0);
        }
    }
}

5

  1. 在每个树行中找最大值
    您需要在二叉树的每一行中找到最大的值。

示例:

输入:

      1
     / \
    3   2
   / \   \  
  5   3   9 

输出: [1, 3, 9]

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> largestValues(TreeNode root) {
        //层序遍历
        List<Integer> ans = new ArrayList<>();
        List<TreeNode> list = new ArrayList<>();
        if(root==null)
        return ans;
        int cnt=0;
        int max=Integer.MIN_VALUE;
        list.add(root);
        while(list.size()!=0){
            if(cnt==0){
                ans.add(max);
                max=Integer.MIN_VALUE;
                cnt=list.size();
            }
            TreeNode tmp=list.remove(0);
            max=Math.max(max,tmp.val);
            if(tmp.left!=null)
            list.add(tmp.left);
            if(tmp.right!=null)
            list.add(tmp.right);
            cnt--;
        }
        ans.remove(0);
        ans.add(max);
        return ans;
    }
}

6

  1. 钥匙和房间
    有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。

在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。

最初,除 0 号房间外的其余所有房间都被锁住。

你可以自由地在房间之间来回走动。

如果能进入每个房间返回 true,否则返回 false。

示例 1:

输入: [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。
示例 2:

输入:[[1,3],[3,0,1],[2],[0]]
输出:false
解释:我们不能进入 2 号房间。

思路:

利用一个数组来记录已访问过的房间,初始标记0号下标为true,然后进行dfs,如果该房间已访问过,则返回,否则将该房间标记为已访问,并且拿着该房间内有的钥匙访问能访问的房间,直至结束,最后遍历一次数组,若发现有为访问的房间,则返回false,否则返回true

class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        boolean[] visited = new boolean[rooms.size()];
        visited[0]=true;
        for(int i:rooms.get(0)){
            dfs(visited,rooms,i);
        }
        for(boolean visit:visited){
            if(!visit){
                return false;
            }
        }
        return true;
    }
    
    public void dfs(boolean[] visited,List<List<Integer>> rooms,int key){
        if(visited[key]==true){
            return;
        }
        visited[key]=true;
        for(int j:rooms.get(key)){
            dfs(visited,rooms,j);
        }
    }
}

7

8

9

10

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值