References:
[1] Geeks for Geeks https://www.geeksforgeeks.org/graph-data-structure-and-algorithms/
BFS, DFS
127 word ladder
public class WordLadder {
public int ladderLength(String beginWord, String endWord, List wordList) {
Set marked = new HashSet<>();
Queue queue = new LinkedList();
queue.offer(beginWord);
marked.add(beginWord);
int layer = 1;
int layerSize = 1;
while (!queue.isEmpty()) {
String cur = queue.poll();
if (cur.equals(endWord))
return layer;
layerSize--;
//找cur的所有adjacent nodes
for (String s : wordList) {
if (isAdjacent(cur, s)) {
if (!marked.contains(s)) {
marked.add(s);
queue.offer(s);
}
}
}
if (layerSize == 0) {
layerSize = queue.size();
layer++;
}
}
// if not found;
return 0;
}
private boolean isAdjacent(String a, String b) {
if (a == null || b == null || a.length() != b.length()) return false;
int diff = 0;
for (int i = 0; i < a.length(); i++) {
if (a.charAt(i) != b.charAt(i)) {
diff++;
}
}
return diff == 1 ? true : false;
}
}
解析:
等同于在一个无向图中做BFS,起点是beginWord,终点是endWord
比较简单的想法是先根据wordlist构建无向图,再进行常规的bfs搜索。
快一些的方法是不够造无向图,每一步直接搜索adjcent nodes
更快的方法是不相互比较是否是adjacent -- O(dict.size() ^ 2 * String.length)。而是直接根据给定的string来生成所有的adjacent string -- O(string.length * 26), 生成后判断是否在dictionary里面。O(dict.size() * String.length)
126 word ladder II
与127的区别是
(1) 要求所有的paths
(2) 要给出具体的path : start -> ... -> end解法: Two Phases (1) BFS求出最短路径dst (2) dfs 全搜索最短路径dst个节点 (类似exhaustive search), 遇到dst个string == endWord时,就把那条path输出(很像permutation 里面backtracking的做法,需要还原重置)。
199 Binary Tree Right Side View
public List rightSideView(TreeNode root) {
List res = new LinkedList<>();
if (root == null)
return res;
Queue queue = new LinkedList();
queue.offer(root);
int layerSize = 1;
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
layerSize--;
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
if (layerSize == 0) {
layerSize = queue.size();
res.add(cur.val);
}
}
return res;
}
解析:bfs按照每个layer最后一个元素就是right side view
133 Clone Graph
public class CloneGraph {
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
if (node == null)
return null;
// phase I
Map map = new HashMap<>();
Set marked = new HashSet<>();
Queue queue = new LinkedList<>();
queue.offer(node);
marked.add(node);
while (!queue.isEmpty()) {
UndirectedGraphNode curNode = queue.poll();
map.put(curNode, new UndirectedGraphNode(curNode.label)); // copy node;
for (UndirectedGraphNode n : curNode.neighbors) {
if (!marked.contains(n)) {
marked.add(n);
queue.offer(n);
}
}
}
// phase II copy edges;
marked.clear();
queue.offer(node);
marked.add(node);
while (!queue.isEmpty()) {
UndirectedGraphNode curNode = queue.poll();
UndirectedGraphNode curNodeCopy = map.get(curNode);
for (UndirectedGraphNode n : curNode.neighbors) {
curNodeCopy.neighbors.add(map.get(n));
if (!marked.contains(n)) {
marked.add(n);
queue.offer(n);
}
}
}
return map.get(node);
}
解析: 分成两个phase
(1) bfs/dfs复制节点。
(2) bfs/dfs复制edges
200 Number of Islands
private char[][] grid;
public int numIslands(char[][] grid) {
this.grid = grid;
if (grid == null || grid.length == 0)
return 0;
int numOfIslands = 0;
int N = grid.length;
int M = grid[0].length;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == '1') {
dfs(i, j);
numOfIslands++;
}
}
}
return numOfIslands;
}
// dfs from node (i, j);
private void dfs(int i, int j) {
int N = grid.length;
int M = grid[0].length;
if (i < 0 || i >= N || j < 0 || j >= M || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(i+1, j);
dfs(i-1, j);
dfs(i, j+1);
dfs(i, j-1);
}
解析:
这题可以理解为森林找树的数量
笨的方法可以maintain一个visited矩阵。这个矩阵可以用在dfs中,也可以用在遍历所有点时判断是否还需要dfs。
比较巧的方法是在visit完一个点后就把这个点删掉(设为0),同时visit该点的所有neighbors。
(1)这样dfs时无需判断点是否已经visit过
(2) visit neighbour时也可以直接visit上下左右而不需要判断marked
Number of Islands II
public class NumberOfIslandsII {
private int[] dx = new int[] {1, -1, 0, 0};
private int[] dy = new int[] {0, 0, 1, -1};
private int[] id; // position -> id : (x,y) -> x*n+y
public List numIslands2(int m, int n, int[][] positions) {
List res = new LinkedList();
if (m == 0 || n == 0 || positions == null || positions.length == 0)
return res;
id = new int[m*n];
Arrays.fill(id, -1);
int count = 0; // initially 0 islands
for (int i = 0; i < positions.length; i++) {
int curX = positions[i][0];
int curY = positions[i][1];
int curId = curX*n + curY;
id[curId] = curId;
count++; // assume it is an island
for (int j = 0; j < dx.length; j++) {
int adjX = curX + dx[j];
int adjY = curY + dy[j];
int adjId = adjX*n + adjY;
if (adjX < 0 || adjX >= n || adjY < 0 || adjY >= m || id[adjId] < 0) {
continue; // not valid adjacent nodes;
}
// otherwise check the father;
int curFather = findFather(curId);
int adjFather = findFather(adjId);
if (curFather != adjFather) {
id[curFather] = adjFather; // union
count--;
}
}
res.add(count);
}
return res;
}
// returns the boss of this point.
private int findFather(int pointId) {
if (id[pointId] == pointId)
return pointId;
else {
id[pointId] = findFather(id[pointId]);
return id[pointId]; // here is path compression;
}
}
}
解析:无向图找connected component最快的方法就是union-find
这题果然是把二维的点转化成1维:[x,y] -> xN + y 作为ID,然后运用union-find。
一开始总共有MN个tree(假设每个位置都是树)。Every time a new position is placed, find root of adjacent trees and union if possible;
- path compression:
findId(int i) {return id[i] = findId(id[i])}