133. 克隆圖

知識點:圖;遞迴;BFS

題目描述

給你無向 連通 圖中一個節點的引用,請你返回該圖的 深拷貝(克隆)。

圖中的每個節點都包含它的值 val(int) 和其鄰居的列表(list[Node])。

  1. class Node {
  2. public int val;
  3. public List<Node> neighbors;
  4. }
示例

  1. 輸入:adjList = [[2,4],[1,3],[2,4],[1,3]]
  2. 輸出:[[2,4],[1,3],[2,4],[1,3]]
  3. 解釋:
  4. 圖中有 4 個節點。
  5. 節點 1 的值是 1,它有兩個鄰居:節點 2 4
  6. 節點 2 的值是 2,它有兩個鄰居:節點 1 3
  7. 節點 3 的值是 3,它有兩個鄰居:節點 2 4
  8. 節點 4 的值是 4,它有兩個鄰居:節點 1 3
  9. 輸入:adjList = [[]]
  10. 輸出:[[]]
  11. 解釋:輸入包含一個空列表。該圖僅僅只有一個值為 1 的節點,它沒有任何鄰居。
  12. 輸入:adjList = []
  13. 輸出:[]
  14. 解釋:這個圖是空的,它不含任何節點。
  15. 輸入:adjList = [[2],[1]]
  16. 輸出:[[2],[1]]

解法一:深度優先(DFS)

圖的深拷貝是在做什麼,對於一張圖而言,它的深拷貝即構建一張與原圖結構,值均一樣的圖,但是其中的節點不再是原來圖節點的引用。

所以需要進行圖的遍歷,圖的遍歷有兩種方法:DFS和BFS,為了避免陷入死迴圈,需要定義一個結構來儲存我們已經遍歷過的節點,不然從1能到2,到2後又會回到1,所以使用雜湊表來儲存遍歷到的節點,key是遍歷到的節點,value是建立的克隆節點,如果遍歷過,那直接返回建立的克隆節點。

函式功能:克隆圖,其實就是建立節點,然後填充好neighbors。

1.終止條件: node==null, 直接返回node;

2.該做什麼:就是建立克隆節點,然後填滿鄰居節點;所以首先判斷有沒有在map裡,有了的話證明來過了,直接返回克隆的節點就可以,沒有的話就建立節點,並且將其放入map中,然後就該填充鄰居節點了,遞迴呼叫。

3.什麼時候做,先建立節點,然後填充呼叫,先序。

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public int val;
  5. public List<Node> neighbors;
  6. public Node() {
  7. val = 0;
  8. neighbors = new ArrayList<Node>();
  9. }
  10. public Node(int _val) {
  11. val = _val;
  12. neighbors = new ArrayList<Node>();
  13. }
  14. public Node(int _val, ArrayList<Node> _neighbors) {
  15. val = _val;
  16. neighbors = _neighbors;
  17. }
  18. }
  19. */
  20. class Solution {
  21. Map<Node, Node> vis = new HashMap<>();
  22. public Node cloneGraph(Node node) {
  23. if(node == null) return null;
  24. if(vis.containsKey(node)){
  25. return vis.get(node); //訪問過了就從表裡直接取出克隆的節點;
  26. }
  27. Node cloneNode = new Node(node.val, new ArrayList());
  28. //建立之後放到雜湊表裡;
  29. vis.put(node, cloneNode);
  30. //遍歷鄰居並更新;
  31. for(Node neighbor : node.neighbors){
  32. cloneNode.neighbors.add(cloneGraph(neighbor)); //注意鄰居節點是克節點;
  33. }
  34. return cloneNode;
  35. }
  36. }

解法二:廣度優先(BFS)

和深度一樣需要有個map來判斷是否遍歷過了,使用BFS,建立一個佇列,然後將各節點依次入隊。入隊頭節點,然後取出,遍歷出隊的鄰居節點,如果沒有被訪問過,那就入隊,克隆並且新增到map中,如果訪問過了,那就更新克隆節點的鄰居節點就可以了。

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public int val;
  5. public List<Node> neighbors;
  6. public Node() {
  7. val = 0;
  8. neighbors = new ArrayList<Node>();
  9. }
  10. public Node(int _val) {
  11. val = _val;
  12. neighbors = new ArrayList<Node>();
  13. }
  14. public Node(int _val, ArrayList<Node> _neighbors) {
  15. val = _val;
  16. neighbors = _neighbors;
  17. }
  18. }
  19. */
  20. class Solution {
  21. public Node cloneGraph(Node node) {
  22. Map<Node, Node> vis = new HashMap<>();
  23. if(node == null) return null;
  24. Queue<Node> queue = new LinkedList<>();
  25. queue.add(node); //首節點入隊;
  26. vis.put(node, new Node(node.val, new ArrayList())); //克隆節點併入表;
  27. while(!queue.isEmpty()){
  28. Node head = queue.poll();
  29. for(Node neighbor : head.neighbors){
  30. if(!vis.containsKey(neighbor)){
  31. vis.put(neighbor, new Node(neighbor.val, new ArrayList()));
  32. queue.add(neighbor); //依次設定訪問過併入隊;
  33. }
  34. vis.get(head).neighbors.add(vis.get(neighbor)); //新增鄰居,注意是新增的克隆的;
  35. }
  36. }
  37. return vis.get(node);
  38. }
  39. }

相關連結

克隆圖