5.算法练习-搜索算法

深度优先搜索

695. 岛屿的最大面积(easy)

题目:给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
输入: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
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
示例 2:

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

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j]01

分析
这个题目用深度递归的思路比较简单。首先他给了我一个二维数组,那么我可以去遍历,以当前的格子为基准,向四处发散,如果遇到了边缘处或者值为0的地方,返回0。走过的地方,赋值为0。采用递归的思想,当前的面积加上后续路径的面积是总面积,然后继续递归下去。最后从所有的结果里选出最大的那个,就是最大值了。
一个比较关键的点就是:先判断边界返回,再处理方向(或是在方向里继续递归)

var maxAreaOfIsland = function (grid) {
    // 采用深度优先看一下
    let ans = 0;
    let row = grid.length;
    let col = grid[0].length;
    let dx = [-1, 1, 0, 0];
    let dy = [0, 0, -1, 1];
    function dfs(i, j) {
        // 走不通或者是边界不符合条件
        if (i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == 0) return 0;
        // 走过后赋值0
        grid[i][j] = 0;
        let len = 1;
        for (let m = 0; m < dx.length; m++) {
            len += dfs(i + dx[m], j + dy[m])
        }
        return len
    }
    for (let i = 0; i < row; i++) {
        for (let j = 0; j < col; j++) {
            ans = Math.max(ans, dfs(i, j))
        }
    }
    return ans;
};

547. 省份数量(medium)

题目:有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。
示例 1:

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2


输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

分析
这题需要思考,那么多的城市,需要怎么连成一片。在上题里面,重点要记录里面的面积。而这里只用把城市连接起来,不用进行状态的叠加。连起来的城市之间的值赋值0,而且,a-b和b-a是一样的,所以这里走一半表格就可以了。
**关键:**这里不是向四处散发了,以行或者列去查找关联的城市。原因是同一行或同一列的城市之间才会有连接。

var findCircleNum = function (isConnected) {
    // 继续采用深度优先遍历
    let row = isConnected.length;
    let col = isConnected[0].length;
    function dfs(i,j){
        if(isConnected[i][j]==0) return 0;
        isConnected[i][j]=0;
        let len=1;
        for(let m=0;m<=j;m++){
            dfs(m,j)
        }
        for(let m=i;m<col;m++){
            dfs(i,m)
        }
        return len;

    }
    let count=0
    for(let i=0;i<row;i++){
        for(let j=i;j<col;j++){
            count += dfs(i,j)
        }
    }
    return count;
};

417. 太平洋大西洋水流问题(medium)

题目:有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度 。

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋 
示例 1输入: heights = [[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]]
示例 2输入: heights = [[2,1],[1,2]]
输出: [[0,0],[0,1],[1,0],[1,1]]

分析
这题正着想,比较难。逆推着写,逻辑会简单很多。题目像要求得陆地的高地。也就是说从太平洋能流到的地方,从太平洋也能流过去。重叠的地方是既能流到太平洋,也能流到大西洋了。准备两个二维数组记录,有数据重叠的就是我们需要的地方。

var pacificAtlantic = function(heights) {
    // 深度优先
    let row=heights.length;
    let col=heights[0].length;
    let Pacific=new Array(row).fill(0).map(()=>new Array(col).fill(0));
    let Atlantic=new Array(row).fill(0).map(()=>new Array(col).fill(0));
    let ans=[];
    let dx = [1,-1,0,0];let dy=[0,0,1,-1]
    function dfs(x,y,arr){
        if(arr[x][y]==1) return;
        arr[x][y]=1;
        for(let i=0;i<dx.length;i++){
            let [x2,y2] = [x+dx[i],y+dy[i]]
            if(0<=x2 && x2<row && 0<=y2 && y2<col && heights[x][y]<=heights[x2][y2]){
                dfs(x2,y2,arr)
            }
        }
    }
    // 上边流动
    for(let i=0;i<col;i++){
        dfs(0,i,Pacific)
    }
    // 左边框流动
    for(let i=0;i<row;i++){
        dfs(i,0,Pacific)
    }
    // 右边流动
    for(let i=0;i<row;i++){
        dfs(i,col-1,Atlantic)
    }
    // 下边流动
    for(let i=0;i<col;i++){
        dfs(row-1,i,Atlantic)
    }
    for(let m=0;m<row;m++){
        for(let n=0;n<col;n++){
            if(Pacific[m][n]==Atlantic[m][n] && Pacific[m][n]==1){
                ans.push([m,n])
            }
        }
    }
    return ans;
};

回溯法

46. 全排列(medium)

题目:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:

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

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]
提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

