无向图 Undirected Graph
133. Clone Graph
第一种方法是两步走,用BFS,第一步先copy所有的点,新老映射关系存在一个map里面,第二步是copy所有边,每次拿点出来就直接从map里面拿就行,因为我们copy的新点都是新建过的,不需要再建了,再建肯定不对了。
注意这是无向图,需要一个set来判断是否访问过,不然就死循环了。
参考了下论坛讨论之后,不太同意大多数解法,因为假设所有点的 label 唯一不是很适合 generalize 这个算法。用 HashMap<> 实现 Node - Node mapping 比较靠谱。
public class Solution {
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
if (node == null) return null;
List<UndirectedGraphNode> graph = getNodes(node);
Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<>();
for (UndirectedGraphNode n : graph) {
map.put(n, new UndirectedGraphNode(n.label));
}
for (UndirectedGraphNode n : graph) {
UndirectedGraphNode newNode = map.get(n);
for (UndirectedGraphNode neighbor : n.neighbors) {
UndirectedGraphNode newNeighbor = map.get(neighbor);
newNode.neighbors.add(newNeighbor);
}
}
return map.get(node);
}
private List<UndirectedGraphNode> getNodes(UndirectedGraphNode node) {
Queue<UndirectedGraphNode> queue = new LinkedList<>();
Set<UndirectedGraphNode> set = new HashSet<>();
queue.offer(node);
set.add(node);
while (!queue.isEmpty()) {
UndirectedGraphNode head = queue.poll();
for (UndirectedGraphNode neighbor : head.neighbors) {
if (!set.contains(neighbor)) {
queue.offer(neighbor);
set.add(neighbor);
}
}
}
return new ArrayList<UndirectedGraphNode>(set);
}
}
7/17更新:重新写这个的代码和之前完全不同,不理解我以前为啥非要用set,一个map多简单就做完了。
啊我想起来了,是因为九章算法的答案是上面这么写的,解法有点蠢。
public class Solution {
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
if (node == null) return null;
Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<>();
Queue<UndirectedGraphNode> queue = new LinkedList<>();
queue.offer(node);
while (!queue.isEmpty()) {
UndirectedGraphNode tmp = queue.poll();
if (!map.containsKey(tmp)) {
UndirectedGraphNode newNode = new UndirectedGraphNode(tmp.label);
map.put(tmp, newNode);
for (UndirectedGraphNode curt : tmp.neighbors) {
queue.offer(curt);
}
}
}
for (Map.Entry<UndirectedGraphNode, UndirectedGraphNode> entry : map.entrySet()) {
UndirectedGraphNode old = entry.getKey();
UndirectedGraphNode nee = entry.getValue();
List<UndirectedGraphNode> list = old.neighbors;
for (UndirectedGraphNode tmp : list) {
nee.neighbors.add(map.get(tmp));
}
}
return map.get(node);
}
}
310. Minimum Height Trees
dietpepsi 添加的题目。
在这个 graph 里,我们要找的,其实也是一层一层剥开最外围 nodes 之后,最里面的点。
因此,即使是 undirected graph,degree 也是非常重要的信息,只不过对于 undirected graph 来讲:
degree = "in"-degree + "out"-degree 而已,当然这个也是我们自己定义的,想怎么定义都是可以的,就看最后是否能通过BFS做出来就行。
这个题和 course schedule 的BFS解法相差无几,只不过我们层层剥开之后,每一层都可能是最后要返回的答案,所以每一层都要新建list保存下来,下一层再覆盖。
public class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> result = new ArrayList<>();
if (edges == null || edges.length == 0) {
result.add(0);
return result;
}
int[] degree = new int[n];
Map<Integer, List<Integer>> map = new HashMap<>();
for (int[] num : edges) {
int node1 = num[0];
int node2 = num[1];
addNode(map, node1, node2);
addNode(map, node2, node1);
degree[node1]++;
degree[node2]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; ++i) {
if (degree[i] <= 1) queue.offer(i);
}
while (!queue.isEmpty()) {
int size = queue.size();
result = new ArrayList<>();
for (int i = 0; i < size; ++i) {
int cur = queue.poll();
result.add(cur);
for (int j = 0; j < map.get(cur).size(); ++j) {
int next = map.get(cur).get(j);
degree[next]--;
if (degree[next] == 1) queue.offer(next);
}
}
}
return result;
}
private void addNode(Map<Integer, List<Integer>> map, int node1, int node2) {
if (map.containsKey(node1)) {
List<Integer> list = map.get(node1);
list.add(node2);
map.put(node1, list);
} else {
List<Integer> list = new ArrayList<>();
list.add(node2);
map.put(node1, list);
}
}
}
261. Graph Valid Tree
这道题也不难,和 course schedule 的区别只在有向和无向之间。
对于有向图,我们不能随便挑一个起始点,必须从头开始,不然前面就遍历不到了,同样是找环的操作,无向图就可以从任意点开始。
public class Solution {
public boolean validTree(int n, int[][] edges) {
int[] state = new int[n];
Map<Integer, List<Integer>> map = new HashMap<>();
for (int[] num : edges){
int node1 = num[0];
int node2 = num[1];
addNode(map, node1, node2);
addNode(map, node2, node1);
}
Queue<Integer> queue = new LinkedList<>();
queue.offer(0);
state[0] = 1;
int count = 0;
while (!queue.isEmpty()) {
int cur = queue.poll();
count++;
if (map.containsKey(cur)) {
for (int i = 0; i < map.get(cur).size(); ++i) {
int next = map.get(cur).get(i);
if (state[next] == 1) return false;
if (state[next] == 0) {
state[next] = 1;
queue.offer(next);
}
}
}
state[cur] = 2;
}
return count == n;
}
private void addNode(Map<Integer, List<Integer>> map, int node1, int node2) {
if (map.containsKey(node1)) {
List<Integer> list = map.get(node1);
list.add(node2);
map.put(node1, list);
} else {
List<Integer> list = new ArrayList<>();
list.add(node2);
map.put(node1, list);
}
}
}
07/18更新:试图用今天的方法做,但是不行,无向图不能用这种方法做,这种方法只适合有向图,因为有向图没有环的时候,无向图是认为有环的。
public class Solution {
public boolean validTree(int n, int[][] edges) {
int[] degree = new int[n];
Set<Integer> set = new HashSet<>();
for (int i = 0; i < edges.length; ++i) {
degree[edges[i][0]]++;
}
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < degree.length; ++i) {
if (degree[i] == 0) {
queue.offer(i);
set.add(i);
}
}
while (!queue.isEmpty()) {
int node = queue.poll();
for (int i = 0; i < edges.length; ++i) {
if (edges[i][1] == node && !set.contains(edges[i][0])) {
degree[edges[i][0]]--;
if (degree[edges[i][0]] == 0) {
queue.offer(edges[i][0]);
set.add(edges[i][0]);
}
}
}
}
for (int i = 0; i < degree.length; ++i) {
if (degree[i] != 0) return false;
}
return true;
}
}