美文网首页
广度优先遍历和深度优先遍历的共同应用

广度优先遍历和深度优先遍历的共同应用

作者: dreamsfuture | 来源:发表于2018-03-30 17:50 被阅读0次

    0.1 广度优先遍历

    0.1.1 定义

    广度优先遍历是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围相邻的的区域。(处理最短路径问题

    0.1.1 代码实现方式

    for循环或while循环实现,并利用队列数据结构存储上一层遍历的结果

    0.2 深度优先遍历的定义

    0.2.1 定义及思路

    首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问。(处理最长路径问题

    0.2.2 代码实现方式

    递归函数实现,利用编译器调用的stack数据结构存储每一次递归的数据,返回时就能找到递归之前的变量状态

    0.3 看别人写的代码的快速方法

    如果不太好理解别人写的代码,则最好拿例子一步一步地跟着代码走,这样就知道代码的每一步做了什么事情了

    1. Surrounded Regions

    题目描述

    给定一个包含'X'和'O'(字母 O)的2D矩阵,检索所有被包围的区域'X'。

    通过在所围绕的区域中将所有'O's 翻转成'X's 来捕获区域。

    例如,矩阵形式如下:

    XXXX
    XOOX
    XXOX
    XOXX
    

    运行你的函数后,该矩阵应该是:

    XXXX
    XXXX
    XXXX
    XOXX
    

    代码实现(广度优先遍历)

    public class Solution {
        public void solve(char[][] board) {
            if (board.length == 0) return;
            
            int rowN = board.length;
            int colN = board[0].length;
            Queue<Point> queue = new LinkedList<Point>();
           
           //get all 'O's on the edge first
            for (int r = 0; r< rowN; r++){
                if (board[r][0] == 'O') {
                    board[r][0] = '+';
                    queue.add(new Point(r, 0));
                }
                if (board[r][colN-1] == 'O') {
                    board[r][colN-1] = '+';
                    queue.add(new Point(r, colN-1));
                }
                }
            
            for (int c = 0; c< colN; c++){
                if (board[0][c] == 'O') {
                    board[0][c] = '+';
                    queue.add(new Point(0, c));
                }
                if (board[rowN-1][c] == 'O') {
                    board[rowN-1][c] = '+';
                    queue.add(new Point(rowN-1, c));
                }
                }
            
            //BFS for the 'O's, and mark it as '+'
            while (!queue.isEmpty()){
                Point p = queue.poll();
                int row = p.x;
                int col = p.y;
                if (row - 1 >= 0 && board[row-1][col] == 'O') {board[row-1][col] = '+'; queue.add(new Point(row-1, col));}
                if (row + 1 < rowN && board[row+1][col] == 'O') {board[row+1][col] = '+'; queue.add(new Point(row+1, col));}
                if (col - 1 >= 0 && board[row][col - 1] == 'O') {board[row][col-1] = '+'; queue.add(new Point(row, col-1));}
                if (col + 1 < colN && board[row][col + 1] == 'O') {board[row][col+1] = '+'; queue.add(new Point(row, col+1));}                
            }
            
            //turn all '+' to 'O', and 'O' to 'X'
            for (int i = 0; i<rowN; i++){
                for (int j=0; j<colN; j++){
                    if (board[i][j] == 'O') board[i][j] = 'X';
                    if (board[i][j] == '+') board[i][j] = 'O';
                }
            }        
        }
    }
    

    代码实现(深度优先遍历)

    class Solution {
        public void solve(char[][] board) {
            if (board.length == 0 || board[0].length == 0) return;
            if (board.length < 3 || board[0].length < 3) return;
            int m = board.length;
            int n = board[0].length;
            for (int i = 0; i < m; i++) {
                if (board[i][0] == 'O') helper(board, i, 0);
                if (board[i][n - 1] == 'O') helper(board, i, n - 1);
            }
            for (int j = 1; j < n - 1; j++) {
                if (board[0][j] == 'O') helper(board, 0, j);
                if (board[m - 1][j] == 'O') helper(board, m - 1, j);
            }
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (board[i][j] == 'O') board[i][j] = 'X';
                    if (board[i][j] == '*') board[i][j] = 'O';
                }
            }
        }
        
        private void helper(char[][] board, int r, int c) {
            if (r < 0 || c < 0 || r > board.length - 1 || c > board[0].length - 1 || board[r][c] != 'O') return;
            board[r][c] = '*';
            helper(board, r + 1, c);
            helper(board, r - 1, c);
            helper(board, r, c + 1);
            helper(board, r, c - 1);
        }
    }
    

    2.Number of Islands

    题目描述

    给定'1's(陆地)和'0's(水)的二维网格图,计算岛的数量。一个岛被水包围,并且通过水平或垂直连接相邻的陆地而形成。你可以假设网格的所有四个边都被水包围。

    例1:

    11110
    11010
    11000
    00000
    
    答:1
    

    例2:

    11000
    11000
    00100
    00011
    
    答:3
    

    代码实现(广度优先遍历)

    public int numIslands(char[][] grid) {
        int count=0;
        for(int i=0;i<grid.length;i++)
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]=='1'){
                    bfsFill(grid,i,j);
                    count++;
                }
            }
        return count;
    }
    private void bfsFill(char[][] grid,int x, int y){
        grid[x][y]='0';
        int n = grid.length;
        int m = grid[0].length;
        LinkedList<Integer> queue = new LinkedList<Integer>();  
        int code = x*m+y;  
        queue.offer(code);  
        while(!queue.isEmpty())  
        {  
            code = queue.poll();  
            int i = code/m;  
            int j = code%m;  
            if(i>0 && grid[i-1][j]=='1')    //search upward and mark adjacent '1's as '0'.
            {  
                queue.offer((i-1)*m+j);  
                grid[i-1][j]='0';  
            }  
            if(i<n-1 && grid[i+1][j]=='1')  //down
            {  
                queue.offer((i+1)*m+j);  
                grid[i+1][j]='0';  
            }  
            if(j>0 && grid[i][j-1]=='1')  //left
            {  
                queue.offer(i*m+j-1);  
                grid[i][j-1]='0';  
            }  
            if(j<m-1 && grid[i][j+1]=='1')  //right
            {  
                queue.offer(i*m+j+1);  
                grid[i][j+1]='0';  
            }
        } 
    }
    

    代码实现(深度优先遍历)

    
    private int n;
    private int m;
    
    public int numIslands(char[][] grid) {
        int count = 0;
        n = grid.length;
        if (n == 0) return 0;
        m = grid[0].length;
        for (int i = 0; i < n; i++){
            for (int j = 0; j < m; j++)
                if (grid[i][j] == '1') {
                    DFSMarking(grid, i, j);
                    ++count;
                }
        }    
        return count;
    }
    
    private void DFSMarking(char[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= n || j >= m || grid[i][j] != '1') return;
        grid[i][j] = '0';
        DFSMarking(grid, i + 1, j);
        DFSMarking(grid, i - 1, j);
        DFSMarking(grid, i, j + 1);
        DFSMarking(grid, i, j - 1);
    }
    

    3.Minesweeper

    给你一个代表游戏板的2D字符矩阵。'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']]

    点击:[3,0]

    输出:
    [['B','1','E','1','B'],
    ['B','1','M','1','B'],
    ['B','1','1','1','B' ],
    ['B','B','B','B','B']

    说明:

    例2:

    输入:
    [['B','1','E','1','B'],
    ['B','1','M','1','B'],
    ['B','1','1','1','B' ],
    ['B','B','B','B','B']

    点击:[1,2]

    输出:
    [['B','1','E','1','B'],
    ['B','1','X','1','B' ],
    ['B','1','1','1','B' ],
    ['B','B','B','B','B']

    说明:

    思路分析

    这是一个典型的Search问题,无论是使用DFS或BFS。搜索规则:

    1.如果点击地雷(' M'),将其标记为' X',停止进一步搜索。
    2.如果点击空单元格(' E'),取决于周围的矿井数量:
    2.1有周围的地雷,用周围地雷的数量标记,停止进一步的搜索。
    2.2没有周围的地雷,将其标记为“ B',继续搜索它的8个邻居。

    代码实现(广度优先遍历)

    public class Solution {
        public char[][] updateBoard(char[][] board, int[] click) {
            int m = board.length, n = board[0].length;
            Queue<int[]> queue = new LinkedList<>();
            queue.add(click);
            
            while (!queue.isEmpty()) {
                int[] cell = queue.poll();
                int row = cell[0], col = cell[1];
                
                if (board[row][col] == 'M') { // Mine
                    board[row][col] = 'X';
                }
                else { // Empty
                    // Get number of mines first.
                    int count = 0;
                    // 这里的两层for循环目的是为了检索这个点的上下左右和四个角,其实这个与前面的四个递归本质是一样的,只不过这里要8个,所以用for循环更好,而且本来用递归效率就不高。
                    for (int i = -1; i < 2; i++) {
                        for (int j = -1; j < 2; j++) {
                            if (i == 0 && j == 0) continue;
                            int r = row + i, c = col + j;
                            if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
                            if (board[r][c] == 'M' || board[r][c] == 'X') count++;
                        }
                    }
                    
                    if (count > 0) { // If it is not a 'B', stop further BFS.
                        board[row][col] = (char)(count + '0');
                    }
                    else { // Continue BFS to adjacent cells.
                        board[row][col] = 'B';
                        for (int i = -1; i < 2; i++) {
                            for (int j = -1; j < 2; j++) {
                                if (i == 0 && j == 0) continue;
                                int r = row + i, c = col + j;
                                if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
                                if (board[r][c] == 'E') {
                                    queue.add(new int[] {r, c});
                                    board[r][c] = 'B'; // Avoid to be added again.
                                }
                            }
                        }
                    }
                }
            }
            
            return board;
        }
    }
    

    代码实现(深度优先遍历)

    public class Solution {
        public char[][] updateBoard(char[][] board, int[] click) {
            int m = board.length, n = board[0].length;
            int row = click[0], col = click[1];
            
            if (board[row][col] == 'M') { // Mine
                board[row][col] = 'X';
            }
            else { // Empty
                // Get number of mines first.
                int count = 0;
                for (int i = -1; i < 2; i++) {
                    for (int j = -1; j < 2; j++) {
                        if (i == 0 && j == 0) continue;
                        int r = row + i, c = col + j;
                        if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
                        if (board[r][c] == 'M' || board[r][c] == 'X') count++;
                    }
                }
                
                if (count > 0) { // If it is not a 'B', stop further DFS.
                    board[row][col] = (char)(count + '0');
                }
                else { // Continue DFS to adjacent cells.
                    board[row][col] = 'B';
                    for (int i = -1; i < 2; i++) {
                        for (int j = -1; j < 2; j++) {
                            if (i == 0 && j == 0) continue;
                            int r = row + i, c = col + j;
                            if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
                            if (board[r][c] == 'E') updateBoard(board, new int[] {r, c}); //递归实现数据传递和深度优先遍历,这是唯一与广度优先遍历的区别
                        }
                    }
                }
            }
            
            return board;
        }
    }
    

    4. 0&1 Matrix

    问题描述

    给定一个由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
    

    注意:
    给定矩阵的元素数量不会超过10,000。
    给定矩阵中至少有一个0。
    细胞只在四个方向上相邻:向上,向下,向左和向右。

    代码实现(广度优先遍历)

    public class Solution {
        public int[][] updateMatrix(int[][] matrix) {
            int m = matrix.length;
            int n = matrix[0].length;
            
            Queue<int[]> queue = new LinkedList<>();
            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] = Integer.MAX_VALUE; //非0的矩阵值设为整型最大值
                    }
                }
            }
            //此矩阵的目的就是实现寻找某个点的上下左右四个相邻点
            int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
            
            while (!queue.isEmpty()) {
                int[] cell = queue.poll();
                for (int[] d : dirs) {
                    int r = cell[0] + d[0];
                    int c = cell[1] + d[1];
                    if (r < 0 || r >= m || c < 0 || c >= n || 
                        matrix[r][c] <= matrix[cell[0]][cell[1]] + 1) continue;  //如果原来是1,则矩阵的值为整型最大值,没有谁会大于本点,如果本点的上下左右四个点都小于等于本点,表示本点的周围没有1
                    queue.add(new int[] {r, c});
                    matrix[r][c] = matrix[cell[0]][cell[1]] + 1;  //如果遇到某个点的左右上下有大于本点的,表示遇到了本点相邻为1的情况,此时整型最大值加1就是1,因为溢出了
                }
            }
            
            return matrix;
        }
    }
    

    代码实现(深度优先遍历)

    public class Solution {
        public int[][] updateMatrix(int[][] matrix) {
            if(matrix.length==0) return matrix;
            
            for(int i = 0; i<matrix.length; i++)
                for(int j = 0; j<matrix[0].length; j++)
                    if(matrix[i][j]==1&&!hasNeiberZero(i, j,matrix)) 
                        matrix[i][j] = matrix.length+matrix[0].length+1;
            
            for(int i = 0; i<matrix.length; i++)
                for(int j = 0; j<matrix[0].length; j++)
                    if(matrix[i][j]==1)
                        dfs(matrix, i, j, -1);
            
            return matrix;
        }
        private void dfs(int[][] matrix, int x, int y, int val){
            if(x<0||y<0||y>=matrix[0].length||x>=matrix.length||matrix[x][y]<=val)
                return;
            
            if(val>0) matrix[x][y] = val;
            
            dfs(matrix, x+1, y, matrix[x][y]+1);
            dfs(matrix, x-1, y, matrix[x][y]+1);
            dfs(matrix, x, y+1, matrix[x][y]+1);
            dfs(matrix, x, y-1, matrix[x][y]+1);
            
        }
        private boolean hasNeiberZero(int x, int y, int[][] matrix){
            if(x>0&&matrix[x-1][y]==0) return true;
            if(x<matrix.length-1&&matrix[x+1][y]==0) return true;
            if(y>0&&matrix[x][y-1]==0) return true;
            if(y<matrix[0].length-1&&matrix[x][y+1]==0) return true;
            
            return false;
        }
    }
    

    5.Minimum Depth of Binary Tree

    题目描述

    给定一个二叉树,找到它的最小深度。

    最小深度是沿着从根节点到最近叶节点的最短路径的节点数量。

    代码实现(广度优先遍历)

    public int minDepth(TreeNode root) {
        if(root == null) return 0;
        int depth = 1;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        while(!q.isEmpty()){
            int size = q.size();
            // for each level
            for(int i=0;i<size;i++){
                TreeNode node = q.poll();
                if(node.left == null && node.right == null){
                    return depth;
                }
                if(node.left != null){
                    q.offer(node.left);
                }
                if(node.right != null){
                    q.offer(node.right);
                }
            }
            depth++;
        }
        return depth;
    }
    

    代码实现(深度优先遍历)

    public class Solution {
        public int minDepth(TreeNode root) {
            if(root == null) return 0;
            int left = minDepth(root.left);
            int right = minDepth(root.right);
            return (left == 0 || right == 0) ? left + right + 1: Math.min(left,right) + 1;
           
        }
    }
    ###复杂度
    BFS和DFS的复杂度不一样,BFS要好些,所以虽然都可以完成功能,但是因为算法效率不一样,最后复杂度也不一样。
    
    

    6.Course Schedule II

    问题描述

    总共有n个课程需要学习,标记为0 至 n - 1。

    有些课程可能有先决条件,例如,如果选择课程0,则必须先参加课程1,该课程表示为一对: [0,1]

    输入课程总数和先修课程的条件对列表,请返回应完成所有课程的顺序。

    可能有多个正确的命令,你只需要返回其中的一个。如果不可能完成所有课程,则返回一个空数组。

    例如:

    2, [[1,0]]
    

    总共有2门课程可供选择。要参加课程1,你应该已经完成​​课程0.所以正确的课程顺序是[0,1]

    4, [[1,0],[2,0],[3,1],[3,2]]
    

    总共有4门课程可供选择。要参加课程3,你应该已经完成​​了课程1和2.在完成课程0之后,应该采用课程1和2。因此,一个正确的课程顺序是[0,1,2,3]。另一个正确的顺序是[0,2,1,3]

    代码实现(广度优先遍历)

    1.邻接表图结构

    private int[] solveByBFS(int[] incLinkCounts, List<List<Integer>> adjs){
        int[] order = new int[incLinkCounts.length];
        Queue<Integer> toVisit = new ArrayDeque<>();
        for (int i = 0; i < incLinkCounts.length; i++) {
            if (incLinkCounts[i] == 0) toVisit.offer(i);
        }
        int visited = 0;
        while (!toVisit.isEmpty()) {
            int from = toVisit.poll();
            order[visited++] = from;
            for (int to : adjs.get(from)) {
                incLinkCounts[to]--;
                if (incLinkCounts[to] == 0) toVisit.offer(to);
            }
        }
        return visited == incLinkCounts.length ? order : new int[0]; 
    }
    

    2.邻接矩阵图结构

    public int[] findOrder(int numCourses, int[][] prerequisites) { 
        if (numCourses == 0) return null; //如果修的课程为0,则直接返回-1
        // Convert graph presentation from edges to indegree of adjacent list.
        int indegree[] = new int[numCourses], order[] = new int[numCourses], index = 0;
        //order数组表示输出修的课程的顺序,indegree数组表示每一门课程前面需要修的其他课程的总数
        for (int i = 0; i < prerequisites.length; i++) // Indegree - how many prerequisites are needed.
            indegree[prerequisites[i][0]]++;    //计算每门课需要需要先修的课数,即前面有几门课需要先修
    
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < numCourses; i++) 
            if (indegree[i] == 0) {  //把不需要先修的课程放入到队列中,没有顺序,仅仅根据课程编号的顺序执行
                // Add the course to the order because it has no prerequisites.
                order[index++] = i;
                queue.offer(i);
            }
    
        // How many courses don't need prerequisites. 
        while (!queue.isEmpty()) {
            int prerequisite = queue.poll(); // Already finished this prerequisite course.
            for (int i = 0; i < prerequisites.length; i++)  {
                if (prerequisites[i][1] == prerequisite) {
                    indegree[prerequisites[i][0]]--; //取出一个先修课程,然后遍历找出在此课程之前需要修的数量,减去1
                    if (indegree[prerequisites[i][0]] == 0) { //如果先修课程为0了,此时加入到队列中,作为其他的课程的先修课程
                        // If indegree is zero, then add the course to the order.
                        order[index++] = prerequisites[i][0]; //更新先修课程的输出顺序
                        queue.offer(prerequisites[i][0]); 
                    }
                } 
            }
        }
    
        return (index == numCourses) ? order : new int[0];
    }
    

    代码实现(深度优先遍历)

    private int[] solveByDFS(List<List<Integer>> adjs) {
        BitSet hasCycle = new BitSet(1);
        BitSet visited = new BitSet(adjs.size());
        BitSet onStack = new BitSet(adjs.size());
        Deque<Integer> order = new ArrayDeque<>();
        for (int i = adjs.size() - 1; i >= 0; i--) {
            if (visited.get(i) == false && hasOrder(i, adjs, visited, onStack, order) == false) return new int[0];
        }
        int[] orderArray = new int[adjs.size()];
        for (int i = 0; !order.isEmpty(); i++) orderArray[i] = order.pop();
        return orderArray;
    }
    
    private boolean hasOrder(int from, List<List<Integer>> adjs, BitSet visited, BitSet onStack, Deque<Integer> order) {
        visited.set(from);
        onStack.set(from);
        for (int to : adjs.get(from)) {
            if (visited.get(to) == false) {
                if (hasOrder(to, adjs, visited, onStack, order) == false) return false;
            } else if (onStack.get(to) == true) {
                return false;
            }
        }
        onStack.clear(from);
        order.push(from);
        return true;
    }
    

    7.Cheapest Flights Within K Stops

    题目描述

    有m个航班连接n个城市。航班从城市u飞抵城市v,机票价格为w。

    输入所有的城市和机票价格,出发城市为src和目的地为dst,任务是找到从城市src到dst中间停留最多k站的最便宜的机票价格。如果没有这样的路线,输出-1。

    Example 1:
    Input: 
    n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
    src = 0, dst = 2, k = 1
    Output: 200
    Explanation: 
    The graph looks like this:
    

    The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture.

    题目分析

    本题与上一题本质是一样的,就是从一个地方走到另外一个地方,之间有对应关系。直接表现为广度优先遍历或深度优先遍历的情况

    代码实现(BFS)

    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
            //使用map数据结构的目的是为了集合可能的多个从同一个城市(顶点)出发的数据,同时为了避免混淆同一个城市的数据,用目标城市来作为value中的map的key,price值作为目标城市的key,利用这种数据结构可以实现唯一性
            Map<Integer, Map<Integer, Integer>> prices = new HashMap<>();
            //创建图结构,利用map实现顶点f[0](城市名)到目标城市f[1]与价格f[2]的映射
            for (int[] f : flights) {
                if (!prices.containsKey(f[0])) prices.put(f[0], new HashMap<>());
                prices.get(f[0]).put(f[1], f[2]);
            }
            //优先队列实现那个价格更小,然后放到前面,这样就可以实现找到结果就可以终止程序
            Queue<int[]> pq = new PriorityQueue<>((a, b) -> (Integer.compare(a[0], b[0])));
            pq.add(new int[] {0, src, k + 1}); //加入起始城市
            while (!pq.isEmpty()) {
                int[] top = pq.remove();
                int price = top[0];
                int city = top[1];
                int stops = top[2];
                if (city == dst) return price;
                if (stops > 0) {
                    Map<Integer, Integer> adj = prices.getOrDefault(city, new HashMap<>());
                    for (int a : adj.keySet()) {//取出所有与city这个键对应的value,然后依次价格相加,起始城市更新为下一个城市,即当前city所邻接的城市,同时stops-1,表示当前city的相邻城市停了
                        pq.add(new int[] {price + adj.get(a), a, stops - 1});
                    }
                }
            }
            return -1;
        }
    

    8. Employee Importance

    题目描述

    给你一个员工信息的数据结构,包括员工的唯一ID,他的重要性值和他的直属下属的ID。

    例如,员工1是员工2的领导,员工2是员工3的领导。他们分别具有15,10和5的重要性值。然后,员工1具有[1,15,[2]]的数据结构,并且员工2具有[2,10,[3]],员工3具有[3,5,[]]。请注意,虽然员工3也是员工1的下属,但这种关系并不直接。

    现在给出公司的员工信息和员工ID,您需要返回此员工及其所有下属的总重要性值。

    例1:
    输入: [[1,5,[2,3]],[2,3,[]],[3,3,[]]],1
    输出: 11
    

    说明
    员工1的重要性值为5,他有两个直接下属:员工2和员工3,他们都具有重要性值3,因此员工1的总重要值为5 + 3 + 3 = 11。
    注意
    一名员工最多只有一名直接领导,可能有几名下属。
    员工人数不得超过2000人。

    员工数据结构定义

    public class Employee {
        // It's the unique id of each node;
        // unique id of this employee
        public int id;
        // the importance value of this employee
        public int importance;
        // the id of direct subordinates
        public List<Integer> subordinates;
    }
    

    代码实现(广度优先遍历)

    class Solution {
        public int getImportance(List<Employee> employees, int id) {
            int total = 0;
            Map<Integer, Employee> map = new HashMap<>();
            for (Employee employee : employees) {
                map.put(employee.id, employee);
            }
            Queue<Employee> queue = new LinkedList<>();
            queue.offer(map.get(id));
            while (!queue.isEmpty()) {
                Employee current = queue.poll();
                total += current.importance;
                //如果一个顶点有很多个对应的相邻节点,同时这些节点又可能没有关系,就需要利用for循环一次性全部加载进来
                for (int subordinate : current.subordinates) {
                    queue.offer(map.get(subordinate));
                }
            }
            return total;
        }
    }
    

    代码实现(深度优先遍历)

    class Solution {
        public int getImportance(List<Employee> employees, int id) {
            Map<Integer, Employee> map = new HashMap<>();
            for (Employee employee : employees) {
                map.put(employee.id, employee);
            }
            return getImportanceHelper(map, id);
        }
        
        private int getImportanceHelper(Map<Integer, Employee> map, int rootId) {
            Employee root = map.get(rootId);
            int total = root.importance;
            for (int subordinate : root.subordinates) {
                total += getImportanceHelper(map, subordinate);
            }
            return total;
        }
    }
    

    9.Word Ladder

    问题描述

    给定两个单词(beginWord和endWord)和一个字典的单词列表,找到从beginWord到endWord的最短转换序列的长度,例如:

    一次只能更改一个字母。
    每个转换的单词必须存在于单词列表中。需要注意的是beginWord是可以变换的词。
    例如,

    例如:
    beginWord = "hit"
    endWord = "cog"
    wordList =["hot","dot","dog","lot","log","cog"]
    作为一个最短的转换"hit" -> "hot" -> "dot" -> "dog" -> "cog",
    返回它的长度5。
    

    注意
    如果没有这样的转换序列,则返回0。
    所有单词具有相同的长度。
    所有单词只包含小写字母字符。
    您可能会认为单词列表中没有重复项。
    你可能会认为beginWord和endWord是非空的并且不一样。

    问题分析

    让我们看看问题陈述中的例子。

    start = "hit"

    end = "cog"

    dict = ["hot", "dot", "dog", "lot", "log"]

    由于每次只能更改一个字母,如果我们从"hit"开始,我们只能更改当前单词只有一个字母不相等的单词,例如"hot"。以图论的方式来说,我们可以说这"hot"是一个邻居"hit"。

    这个想法很简单,先来看看start的邻居,然后访问其邻居的未访问邻居......嗯,这只是典型的BFS结构。

    为了简化问题,我们把end单词插入到dict。一旦我们在BFS 遇到end单词,我们找到了答案。我们为变换的当前距离保留一个变量dist,并在完成一轮BFS搜索(请注意,它应该符合问题描述中距离的定义)后更新它dist++。此外,为避免多次访问某个单词,我们会在访问该单词dict后将其删除。

    代码实现

    class Solution {
    public:
        int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) {
            wordDict.insert(endWord); //把目标单词插入到字典中
            queue<string> toVisit; //队列保存单词的邻居,即将要访问的内容
            addNextWords(beginWord, wordDict, toVisit);  
            int dist = 2; //记录单词修改的次数
            while (!toVisit.empty()) {
                int num = toVisit.size();
                for (int i = 0; i < num; i++) { //一次性地把上一个单词的所有相邻单词都提取出来进行计算
                    string word = toVisit.front();
                    toVisit.pop();
                    if (word == endWord) return dist;
                    addNextWords(word, wordDict, toVisit); //没找到目标单词,则继续寻找下去
                }
                dist++;
            }
        }
    private:
        //本函数的目的是选择当前单词的一个索引位置并依次改为26个字母,也即修改单词,然后找到此单词的邻居,只能一个一个地去试一试。
        void addNextWords(string word, unordered_set<string>& wordDict, queue<string>& toVisit) {
            wordDict.erase(word); //删除访问过的单词
            for (int p = 0; p < (int)word.length(); p++) { //单词的各个位置的字母进行修改
                char letter = word[p];
                for (int k = 0; k < 26; k++) { //修改为26个字母
                    word[p] = 'a' + k;
                    if (wordDict.find(word) != wordDict.end()) { //如果修改后的单词在字典中存在,则输入到toVisit队列中,表示修改有效
                        toVisit.push(word);
                        wordDict.erase(word);//放入到toVisit中的单词即为将来要访问的单词,为了避免重复访问,所以删除此单词
                    }
                }
                word[p] = letter;  //记得把修改后的内容恢复,否则一次就修改了好多几个字母
            } 
        } 
    };
    

    参考文献:
    [1] Leetcode

    [2] 字符串每一次变化一个,从起始单词到目标单词的最短路径,就是一个BFS的计算过程,而且BFS的效率会更高,因为我们知道计算谁最短。
    Dijkstra算法利用的是BFS原理实现的,但是通过斐波那契堆实现的Dijkstra算法的时间复杂度为O(n^2),空间复杂度为O(m+nlogn),其中n为顶点个数,m为边数


    Dijkstras_progress_animation

    相关文章

      网友评论

          本文标题:广度优先遍历和深度优先遍历的共同应用

          本文链接:https://www.haomeiwen.com/subject/vkvicftx.html