文章目录
1.深度优先搜索dfs
对于树而言,遍历某个节点优先遍历其孩子节点而不是其兄弟节点。
dfs是考虑所有可能,会重复计算,有时需要记忆化存储(动规)、或者用于树形dp。
其可以通过栈以迭代的方式来实现。
695. 岛屿的最大面积 – 中等
求最大的岛屿面积,相邻的1的土地为同一个岛屿。
输入: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 。
/**
可以使用dfs,也可以使用bfs
1. 递归写法
2. 栈写法
**/
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int max = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[i].length; j++) {
if (grid[i][j] == 1) max = Math.max(max, countArea(grid, i, j));
}
}
return max;
}
private int countArea(int[][] grid, int i, int j) {
if(i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] == 0) return 0;
int ans = 1;
grid[i][j] = 0;
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int row = i + direction[k], colomn = j + direction[k+1];
ans += countArea(grid, row, colomn);
}
return ans;
}
}
/***/
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int max = 0, m = grid.length - 1, n = grid[0].length - 1;
Deque<Integer> xDeque = new LinkedList<>();
Deque<Integer> yDeque = new LinkedList<>();
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
int area = 0;
if (grid[i][j] == 1) {
xDeque.push(i);
yDeque.push(j);
while (!xDeque.isEmpty()) {
int x = xDeque.poll(), y = yDeque.poll();
if (x < 0 || y < 0 || x > m || y > n || grid[x][y] == 0) continue;
grid[x][y] = 0;
++area;
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x1 = x + direction[k], y1 = y + direction[k+1];
xDeque.push(x1);
yDeque.push(y1);
}
}
}
max = Math.max(max, area);
}
}
return max;
}
}
547. 省份数量 – 中等
有n个城市,只要直接或间接相邻的城市就算一个省,求这n个城市中省份数量。
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
[1,1,0],[1,1,0],[0,0,1] 分别表示 城市1 与 城市 1,2 相连
城市2 与 城市 1,2 相连
城市3 与 城市 1,3 相连
/**
1. dfs: 如果该城市没有被访问过,省份+1, 同时访问其相邻城市。
2. bfs类似
3. 并查集:parent[i] 记录每个节点的祖先,每个祖先代表一个省 (即:求连通分量个数)
**/
class Solution {
boolean[] isVisted;
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
isVisted = new boolean[n];
int res = 0;
for(int i = 0; i < n; ++i) {
if (!isVisted[i]) {
++res;
dfs(isConnected, i);
}
}
return res;
}
private void dfs(int[][] isConnected, int i) {
isVisted[i] = true;
for (int j = 0; j < isConnected[i].length; j++) {
int val = isConnected[i][j];
if (val == 1 && !isVisted[j]) {
dfs(isConnected, j);
}
}
}
}
/****/
class Solution {
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
int[] parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (isConnected[i][j] == 1) {
union(parent, i, j);
}
}
}
int res = 0;
for (int i = 0; i < n; i++) {
if (parent[i] == i) ++res;
}
return res;
}
private void union(int[] parent, int i, int j) {
parent[find(parent, j)] = find(parent, i); // j的祖先挂到i祖先下面
}
private int find(int[] parent, int i) {
while (parent[i] != i) i = parent[i];
return i;
}
}
417. 太平洋大西洋水流问题 – 中等
求m*n的岛屿中,雨水同时可以流入太平洋和大西洋的岛屿。高的岛屿的雨水可以流到不高于自己的相邻岛屿上,与海相邻的岛屿雨水可以直接流入海里。岛屿的左上相邻太平洋,右下相邻大西洋。
输入: 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]]
如图所示:黄色部分的岛屿上的雨水可以同时流入到太平洋和大西洋中。
/**
从四个边界出发dfs。
例如: 左边界一定可以流入太平洋,那么与左边界相邻的且比它高的一定也可以流入太平洋。
如果一个格子同时可以流入太平洋和大西洋,那么加入到结果中。
**/
class Solution {
public List<List<Integer>> pacificAtlantic(int[][] heights) {
int m = heights.length, n = heights[0].length;
boolean[][] p2a = new boolean[m][n];
boolean[][] a2p = new boolean[m][n];
for (int i = 0; i < m; i++) {
dfs(heights, i, 0, a2p);
dfs(heights, i, n-1, p2a);
}
for (int i = 0; i < n; i++) {
dfs(heights, 0, i, a2p);
dfs(heights, m-1, i, p2a);
}
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (p2a[i][j] && a2p[i][j]) {
List<Integer> ans = new ArrayList<>();
Collections.addAll(ans, i, j);
res.add(ans);
}
}
}
return res;
}
private void dfs(int[][] heights, int i, int j, boolean[][] res) {
if (res[i][j]) return;
res[i][j] = true;
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x1 = i + direction[k], y1 = j + direction[k+1];
if (x1 < 0 || y1 < 0 || x1 >= res.length || y1 >= res[0].length) continue;
if (heights[x1][y1] >= heights[i][j]) {
dfs(heights, x1, y1, res);
}
}
}
}
回溯法
在搜索过程中,状态会一直改变,回溯是相当于同一个位置,使得其状态一样。也就是在dfs方法执行返回到上一级时,需要返回到上一级的初始状态。
46. 全排列 – 中等
给一个不含重复元素的数组,返回其全排列。
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
/**
回溯法:对于i位置,其可取的范围是 [i,n-1] 每次交换一个位置,每次交换需要返回原状态
*/
class Solution {
public List<List<Integer>> permute(int[] nums) {
int n = nums.length;
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
path.add(nums[i]);
}
backTrack(res, n, 0, path);
return res;
}
private void backTrack(List<List<Integer>> res, int n, int cur, List<Integer> path) {
if (cur == n) {
res.add(new ArrayList<>(path));
return;
}
for (int i = cur; i < n; i++) {
Collections.swap(path, cur , i);
backTrack(res, n, cur + 1, path);
Collections.swap(path, cur , i);
}
}
}
77. 组合 – 中等
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
输入:n = 4, k = 2
输出: [[2,4], [3,4], [2,3], [1,2], [1,3], [1,4]]
解释:[1,4] 和 [4,1] 是同一种组合
/**
组合与排列不同:
排列:[1,4] [4,1] 是不同排列
组合: [1,4] [4,1] 是同一种组合
-- 可以对原数组进行一次升序排序,防止出现重复组合
-- 重点:这里是有序的所以不用排序
组合的回溯问题:在i位置的可选数据范围 [i, n]
每次选择了一个数,之后就不会重复选择了,回溯后把它给删了
*/
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
back_Track(res, n, k, 1, path);
return res;
}
private void back_Track(List<List<Integer>> res, int n, int k, int cur, List<Integer> path) {
if (path.size() + n - cur + 1 < k) return; // 剪枝,如果选择剩下所有元素达不到k个数,结束
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
for (int i = cur; i <= n; i++) {
path.add(i);
back_Track(res, n, k, i + 1, path);
path.remove(path.size() - 1);
}
}
}
79. 单词搜索 – 中等
判断一个单词是否在二维字符网格中,同一单元格内字母不允许被重复使用。
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
/**
dfs+回溯。
用tag数组记录每个字符是否被访问,如果没有找到答案回溯,置为未访问。
**/
class Solution {
int m,n;
public boolean exist(char[][] board, String word) {
m = board.length;
n = board[0].length;
boolean[][] tag = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == word.charAt(0)) {
if (back(board, word, tag, 0, i , j)) return true;
}
}
}
return false;
}
private boolean back(char[][] board, String word, boolean[][] tag, int idx, int i, int j) {
tag[i][j] = true;
if (idx == word.length() - 1) return true;
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x = i + direction[k], y = j + direction[k+1];
if (x < 0 || y < 0 || x >= board.length || y >= board[0].length
|| board[x][y] != word.charAt(idx + 1) || tag[x][y]) continue;
if (back(board, word, tag, idx + 1, x, y)) return true;
}
tag[i][j] = false;
return false;
}
}
51. N 皇后 – 困难
同一行同一列同一斜线的皇后可以互相攻击,求在n*n的棋盘上放n个皇后,使得皇后之间不互相攻击的所有可能。
输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如图,2种情况,其中.表示不放皇后,Q表示放皇后。
/**
回溯:一行一行添加皇后,如果该行的某个位置可以放皇后
那么将皇后所在的列,左对角线 和 有对角线 设置为true,表示不可以在放了
注:左对角线的 row + col 是定值
右对角线 row-col 是定值, row-col+n-1 将所有值映射到 正值
**/
class Solution {
boolean[] colomn, lDiagonal, rDiagonal;
public List<List<String>> solveNQueens(int n) {
List<List<String>> res = new ArrayList<>();
List<String> path = new ArrayList<>();
colomn = new boolean[n]; lDiagonal = new boolean[2*n]; rDiagonal = new boolean[2*n];
dfs(res, 0, path, n-1);
return res;
}
private void dfs(List<List<String>> res, int row, List<String> path, int tail) {
if (row == tail + 1) {
res.add(new ArrayList<>(path));
return;
}
for (int col = 0; col <= tail; ++col) {
if (colomn[col] || lDiagonal[row + 1 + col] || rDiagonal[tail + row + 1 - col]) continue;
colomn[col] = lDiagonal[row + 1 + col] = rDiagonal[tail + row + 1 - col] = true;
path.add(createString(col, tail));
dfs(res, row+1 , path, tail);
path.remove(path.size() - 1);
colomn[col] = lDiagonal[row + 1 + col] = rDiagonal[tail + row + 1 - col] = false;
}
}
private String createString(int col, int tail) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j <= tail; j++) {
if (j == col) sb.append('Q');
else sb.append('.');
}
return sb.toString();
}
}
广度优先搜索
对于树而言,优先搜索其兄弟节点,然后搜索其孩子节点。可以使用队列来迭代实现。
934. 最短的桥 – 中等
连接两个岛屿的最短长度。
输入:grid = [[0,1],[1,0]]
输出:1
解释:把其中一个0变成1,两个岛屿就相连了
/**
dfs+bfs:
找到第一个岛并将其所有元素存在队列中,然后弹出队列中元素进行bfs搜索。
第一次找第一个岛时,将其元素涂成2。
遍历时遇到0,就将其涂成2,遇到1返回其层级。
**/
class Solution {
Queue<Integer> x_i = new LinkedList<>();
Queue<Integer> y_j = new LinkedList<>();
public int shortestBridge(int[][] grid) {
int n = grid.length;
OUT:
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
dfsIs(grid, i, j);
break OUT;
}
}
}
int level = 0;
// System.out.println(x_i);
while(!x_i.isEmpty()) {
int size = x_i.size();
for (int i = 0; i < size; i++) {
int x = x_i.poll(), y = y_j.poll();
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x1 = x + direction[k], y1 = y + direction[k+1];
if (x1 < 0 || y1 < 0 || x1 >= n || y1 >= n) continue;
if (grid[x1][y1] == 1) return level;
else if (grid[x1][y1] == 0) {
grid[x1][y1] = 2;
x_i.add(x1);
y_j.add(y1);
}
}
}
++level;
}
return level;
}
// 将第一个岛中所有1图成2
private void dfsIs(int[][] grid, int i, int j) {
if (grid[i][j] == 2) return;
grid[i][j] = 2;
x_i.add(i);
y_j.add(j);
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x = i + direction[k], y = j + direction[k+1];
if (x < 0 || y < 0 || x >= grid.length
|| y >= grid[0].length || grid[x][y] != 1) continue;
dfsIs(grid, x, y);
}
}
}
126. 单词接龙 II – 困难
给定一个单词和词典,单词每次可以修改一个字符得到一个新单词,如果新单词在词典中,这次修改是可行的,输出单词到目标单词的最短转换序列,可以不存在转换序列。
输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
解释:存在 2 种最短的转换序列:
"hit" -> "hot" -> "dot" -> "dog" -> "cog" "hit" -> "hot" -> "lot" -> "log" -> "cog"
单BFS超时,需要双向同时BFS,太麻烦了,懒得思考。
练习
130. 被围绕的区域 – 中等
给定一个m*n的矩阵,将所有被X包围的O变成X。
输入: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相邻的O不会被修改为X
/**
思路:把与边界O相邻的所有改为A,然后遍历整个board,将O变为X,A变为O
**/
class Solution {
public void solve(char[][] board) {
int m = board.length, n = board[0].length;
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O') dfsFindX(board, i, 0);
if (board[i][n-1] == 'O') dfsFindX(board, i, n-1);
}
for (int i = 0; i < n; i++) {
if (board[0][i] == 'O') dfsFindX(board, 0, i);
if (board[m-1][i] == 'O') dfsFindX(board, m-1, i);
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') board[i][j] = 'X';
else if (board[i][j] == 'A') board[i][j] = 'O';
}
}
}
private void dfsFindX(char[][] board, int i, int j) {
board[i][j] = 'A';
int[] direction = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; k++) {
int x = i + direction[k], y = j + direction[k+1];
if (x < 0 || y < 0 || x >= board.length || y >= board[0].length || board[x][y] != 'O') continue;
dfsFindX(board, x, y);
}
}
}
257. 二叉树的所有路径 – 简单
输出根节点到叶子节点的所有路径。
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
dfsTree(res, root, new String());
return res;
}
private void dfsTree(List<String> res, TreeNode root, String s) {
s += root.val;
if (root.left == null && root.right == null) {
res.add(new String(s));
return;
}
s += "->";
int len = s.length();
if (root.left != null) dfsTree(res, root.left, s);
if (root.right != null) dfsTree(res, root.right, s.substring(0, len));
}
}
47. 全排列 II – 中等
返回可包含重复数的全排列。
输入:nums = [1,1,2]
输出: [[1,1,2], [1,2,1], [2,1,1]]
/**
思路和不包含重复数的全排列类似。
只是在同一个位置不能取相同的数。
如 [1 1 2] 第一个位置可取第一个1 而不可以取第2个1 --- 自己规定的一种方式
可以用set来记录当前取值集合。
**/
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
int n = nums.length;
for (int i = 0; i < n; i++) {
path.add(nums[i]);
}
dfsBack(res, path, 0, n);
return res;
}
private void dfsBack(List<List<Integer>> res, List<Integer> path, int loc, int n) {
if (loc == n) {
res.add(new ArrayList<>(path));
return;
}
Set<Integer> hasVisted = new HashSet<>(); // 去重
for (int i = loc; i < n; i++) {
if (hasVisted.contains(path.get(i))) continue;
hasVisted.add(path.get(i));
Collections.swap(path, i, loc);
dfsBack(res, path, loc + 1, n);
Collections.swap(path, i, loc);
}
}
}
40. 组合总和 II – 中等
找出数组中所有和为目标值的组合。(不能包含重复的组合)
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出: [[1,1,6], [1,2,5], [1,7], [2,6]]
/**
每个位置不能选重复数,但还存在另一种重复 如上面例子中 [1,7] 和 [7,1]
选择顺序是不同的,但组合是相同。
因此在选择前对数组进行排序。
**/
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates); // 增加一个排序
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfsFind(res, candidates, target, path, 0, target);
return res;
}
private void dfsFind(List<List<Integer>> res, int[] candidates, int target, List<Integer> path, int idx, int val) {
if (val == 0) {
res.add(new ArrayList<>(path));
return;
}
if (val < 0 || idx >= candidates.length) return;
Set<Integer> isVisited = new HashSet<>();
for (int i = idx; i < candidates.length; i++) {
if (isVisited.contains(candidates[i])) continue;
isVisited.add(candidates[i]);
path.add(candidates[i]);
dfsFind(res, candidates, target, path, i + 1, val - candidates[i]);
path.remove(path.size() - 1);
}
}
}
37. 解数独 – 困难
给定一个9宫格数组,求解该数独,填入数字使得其满足数独规则。
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
/**
这里和n皇后类似,除了考虑每行每列,还要考虑9宫格里面的数。
不同之处:n皇后问题,每一行只要选择一个皇后位置即可,其它位置不需要考虑,最后一行结束即可返回。
而数独问题,每一行所有空位置都要考虑,因此回溯问题不等同。
这里可以在遍历时,将每个空格记录在List中,用pos作为这是处理的第pos个空格,且每个空格取值
在[1,9]之间,如果 pos = List.size() 处理完成,结束。
**/
class Solution {
boolean[][] row, col;
boolean[][][] square;
boolean flag = false;
List<int[]> space;
public void solveSudoku(char[][] board) {
int n = board.length;
row = new boolean[9][9];
col = new boolean[9][9];
square = new boolean[3][3][9];
space = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] != '.') {
int cur = board[i][j] - '1';
row[i][cur] = col[j][cur] = square[i/3][j/3][cur] = true;
} else {
space.add(new int[] {i, j});
}
}
}
dfsBoard(board, 0);
}
private void dfsBoard(char[][] board, int pos) {
if (pos == space.size()) {
flag = true;
return;
}
int x = space.get(pos)[0], y = space.get(pos)[1];
for (int val = 0; val < 9 && !flag; ++val) {
if (!row[x][val] && !col[y][val] && !square[x/3][y/3][val]) {
row[x][val] = col[y][val] = square[x/3][y/3][val] = true; board[x][y] = (char) (val + 1 + '0');
dfsBoard(board, pos+1);
row[x][val] = col[y][val] = square[x/3][y/3][val] = false;
}
}
}
}
310. 最小高度树 – 中等
给定一个树,选择一个节点作为根节点,使得其树的高度最小,返回所有可能的根节点。(高度 = 根到叶边数)
输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]
解释:n表示6个节点, edge[0] = [3,0] 表示 3,0 相连, edge[1] = [3,1] 表示 3,1 相连
这里只有两种选择,即选择3,4作为根节点,此时最小高度为2
/**
常规做法:以每个节点作为根节点,求其高度,可以用当前最小高度来剪枝
优化:寻找最长路径,返回其中间一个或两个节点
最长路径寻找:以任意点为起点 (以0为例),找到距其最远节点u;
在从u出发,找到距其最远节点v,那么 uv这里路径是最长路径。
这里可以在处理最长路径时,增加一个parent 记录其父节点,便于搜索该路径。
另一种思路:不断删除度为1的节点
**/
class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> res = new ArrayList<>();
List<Integer>[] tree = new ArrayList[n];
for (int i = 0; i < tree.length; i++) {
tree[i] = new ArrayList<>();
}
for (int i = 0; i < edges.length; i++) {
int x = edges[i][0], y = edges[i][1];
tree[x].add(y);
tree[y].add(x);
}
int[] parent = new int[n];
for (int i = 0; i < n; ++i) parent[i] = i;
int u = findMaxFarNode(tree, parent, 0);
int v = findMaxFarNode(tree, parent, u);
List<Integer> path = new ArrayList<>();
while (v != u) {
path.add(v);
v = parent[v];
}
path.add(u);
res.add(path.get(path.size() / 2));
if (path.size() % 2 == 0) {
res.add(path.get(path.size() / 2 - 1));
}
return res;
}
private int findMaxFarNode(List<Integer>[] tree, int[] parent, int node) {
Queue<Integer> queue = new LinkedList<>();
queue.add(node);
boolean[] hasVisited = new boolean[tree.length];
int resNode = -1;
while (!queue.isEmpty()) {
int size = queue.size();
resNode = queue.peek();
for (int i = 0; i < size; i++) {
int curNode = queue.poll();
hasVisited[curNode] = true;
for (int nextNode : tree[curNode]) {
if (hasVisited[nextNode]) continue;
parent[nextNode] = curNode; // 记录每个节点的父亲节点
queue.add(nextNode);
}
}
}
return resNode;
}
}