分析
回溯法用的时候,会搭配深度优先或者广度优先的方法来写,但是不同的是,他需要记录每一步的记录,最后一条条记录里,就有可能会有我们需要的答案。前面几个题注重最后的搜索结果,每一步走的什么,并不关注。回溯法里有一个重要的变量path,用Array.from(),进行传递,可以不影响到原来的数组,并将数组传递下去。

var permute = function (nums) {
    let ans = [];
    let path = [];
    allsort(nums, nums.length, [])
    function allsort(nums, k, used) {
        for (let i = 0; i < k; i++) {
            if (path.length == k) {
                ans.push(Array.from(path))
                return
            }
            if (used[i]) continue;
            used[i] = true;
            path.push(nums[i]);
            allsort(nums, k, used)
            path.pop();
            used[i] = false;
        }
    }
    return ans;
};

77. 组合(medium)

题目:给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。
示例 1:

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

输入:n = 1, k = 1
输出:[[1]]
 

提示:

1 <= n <= 20
1 <= k <= n

分析
从N个数中选择K个。首先找到循环的切入点。因为顺序是任意的,所以我们在选的时候就不要考虑乱序的问题了,从前向后一个一个找,不要返回去找参数,避免重复。那么第一个数的范围就是在[1,n-k+1],后面一定要留K-1个可选择的数,否则这条选择路径就是不可行的,后面再依次找下去,比如第一个的位置是1,那么第2个数的位置范围就是[2,n-k+2],依次类推。

var combine = function(n, k) {
    let ans=[];
    let path=[];
    let arr=[];
    for(let i=1;i<=n;i++){
        arr.push(i)
    }
    back(arr,0,n)
    function back(arr,start,end){
        if(end-start<k-path.length) return
        if(path.length==k){
            ans.push(Array.from(path));
            return;
        }
        for(let i=start;i<end;i++){
            path.push(arr[i]);
            back(arr,i+1,end);
            path.pop();
        }
    }
    return ans;
};

79. 单词搜索(medium)

题目:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

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

在这里插入图片描述

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
在这里插入图片描述
分析
采用回溯法,还是深度优先递归的思路,从左上角开始走起,如果字符和判断的字符一样,那么就标记这里已经走过了,然后继续向四个方向走去递归,否则返回,标记为未走过。如果这条路后面的路径走不通,那么已走过的标记撤回,如果不撤回标记,走通后,返回true。

var exist = function (board, word) {
    let row = board.length;
    let col = board[0].length;
    let dx = [1, -1, 0, 0];
    let dy = [0, 0, 1, -1];
    function back(x, y, index) {
        if (index == word.length) {
            return true;
        }
        if (x < 0 || x >= row || y < 0 || y >= col || board[x][y] == 'used') {
            return false;
        }
        if (board[x][y] == word[index]) {
            let temp = board[x][y]
            board[x][y] = 'used';
            let res = false
            for (let i = 0; i < dx.length; i++) {
                res = res || back(x + dx[i], y + dy[i], index + 1);
            }
            if (!res) {
                board[x][y] = temp;
            }else{
                return true;
            }
        }
        return false;
    }
    for (let i = 0; i < row; i++) {
        for (let j = 0; j < col; j++) {
            let res = back(i, j, 0);
            if (res) return res;
        }
    }
    return false
};

51. N 皇后(hard)

题目:按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

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

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

示例一
示例 2:

输入:n = 1
输出:[[“Q”]]

分析
这题呢,还是用深度递归,但是和前面的题有点区别。1.首先,在这个棋盘上再去遍历每一个位置作为第一个棋是没有必要的,因为他又不分顺序。所以我们只用遍历第一行,让第一行的每个位置都作为起始位置就行了,然后再一行一行接着向下找。2.判断是否可下,不可同行,不可同列,斜着同行或者同列也都不行。可以借助外在的变量去记录,首先同行可以排除,因为同行是不可能的,之后用数组记录是否同列即可。左斜与右斜也不可以,此时借助map去记录,左上到右下的每一条斜线,同一条线上x-y的差都相同的,可以用差值作为K,记录这条线上是否有棋子。从右上到左下,同一条线上x+y的只都是相同的,同理可记录。
当最后一行可以确定一个棋子的时候,说明这个走法是可行的。
走完之后一定要回溯,因为还要走其他的路径

