Question 0 Traversal & Reachable模版
Graph Node
// Some code
class GraphNode {
int id;
List<GraphNode> neighbors;
// assume the graphNode has already overridden hashCode(), equals(), toString();
@Override
public int hashCode(){
}
@Override
public int equals() {
}
@Override
public int toString() {
}
}
DFS对点的遍历
一点出发,遍历到我们能遍历到的所有点
每个点可能被到达多次(环,多个父亲节点)
Logic
如果访问到当前的点,赶紧回头(不访问)
否则,访问这个点,同时继续访问他的neighbors
public List<GraphNode> DFSMarkVisitedOne(GraphNode start) {
List<GraphNode> result = new ArrayList<>();
Set<GraphNode> visited = new HashSet<>();
DFSMarkVisitedOne(start, visited, result);
return result;
}
private void DFSMarkVisitedOne(GraphNode curNode, Set<GraphNode> visited, List<GraphNode> result) {
if (visited.contains(curNode)) {
return;
}
visited.add(curNode);
result.add(curNode);
for (GraphNode nei: curNode.neighbors) {
DFSMarkVisitedOne(nei, visited, result);
}
}
对于不联通图中有多个联通分量
要想遍历出所有的点,不可能从一个点出发
有几个联通分量,需要从几个点出发/需要出发几次,每一次从一个新的connect里挑选一个点出发
// Some code
//有多少个联通分量
public int DFSMarkVisitedOne(List<GraphNode> nodeList) {
Set<GraphNode> visited = new HashSet<>();
int numberOfConnectedComponent = 0;
for (GraphNode eachNode : nodeList) {
if (!visited.contains(curNode)) {
DFSMarkVisitedOne(curNode, visited);
numberOfConnectedComponent++;
}
}
return numberOfConnectedComponent;
}
private void DFSMarkVisitedOne(GraphNode curNode, Set<GraphNode> visited, List<GraphNode> result) {
if (visited.contains(curNode)) {
return;
}
visited.add(curNode);
result.add(curNode);
for (GraphNode nei: curNode.neighbors) {
DFSMarkVisitedOne(nei, visited, result);
}
}
Follow Up:
联通分量里最多的点是多少?
记录每个联通分量里的点数,每次出发都得记录一下这次出发遍历;轭多少点
Maximum it
有多少个联通分量包含最多的点?
实际记录下来这个联通分量有哪些点,有多少点
筛选出拥有最多联通分量点数的所有联通分量
BFS对点的遍历
同样的逻辑,同样的操作(expansion and generation), 注意防止多次generate或者expand
去重的位置
既可以在exapansion的时候去重。
允许一个点被generate多次,但是只对第一次exapnd的这个点进行访问,后面我发现再expand到已经visited过的node,直接ignore
也可以在Generation的时候去重。
不允许一个点被generate多次,每一个点如果你最多只放进queue一次,那么它也就至多被拿出来一次
优缺点比较
Expansion去重:必然正确,但是缺点为空间复杂度高(queue size不能保证最大的O(V))
Generation去重:空间好,Queue的wize至多不会超过O(V),缺点是不一定正确(connectivity and reachable problem一定正确,但是shortest path and etc 不一定正确)
public List<GraphNode> BFS(GraphNode start) {
List<GraphNode> result= new ArrayList<>();
Set<GraphNode> visited = new HashSet<>();
Deque<GraphNode> queue = new ArrayDeque<>();
queue.offer(start);
visited.add(start);
while (!queue.isEmpty()) {
GraphNode curNode = queue.poll();
result.add(curNode.id);
for (GraphNode nei: curNode.neighbors) {
if (!visited.contains(nei)) {
queue.offer(nei);
visited.add(nei);
}
}
}
return result;
}
DFS对connection& reachable
public boolean DFSMarkVisitedOne(GraphNode start, GraphNode target) {
Set<GraphNode> visited = new HashSet<>();
return DFSMarkVisitedOne(start, visited, target);
}
// boolean means 从curNode是否能到target
private boolean DFSMarkVisitedOne(GraphNode curNode, Set<GraphNode> visited, GraphNode target){
if (visited.contains(curNode)) {
return false;
}
visited.add(curNode);
if (curNode.equals(target)) { // 注意这里是用equals
return true;
}
for (GraphNode nei: curNode.neighbors) {
if (DESMarkVisitedOne(nei, visited, target)) {
return true;
}
}
return false;
}
BFS对connection& reachable
// BFS all in Generation;
public boolean BFS(GraphNode start, GraphNode target) {
Set<GraphNode> visited = new HashSet<>();
Deque<GraphNode> queue = new ArrayDeque<>();
queue.offer(start);
visited.offer(start);
while (!queue.isEmpty()) {
GraphNode curNode = queue.poll();
for (GraphNode nei: curNode.neighbors) {
if (nei.equals(target)) {
return true;
}
if (!visited.contains(nei)) {
visited.add(nei);
queue.offer(nei);
}
}
}
return false; // unreachable
}
// BFS all in Expansion
public boolean BFS(GraphNode start, GraphNode target) {
Set<GraphNode> visited = new HashSet<>();
Deque<GraphNode> queue = new ArrayDeque<>();
while (!queue.isEmpty()) {
GraphNode curNode = queue.poll();
if (visited.contains(curNode)) {
continue;
}
if (curNode.equals(target)) {
return true;
}
for (GraphNode nei: curNode.neighbors) {
queue.offer(nei);
}
}
}
对边的遍历:Backtracking(see the future section)
The Representation of the Graph in these questions
面试中图论的基本要素
图论面试的第零步: this is actually a graph problem (model)
面试中第一步: graph building (u - v) 构图请单独写一个helper function
题库喜欢的表示方法1: int[][] graph
list of all edges,甚至可以是权重
本质就是更新这两个map
Map<Integer, Map<Integer, Integer>> graph = new HashMap<>()
Map<u, Map<v, weight>> graph
Map<Integer, Integer> IndegreeMap = new HashMap<>();
Map<u, indegree>
Case 1: Telling me is an Undirected Graph
// 这两行声明再其他函数里
Map<Integer, Map<Integer, Integer>> graph = new HashMap<>();
Map<Integer, Integer> IndegreeMap = new HashMap<>();
private void buildGraph(int[][] edges, Map<Integer, Map<Integer, Integer>> graph, Map<Integer, Integer> IndegreeMap) {
for (int[] edge: edges) {
int u = edge[0];
int v = edge[1];
int weight = edge[2];
//两边都要放
graph.putIfAbsent(u, new HashMap<>());
graph.putIfAbsent(v, new HashMap<>());
graph.get(u).put(v, weight);
graph.get(v).put(u, weight);
}
}
Case 2: Telling me is a Directed Graph
// 这两行声明再其他函数里
Map<Integer, Map<Integer, Integer>> graph = new HashMap<>();
Map<Integer, Integer> IndegreeMap = new HashMap<>();
private void buildGraph(int[][] edges, Map<Integer, Map<Integer, Integer>> graph, Map<Integer, Integer> IndegreeMap) {
for (int[] edge: edges) {
int u = edge[0];
int v = edge[1];
int weight = edge[2];
// u --> v u里面加v,但是v的入度更新
graph.putIfAbsent(u, new HashMap<>());
graph.get(u).put(v, weight);
IndegreeMap.put(v, IndegreeMap.getOrDefault(v, 0) + 1);
}
}
题库喜欢的表达方法2: int[][] matrix 扔给你个地图
example: grid/matrix == [[0 0 (0,1) 1 1 ], [0 1(1, 1) 1 2], [0 1 1 0(2,3)], [0 0 1 0]]
坐标当点:
方向:二方向/四方向/八方向
Question 1: 用每个点的matrix坐标表示一个node会不会超界
private boolean isValid(int[][] grid, int i, int j) {
return i >= 0 && i < grid.length && j >=0 && j < grid[0].length;
}
// with blocker && visited[i][j] version
private static final int BLOCKER = 1;
private boolean isValid(int[][] grid, int i, int j) {
return i >= 0 && i < grid.length && j >=0 && j < grid[0].length && grid[i][j]!= BLOCKER && !visited[i][j];
}
Question 2 方向怎么办?
// Case 1: 二方向(随机选两个)或者四方向
private static final int[][] DIRS = {{-1, 0}, {1, 0}; {0, -1}; {0, 1}};
// 上,下,左,右
// Case 2: 八方向
private static final int[][] DIRS = {
{-1, 0}, {1, 0}, {0, -1}, {0, 1},
{-1, -1}, {1, -1}, {-1, 1}, {1, 1};
}
// 上,下,左,右,左上,左下,右上,右下
// Case 3: 跳马八方向
private static final int[][] DIRS = {
{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2},
{1, -2}, {1, 2}, {2, -1}, {2, 1};
}
Use Case: 给你一个点(curX, curY) 能不能访问所有邻居
public void printAllNeighbors(int[][] grid, int curX, int curY, boolean[][] visited) {
for (int[] dir: DIRS) {
int neiX = curX + dir[0];
int neiY = curY + dir[1];
if (isValid(grid, neiX, neiY, visited)) {
System.out.println(grid[neiX][neiY]);
// DFS(neiX, neiY,...) or Queue.offer...
}
}
}
Last updated