leetcode - 133. Clone Graph

算法系列博客之DFS

DFS(深度优先搜索)在图论的范畴里可以通俗的解释为
总是优先向相连接未访问的下一节点搜索,直到没有这样的节点即返回上一节点
DFS标准格式:

Input: G = (V, E) is a Graph

procedure dfs(G):
    for all v∈V:
        visited(v) = false
    for all v∈V:
        if not visited(v): explore(G,v)

procedure explore(G,v):
    visited(v) = true
    previsit(v)
    for each edge (v,u)∈E:
        if not visited(v): explore(G,u)
    postvisit(v)

本篇博客将运用这种思想来解决leetcode上133号问题


问题描述:

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
The definition for undirected graph in c++:
struct UndirectedGraphNode {
   int label;
   vector < UndirectedGraphNode *> neighbors;
   UndirectedGraphNode(int x) : label(x) {};
};

The input is a pointer that points to a UndirectedGraphNode, your output type should be the same with input.

假设传进的节点为node,声明保存结果的节点为newNode,并将其作为两个参数值
算法设计应当确保在整个过程中node对应原图节点和newNode对应新图节点是对应的
采取DFS的思想,算法步骤如下:
1.检查第一个节点是否为空,不为空则创建一个新节点来作为输出,并将其label赋为第一个节点的label值;为空则返回NULL
2.对于每个节点,遍历其邻接表,并检查下一节点是否访问过
       如果访问过,则直接将该节点的地址push到对应新节点的邻接表中去
       如果没有,则声明一个新的节点,并且对这个节点进行递归DFS

可以看出,需要额外保存已经访问过的节点以及这些节点的地址,为了查询和保存简单,我们采取STL中map形式的数据结构
此外注意到label是唯一的,因此我们只需要用label即可标识某个节点是否已经被访问过。c++实现如下:

class Solution {
public:
    UndirectedGraphNode* cloneGraph(UndirectedGraphNode *node) {
        UndirectedGraphNode *newNode = NULL;
        if (node != NULL) {
            newNode = new UndirectedGraphNode(node->label);
            dfs(node, newNode);
        }
        return newNode;
    }
private:
    map<int, UndirectedGraphNode*> visited;
    void dfs(UndirectedGraphNode *node, UndirectedGraphNode *newNode) {
        visited[node->label] = newNode;

        for(int i = 0; i < node->neighbors.size(); i++) {
            if(visited.find(node->neighbors[i]->label) == visited.end()) {
                newNode->neighbors.push_back(new UndirectedGraphNode(node->neighbors[i]->label));
                dfs(node->neighbors[i], newNode->neighbors[i]);
            } else 
                newNode->neighbors.push_back(visited[node->neighbors[i]->label]);
        }
    }
};

算法复杂度的分析,时间上来说每个节点会且只会被访问一次,复杂度为O(n);
空间上来说,运行下来之后会保存每个节点label和地址,并且只保存一次,也为O(n)
思考一下,时间上无法再优化,因为深拷贝必然会访问到每个节点,而空间上的优化必然会带来时间上的消耗
况且空间的使用也并不是很大,所以看起来算法无需在进行优化。

但是仔细一看,就会发现写出的代码里面似乎是做了很多类似重复的语句,并且指针使用时候链构太长也是一种消耗,还是有改进的空间

class Solution {
public:
    UndirectedGraphNode* cloneGraph(UndirectedGraphNode *node) {
        if(node == NULL) return NULL;
        return dfs(node);
    }
private:
    map<int, UndirectedGraphNode*> visited;
    UndirectedGraphNode* dfs(UndirectedGraphNode *node) {
        if(visited.find(node->label) == visited.end()) {
            visited[node->label] = new UndirectedGraphNode(node->label);
            for(int i = 0; i < node->neighbors.size(); i++)
                visited[node->label]->neighbors.push_back(dfs(node->neighbors[i]));
        }

        return visited[node->label];
    }
};

虽然这貌似是改变了DFS判定的思想,即已经访问过的无需在进行DFS;但实质上并没有变,即使仍然向下DFS了一层,但也就止于了这一层
因而复杂度并没有发生任何改变,代码却变的优美、可读性更高了,目的达到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值