var solveNQueens = function (n) {
    // 每行或者每列,至少有一个棋子
    let ans = new Array(n)
    let result = []
    // 斜角
    let s1 = new Map()
    let s2 = new Map()
    let col = new Array(n).fill(0)
    for (let i = 0; i < n; i++) {
        ans[i] = new Array(n).fill('.')
    }
    function back(ans, col, row, index) {
        if (row == n-1) {
            ans[row][index] = 'Q'
            let res = []
            let temp=''
            ans.forEach(item=>{
                for(let i=0;i<item.length;i++){
                    temp += item[i]
                }
                res.push(temp)
                temp=''
            })
            result.push(res)
            ans[row][index] = '.'
            return
        }
        // 占位
        let temp1 = row - index;
        let temp2 = row + index;
        ans[row][index] = 'Q'
        col[index] = 1;
        s1.set(temp1, 1)
        s2.set(temp2, 1)
        // 看下一步能不能走
        for (let i = 0; i < n; i++) {
            if (!col[i]) {//不同列
                let temp1 = row+1 - i;
                let temp2 = row+1 + i;
                if (!s1.has(temp1) && !s2.has(temp2)) {
                    back(ans, col, row + 1, i)
                }

            }
        }
        ans[row][index] = '.'
        col[index] = 0;
        s1.delete(temp1)
        s2.delete(temp2)
    }
    for (let i = 0; i <n; i++) {
        back(ans, col, 0, i)
    }
    return result
};

广度优先搜索

934. 最短的桥(medium)

题目:给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。

岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。

你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。

返回必须翻转的 0 的最小数目。
示例 1:

输入:grid = [[0,1],[1,0]]
输出:1
示例 2:

输入:grid = [[0,1,0],[0,0,0],[0,0,1]]
输出:2
示例 3:

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

分析
广度优先搜索和深度优先搜索不太一样,深度优先搜索是找到一个循环条件,不停地向下去分析,到符合数组或某个条件终止。而广度优先搜索则是一层数据一层数据地判断,往往伴随着while嵌套for循环,并且在for循环之前重新赋值for循环的赋值,也就是代表新的层数。
本题可以通过不断扩展岛的面积来解决。在岛的边缘再加上一圈,从一个岛上上开始扩散。一圈一圈地加,直到最后出现重叠到另一个岛说明有了最小的路径,而翻转数就是层数。

var shortestBridge = function (grid) {
    let n = grid.length;
    let queue = [];
    let dx = [1, -1, 0, 0];
    let dy = [0, 0, -1, 1];
    let isLand = [];
    let step = 0;
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            if (grid[i][j] == 1) {
                // 有地
                queue.push([i, j]);
                grid[i][j] = -1;
                while (queue.length) {
                    let [x, y] = queue.shift();
                    // 把取出的岛存起来
                    isLand.push([x, y])
                    for (let s = 0; s < 4; s++) {
                        let nx = x + dx[s];
                        let ny = y + dy[s];
                        if (0 <= nx && nx < n && 0 <= ny && ny < n) {
                            if (grid[nx][ny] == 1) {
                                queue.push([nx, ny]);
                                grid[nx][ny] = -1;
                            }
                        }
                    }
                }
                for (let land of isLand) {
                    queue.push(land)
                }
                while (queue.length>0) {
                    
                    let length = queue.length;
                    for (let i = 0; i < length; i++) {
                        let [x, y] = queue.shift();
                       
                        for (let s = 0; s < 4; s++) {
                            let nx = x + dx[s];
                            let ny = y + dy[s];
                            if (0 <= nx && nx < n && 0 <= ny && ny < n) {
                                if (grid[nx][ny] == 0) {
                                    queue.push([nx, ny]);
                                    grid[nx][ny] = -1;
                                } else if (grid[nx][ny] == 1) {
                                    return step;
                                }
                            }

                        }

                    }
                    step++;
                }
            }
        }
    }
    return 0
};

练习

130. 被围绕的区域(medium)

题目:给你一个 m x n 的矩阵 board ,由若干字符 'X''O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。
示例 1:


输入:board = [["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'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:

输入:board = [["X"]]
输出:[["X"]]

分析
这题和太平洋那题很像,从里向外找O很复杂。但是从边界处找到O,并且想里面延伸就相对简单,把从边界处扩展到的O标记一下,那么没有被标记到的O就是会被替换成为X。用深度优先递归写。

var solve = function (board) {
    let m = board.length;
    let n = board[0].length;

    function dfs(row, col) {
        if (row < 0 || row >= m || col < 0 || col >= n) return
        if (board[row][col] == 'O') {
            board[row][col] = 'NX';
            dfs(row + 1, col);
            dfs(row - 1, col);
            dfs(row, col - 1);
            dfs(row, col + 1);
        }

    }
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (i == 0 || j == 0 || i == m - 1 || j == n - 1) {
                dfs(i, j)
            }
        }
    }
    for (let t = 0; t < m; t++) {
        for (let k = 0; k < n; k++) {
            if (board[t][k] == "NX") {
                board[t][k] = 'O'
            } else if (board[t][k] == "O") {
                board[t][k] = 'X'
            }
        }
    }
}

257. 二叉树的所有路径(easy)

题目:给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

