算法設計Week3 LeetCode Algorithms Problem #133 Clone Graph

題目描述:

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
OJ’s undirected graph serialization: Nodes are labeled uniquely. We use # as a separator for each node, and , as a separator for node label and each neighbor of the node. As an example, consider the serialized graph {0,1,2#1,2#2,2}.

The graph has a total of three nodes, and therefore contains three parts as separated by #.

First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
Second node is labeled as 1. Connect node 1 to node 2. Third node is
labeled as 2. Connect node 2 to node 2 (itself), thus forming a
self-cycle. Visually, the graph looks like the following:

   1
  / \
 /   \
0 --- 2
     / \
     \_/

題目中給出的圖的結構體如下:

struct UndirectedGraphNode {
    int label;
    vector<UndirectedGraphNode *> neighbors;
    UndirectedGraphNode(int x) : label(x) {};
};

解法一(BFS):

廣度優先算法(BFS)主要思路是首先看一個節點最多能夠訪問到哪些節點,再依次對這些節點進行訪問和檢索。BFS除了原本的圖之外,還需要用到一個隊列(queue)用於存儲要訪問的節點,以及一個標記節點是否已被訪問的數組。
在本題中,除了要考慮節點是否已訪問,還要考慮原節點和新創建的節點間的對應關係。在後邊出現的節點q也有可能會和前邊出現的節點p有邊,p節點已經被訪問過,應該有對應的新節點p_new,但p_new卻不一定好找。也就是說,我們需要保存一張新舊節點對應關係的“地圖”。所以需要使用map的結構。這裏使用hash map來實現。
實現代碼如下:

class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(!node) return NULL;

        unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> nodeList;
        UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label);
        nodeList[node] = new_node;

        queue<UndirectedGraphNode *> tovisit;
        tovisit.push(node);
        while(!tovisit.empty()){
            UndirectedGraphNode *cur_node = tovisit.front();
            tovisit.pop();
            for(UndirectedGraphNode* neigh : cur_node -> neighbors){
                if(nodeList.find(neigh) == nodeList.end()){
                    UndirectedGraphNode *new_neigh = new UndirectedGraphNode(neigh->label);
                    nodeList[neigh] = new_neigh;
                    tovisit.push(neigh);
                }
                nodeList[cur_node]->neighbors.push_back(nodeList[neigh]);
            }
        }
        return new_node;
    }
};

解法二(DFS):

深度優先遍歷(DFS)基於的想法很簡單,就是探索迷宮需要的“線”和“粉筆”。線用來在探索迷宮走到盡頭時返回出發點,而粉筆則用來標註已經訪問過哪些點。類似地,DFS首先查看一個節點最遠能夠遍歷到哪個節點,然後返回查看。這樣,這個算法需要用到一個棧(stack)用於存儲要訪問的節點(與“線”的作用相同),以及一個標記節點是否已被訪問的數組(與“粉筆”的作用相同)。
函數的遞歸調用是自動以棧的形式實現的,因此可以使用遞歸的方式來快速有效地完成DFS算法:

class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(!node) return NULL;
        UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label);
        nodeList[node] = new_node;
        DFS(node);
        return new_node;
    }
    void DFS(UndirectedGraphNode *cur_node){
        for(UndirectedGraphNode* neigh : cur_node -> neighbors){
            if(nodeList.find(neigh) == nodeList.end()){
                UndirectedGraphNode *new_node = new UndirectedGraphNode(neigh->label);
                nodeList[neigh] = new_node;
                DFS(neigh);
            }
            nodeList[cur_node]->neighbors.push_back(nodeList[neigh]);
        }

    }
private:
    unordered_map<UndirectedGraphNode *, UndirectedGraphNode *> nodeList;
};

Notes:

這裏說一些在編程過程中出現的問題:
1、要注意考慮圖爲空時的情況。
2、要仔細考慮每一步用到的是原節點還是新節點,如果使用的是新節點的話,最好使用哈希表nodeList來獲取,這樣可以保證代碼的正確性。

發佈了26 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章