- 描述及例题:
在一个点线组成的矩阵关系上即为一个图的问题。图的问题主要分为三种类型:
- Deep copy: 在图上建立一个新的图,把原图的关系都copy到新图上。
- Union find: 在图上找到有几个union块。
- Topological sort: 根据pair的关系,或者点线的关系,构建出一个图,找出从start到end的path。
- 解决思路及时间复杂度:
图的问题基本都采用 DFS或者BFS的思路进行解决。
- Deep copy:
- 先遍历一遍原图,一边遍历一边建立新的图的点。用hashmap记录原node和新node之间的关系。
- 再复制边的关系。把原图的边的关系,在新的node之间边的关系同样复制
- 以Clone Graph为例,代码如下:
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
if(node == null){
return null;
}
// write your code here
ArrayList<UndirectedGraphNode> list = new ArrayList<UndirectedGraphNode>();
HashMap<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
int size = 0;
list.add(node);
UndirectedGraphNode newHead = new UndirectedGraphNode(node.label);
map.put(node, newHead);
while(size<list.size()){
for(UndirectedGraphNode n : list.get(size).neighbors){
if(!map.containsKey(n)){
UndirectedGraphNode newNode = new UndirectedGraphNode(n.label);
map.put(n, newNode);
list.add(n);
}
}
size++;
}
//copy edge
for(int i=0; i<list.size(); i++){
UndirectedGraphNode newN = map.get(list.get(i));
ArrayList<UndirectedGraphNode> newNeighbor = new ArrayList<UndirectedGraphNode>();
for(UndirectedGraphNode neiNode : list.get(i).neighbors){
newNeighbor.add(map.get(neiNode));
}
newN.neighbors = newNeighbor;
}
return newHead;
}
- Union find:
- 遍历整个图,一但找到符合条件的点进行dfs,把整个union进行标记,并记录union个数。
- 以Number of Islands为例:
public class Solution {
public int numIslands(boolean[][] grid) {
if(grid == null || grid.length == 0 || grid[0].length == 0){
return 0;
}
int res = 0;
boolean[][] visited = new boolean[grid.length][grid[0].length];
for(int i=0; i<grid.length; i++){
for(int j=0; j<grid[0].length; j++){
if(grid[i][j] && !visited[i][j]){
dfs(i, j, grid, visited);
res++;
}
}
}
return res;
}
private void dfs(int i, int j, boolean[][] grid, boolean[][] visited){
//end condition
if(i>=grid.length || i<0 || j>=grid[0].length || j<0 || visited[i][j] || !grid[i][j]){
return;
}
visited[i][j] = true;
dfs(i+1, j, grid, visited);
dfs(i-1, j, grid, visited);
dfs(i, j+1, grid, visited);
dfs(i, j-1, grid, visited);
}
}
- Topological sort:
- 先用hashMap记录到所有Node的入度,选择入度为0的点为起点。
- 从起点开始进行bfs,每遍历一个点就入度减一,直到入度为0时,把这个点加入结果顺序中。
- 注意点:环的问题,如果图是有环的,则计数器会产生断层,即某个节点的计数器永远无法清零(有环意味着有的节点被多加了1,然而遍历的时候一次只减一个1,所以导致无法归零),这样该节点也无法加入到结果中。所以我们只要判断这个结果的节点数和实际图中节点数相等,就代表无环,不相等,则代表有环。
- 以Topological sort代码示例:
public class Solution {
public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
ArrayList<DirectedGraphNode> res = new ArrayList<DirectedGraphNode>();
if(graph==null || graph.size()==0){
return res;
}
//record indegree
HashMap<DirectedGraphNode, Integer> map = new HashMap<DirectedGraphNode, Integer>();
for(int i=0; i<graph.size(); i++){
DirectedGraphNode node = graph.get(i);
for(DirectedGraphNode n : node.neighbors){
if(map.containsKey(n)){
map.put(n, map.get(n)+1);
}else{
map.put(n, 1);
}
}
}
//find start point
LinkedList<DirectedGraphNode> queue = new LinkedList<DirectedGraphNode>();
for(int i=0; i<graph.size(); i++){
if(!map.containsKey(graph.get(i))){
res.add(graph.get(i));
queue.offer(graph.get(i));
}
}
//travelsal
while(!queue.isEmpty()){
DirectedGraphNode node = queue.poll();
for(DirectedGraphNode n : node.neighbors){
if(map.containsKey(n)){
map.put(n, map.get(n)-1);
if(map.get(n)==0){
res.add(n);
queue.offer(n);
}
}
}
}
return res;
}
}
- 代码模板示例:
主要是用dfs或bfs去解决。所以模板以dfs和bfs的递归和迭代两种实现方式为主。
- DFS递归:见island那题。注意visited维护。
- DFS迭代:用stack实现。
- BFS递归:一般不用递归实现。
- BFS迭代:用queue实现。
在一个点线组成的矩阵关系上即为一个图的问题。图的问题主要分为三种类型:
- Deep copy: 在图上建立一个新的图,把原图的关系都copy到新图上。
- Union find: 在图上找到有几个union块。
- Topological sort: 根据pair的关系,或者点线的关系,构建出一个图,找出从start到end的path。
图的问题基本都采用 DFS或者BFS的思路进行解决。
- Deep copy:
- 先遍历一遍原图,一边遍历一边建立新的图的点。用hashmap记录原node和新node之间的关系。
- 再复制边的关系。把原图的边的关系,在新的node之间边的关系同样复制
- 以Clone Graph为例,代码如下:
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { if(node == null){ return null; } // write your code here ArrayList<UndirectedGraphNode> list = new ArrayList<UndirectedGraphNode>(); HashMap<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>(); int size = 0; list.add(node); UndirectedGraphNode newHead = new UndirectedGraphNode(node.label); map.put(node, newHead); while(size<list.size()){ for(UndirectedGraphNode n : list.get(size).neighbors){ if(!map.containsKey(n)){ UndirectedGraphNode newNode = new UndirectedGraphNode(n.label); map.put(n, newNode); list.add(n); } } size++; } //copy edge for(int i=0; i<list.size(); i++){ UndirectedGraphNode newN = map.get(list.get(i)); ArrayList<UndirectedGraphNode> newNeighbor = new ArrayList<UndirectedGraphNode>(); for(UndirectedGraphNode neiNode : list.get(i).neighbors){ newNeighbor.add(map.get(neiNode)); } newN.neighbors = newNeighbor; } return newHead; }
- Union find:
- 遍历整个图,一但找到符合条件的点进行dfs,把整个union进行标记,并记录union个数。
- 以Number of Islands为例:
public class Solution { public int numIslands(boolean[][] grid) { if(grid == null || grid.length == 0 || grid[0].length == 0){ return 0; } int res = 0; boolean[][] visited = new boolean[grid.length][grid[0].length]; for(int i=0; i<grid.length; i++){ for(int j=0; j<grid[0].length; j++){ if(grid[i][j] && !visited[i][j]){ dfs(i, j, grid, visited); res++; } } } return res; } private void dfs(int i, int j, boolean[][] grid, boolean[][] visited){ //end condition if(i>=grid.length || i<0 || j>=grid[0].length || j<0 || visited[i][j] || !grid[i][j]){ return; } visited[i][j] = true; dfs(i+1, j, grid, visited); dfs(i-1, j, grid, visited); dfs(i, j+1, grid, visited); dfs(i, j-1, grid, visited); } }
- Topological sort:
- 先用hashMap记录到所有Node的入度,选择入度为0的点为起点。
- 从起点开始进行bfs,每遍历一个点就入度减一,直到入度为0时,把这个点加入结果顺序中。
- 注意点:环的问题,如果图是有环的,则计数器会产生断层,即某个节点的计数器永远无法清零(有环意味着有的节点被多加了1,然而遍历的时候一次只减一个1,所以导致无法归零),这样该节点也无法加入到结果中。所以我们只要判断这个结果的节点数和实际图中节点数相等,就代表无环,不相等,则代表有环。
- 以Topological sort代码示例:
public class Solution { public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) { ArrayList<DirectedGraphNode> res = new ArrayList<DirectedGraphNode>(); if(graph==null || graph.size()==0){ return res; } //record indegree HashMap<DirectedGraphNode, Integer> map = new HashMap<DirectedGraphNode, Integer>(); for(int i=0; i<graph.size(); i++){ DirectedGraphNode node = graph.get(i); for(DirectedGraphNode n : node.neighbors){ if(map.containsKey(n)){ map.put(n, map.get(n)+1); }else{ map.put(n, 1); } } } //find start point LinkedList<DirectedGraphNode> queue = new LinkedList<DirectedGraphNode>(); for(int i=0; i<graph.size(); i++){ if(!map.containsKey(graph.get(i))){ res.add(graph.get(i)); queue.offer(graph.get(i)); } } //travelsal while(!queue.isEmpty()){ DirectedGraphNode node = queue.poll(); for(DirectedGraphNode n : node.neighbors){ if(map.containsKey(n)){ map.put(n, map.get(n)-1); if(map.get(n)==0){ res.add(n); queue.offer(n); } } } } return res; } }
主要是用dfs或bfs去解决。所以模板以dfs和bfs的递归和迭代两种实现方式为主。
- DFS递归:见island那题。注意visited维护。
- DFS迭代:用stack实现。
- BFS递归:一般不用递归实现。
- BFS迭代:用queue实现。