在这里插入图片描述
示例 2:

输入:root = [1]
输出:[“1”]
分析
这个题目示例有一个迷惑项,就是它让你以为root是个数组,其实root是根节点,直接拿来用。利用回溯+深度优先搜索左右子树搜出来并记录。如果左右子树都为空,说明这是一条路径。

var binaryTreePaths = function (root) {
    let ans = []
    function search(root, path) {
        // return root.val.toString()
        if (root) {
            path.push(root.val)
            if (root.left) {
                search(root.left, Array.from(path))
            }
            if (root.right) {
                search(root.right, Array.from(path))
            }
            if (!root.left && !root.right) {
                let temp = path.join('->');
                ans.push(temp)
            }
        }

    }
    search(root, [])
    return ans
};

难度进阶

47. 全排列 II(medium)

题目:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]
示例 2:

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

分析
这题的核心思想,是构建一棵树,回溯递归。比如示例一:根节点有1,1,2三种情况,但是1和1是一样的,那我们就可以直接忽略重复的情况。从同一个节点出发去扩散,在每一层中将重复的子节点删除,可以使用map来记录重复的值,保证不会走向相同的路径。用过的节点,我们可以用一个新数组来记录他的位置used,为1表示数据已经被使用了。

var permuteUnique = function (nums) {
    let n = nums.length;
    let used = new Array(n).fill(0);
    let ans = [];
    function back(index, path) {
        if (path.length == n) {
            // used[index] = 0;
            if (ans.indexOf(path) == -1) {
                ans.push(path);
            }
            return
        }
        let res = new Map()
        for (let i = 0; i < n; i++) {
            if (!used[i]) {
                if (!res.has(nums[i])) {
                    res.set(nums[i],1)
                    path.push(nums[i])
                    used[i] = 1;
                    back(i, Array.from(path));
                    path.pop();
                    used[i] = 0
                }

            }
        }
        // path.pop();
        // used[index] = 0
    }
    let res = new Map()
    for (let i = 0; i < n; i++) {
        if (!res.has(nums[i])) {
            res.set(nums[i],1);
            used[i] = 1;
            back(i, [nums[i]])
            used[i] = 0
        }

    }
    return ans
};

40. 组合总和 II (medium)

题目:给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

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

注意:解集不能包含重复的组合。
示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
提示:

1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30

分析
这题也是用回溯,方法和上题类似,只是每个加数都是大于0的,可以提前结束某个不合适的路径。同样,他也有重复的数,每一层里,每个加数只让他出现一次。

var combinationSum2 = function (candidates, target) {
    let n = candidates.length;
    let used = new Array(n).fill(0);
    let ans = [];
    function dfs(index,path, sum) {
        if (sum > target) {
            return
        }
        if (sum == target) {
            ans.push(path)
            return
        }
        let temp = new Map();
        for (let i = index+1;i<n;i++){
            if(!used[i] && !temp.has(candidates[i])){
                used[i]=1;
                temp.set(candidates[i],1)
                path.push(candidates[i])
                dfs(i, Array.from(path),sum+candidates[i]);
                used[i]=0;
                path.pop();
            }
        }
    }
    candidates.sort((a,b)=>a-b)
    dfs(-1,[],0)
    return ans
};

310. 最小高度树(medium)

题目:树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树 。

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

在这里插入图片描述
在这里插入图片描述
分析
这题我本来还想着用回溯法去做,找最小高度数。但是后面数据一旦多起来就会超时,所以要换个方法。由于这是个无向图,所以可以尝试用拓扑结构。看了一些分析,找到能让树最小的点,就要让权重小的子树,一层层砍掉。最后留下一个或者两个节点,那就是让树最低的节点。写法上还是用广度优先。但是要建立两个数组,一个存与A相关联的所有点,另一个存权重,有一条线,点的权重+1,砍掉一条线,权重-1.当权重为1的时候将该点相关联的线删除,关联的点权重-1.

var findMinHeightTrees = function(n, edges) {
   let G = new Array(n).fill(0).map(()=>[]);
   let dev = new Array(n).fill(0);
   let queue = []
   let res = []
   if(n==1) return [0]
   for(let [u,v] of edges){
    G[u].push(v);G[v].push(u);
    dev[u]++;dev[v]++;
   }

   for(let i=0;i<n;i++){
    if(dev[i]==1){
        queue.push(i)
    }
   }
   while(queue.length>0){
    res = [];
    let size = queue.length;
    for(let i=0;i<size;i++){
        let node = queue.shift();
        dev[node]--;
        res.push(node)
        for(let d of G[node]){
            if(dev[d]>0){
                dev[d]--;
                if(dev[d]==1) queue.push(d)
            }
           
        }
    }
   }
   return res
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值