算法设计与分析第三次作业

#leetcode 685.Redundant Connection II

In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.
The given input is a directed graph that started as a rooted tree with N nodes (with distinct values 1, 2, …, N), with one additional directed edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed.
The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] that represents a directed edge connecting nodes u and v, where u is a parent of child v.
Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.
Note:
The size of the input 2D-array will be between 3 and 1000.
Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array.

题目给定一个树再加一条多余的边,要求我们找出这个多余的边,这时我们可以分两种情况,一种情况是有一个顶点入度为2,或者是存在环。

之所以只可能有一个顶点入度为2而不是多个顶点或者是一个顶点入度为3其实很好理解,无论怎么样去掉一条边后这两种情况都还会有一个顶点入度为2或者以上,显然不可能为树,而有多个顶点入度为0也不可能,这种情况不可能通过删除边来变成一个树,所以只可能有一个顶点入度为0或者没有顶点入度为0,这两种情况下不是树的唯一可能就是还存在环。

现在先考虑有一个顶点入度为2的情况,首先我们不难把这条指向此顶点的边找出来,我一开始觉得如果是这种情况的话只要删除后面出现的那个就可以了,但是事实上如果随便删的话剩下的图还有可能存在环,所以其实还是要对剩下的图进行判断。

如果没有顶点入度为2,那么就只需要找环就可以了,首先可以确定只有一个环,因为两个环不可能通过只删除一条边就变成没有环,最后返回环的最后一条边就可以了,这个过程其实可以和上述对剩下的图判断有没有环一起执行,具体代码如下:

class Solution {
public:
    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
    	//tree[i]中存节点i的父节点label
        int tree[edges.size() + 1];
        vector<int> suspect1,suspect2;
        for(int i = 0 ; i <= edges.size(); i++){
        	tree[i] = -1;
        }
        for(int i = 0; i < edges.size(); i++){
        	if(tree[edges[i][1]] != -1){
        		//如果有入度为2的节点,则保存冲突的边,把第一条边保留,第二条边保存后移出图
        		suspect1.push_back(tree[edges[i][1]]);
                suspect1.push_back(edges[i][1]);
        		suspect2 = edges[i];
        		edges[i].clear();
        	}
        	else tree[edges[i][1]] = edges[i][0];
        }
        /*如果出现了入度为2的节点,现在edges中存的是去掉了suspect2的情况,否则为原图,parent数组存当前发现的根节点*/
        int parent[edges.size() + 1];
        //一开始还没进行搜索,默认根节点为自己
        for(int i = 1; i <= edges.size(); i++){
        	parent[i] = i;
        }
        for(int i = 1; i <= edges.size(); i++){
        	//跳过suspect2
        	if(edges[i].empty()) 
        		continue;
        	int u = edges[i][0], v = edges[i][1];
        	int root_u = findRoot(parent,u);
        	//如果去掉suspect2之后仍有环,并且suspect2存在,那么说明需要去掉的边实际为suspect1,否则如果suspect2不存在,则返回对应的边即可*/
        	if(root_u == v){
        		if(suspect1.empty())
        			return edges[i];
        		else return suspect1;
        	}
        	parent[v] = root_u;
        }
        return suspect2;
    }
    //一路向上找最根部的节点,并把沿途的值也赋值为根节点的值,避免重复搜索
    int findRoot(int* parent, int tofind){
    	if(parent[tofind] != tofind){
    		parent[tofind] = findRoot(parent, parent[tofind]);
    	}
    	return parent[tofind];
    }
};

这里需要注意一些点:

1.题目要求如果有多条边,那么返回最后出现的那条,之所以找环过程中一找到就可以确定是最后一条是因为我们已经确定只有一个环了,那么我们可以确定这条边在环里面,那么前面的那些边自然就已经找到了,所以在整个数组里面这条边自然是环的所有边里面最后出现的。

2.之所以要采用一个parent数组来存目前找到的此节点的根节点,是因为如果每次都要一步步向上去寻找根节点会浪费非常多的时间,比如1->2->3,如果我确定了2是1的孩子,那儿么如果3是2的孩子,那么3自然是1的后代,而对于找环来说存储3是2的孩子这个信息并没有直接作用,还不如直接存储3是1的后代,中间的节点并不重要。

#leetcode133.Clone Graph

Given the head of a graph, return a deep copy (clone) of the graph. Each node in the graph contains a label (int) and a list (List[UndirectedGraphNode]) of its neighbors. There is an edge between the given node and each of the nodes in its neighbors.

题目本身并不难理解,就是要深复制一个图,我一开始觉得只要先浅复制一个节点,然后对于邻居节点递归调用深复制函数就好了,但是这样交上去后发现run time error,后面仔细想了后发现这不一定是一个无环图,如果有环的话就会出现递归循环调用的情况,导致RE, 所以其实就针对这一点做一些判断就好了,我采用了一个map来判断某个节点是否已经被产生过,如果已经被产生了就直接用之前产生的节点地址就好了,而不需要再去调用深复制函数,具体代码如下:

/**
 * Definition for undirected graph.
 * struct UndirectedGraphNode {
 *     int label;
 *     vector<UndirectedGraphNode *> neighbors;
 *     UndirectedGraphNode(int x) : label(x) {};
 * };
 */
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
    	if(!node){
    		return nullptr;
    	}
    	UndirectedGraphNode* ans;
    	if(labelMap.count(node->label)){
    		ans = labelMap[node->label];
    		return ans;
    	}
        ans = new UndirectedGraphNode(node->label);
        labelMap[node->label] = ans;
        for(int i = 0; i <(int)node->neighbors.size(); i++){
        	UndirectedGraphNode* tmp = cloneGraph(node->neighbors[i]);
        	if(tmp){
        		ans->neighbors.push_back(tmp);
        	}
        }
        return ans;
    }
private:
	map<int,UndirectedGraphNode*> labelMap;
};

不过这种做法的缺点是在调用完一次之后会有“后遗症”,需要对其中的labelMap进行清空,一种比较可行的做法是再封装一个函数f,让函数f来调用上述函数并清空labelMap即可#leetcode 685.Redundant Connection II

In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) for which all other nodes are descendants of this node, plus every node has exactly one parent, except for the root node which has no parents.
The given input is a directed graph that started as a rooted tree with N nodes (with distinct values 1, 2, …, N), with one additional directed edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed.
The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] that represents a directed edge connecting nodes u and v, where u is a parent of child v.
Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array.
Note:
The size of the input 2D-array will be between 3 and 1000.
Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array.

题目给定一个树再加一条多余的边,要求我们找出这个多余的边,这时我们可以分两种情况,一种情况是有一个顶点入度为2,或者是存在环。

之所以只可能有一个顶点入度为2而不是多个顶点或者是一个顶点入度为3其实很好理解,无论怎么样去掉一条边后这两种情况都还会有一个顶点入度为2或者以上,显然不可能为树,而有多个顶点入度为0也不可能,这种情况不可能通过删除边来变成一个树,所以只可能有一个顶点入度为0或者没有顶点入度为0,这两种情况下不是树的唯一可能就是还存在环。

现在先考虑有一个顶点入度为2的情况,首先我们不难把这条指向此顶点的边找出来,我一开始觉得如果是这种情况的话只要删除后面出现的那个就可以了,但是事实上如果随便删的话剩下的图还有可能存在环,所以其实还是要对剩下的图进行判断。

如果没有顶点入度为2,那么就只需要找环就可以了,首先可以确定只有一个环,因为两个环不可能通过只删除一条边就变成没有环,最后返回环的最后一条边就可以了,这个过程其实可以和上述对剩下的图判断有没有环一起执行,具体代码如下:

class Solution {
public:
    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
    	//tree[i]中存节点i的父节点label
        int tree[edges.size() + 1];
        vector<int> suspect1,suspect2;
        for(int i = 0 ; i <= edges.size(); i++){
        	tree[i] = -1;
        }
        for(int i = 0; i < edges.size(); i++){
        	if(tree[edges[i][1]] != -1){
        		//如果有入度为2的节点,则保存冲突的边,把第一条边保留,第二条边保存后移出图
        		suspect1.push_back(tree[edges[i][1]]);
                suspect1.push_back(edges[i][1]);
        		suspect2 = edges[i];
        		edges[i].clear();
        	}
        	else tree[edges[i][1]] = edges[i][0];
        }
        /*如果出现了入度为2的节点,现在edges中存的是去掉了suspect2的情况,否则为原图,parent数组存当前发现的根节点*/
        int parent[edges.size() + 1];
        //一开始还没进行搜索,默认根节点为自己
        for(int i = 1; i <= edges.size(); i++){
        	parent[i] = i;
        }
        for(int i = 1; i <= edges.size(); i++){
        	//跳过suspect2
        	if(edges[i].empty()) 
        		continue;
        	int u = edges[i][0], v = edges[i][1];
        	int root_u = findRoot(parent,u);
        	//如果去掉suspect2之后仍有环,并且suspect2存在,那么说明需要去掉的边实际为suspect1,否则如果suspect2不存在,则返回对应的边即可*/
        	if(root_u == v){
        		if(suspect1.empty())
        			return edges[i];
        		else return suspect1;
        	}
        	parent[v] = root_u;
        }
        return suspect2;
    }
    //一路向上找最根部的节点,并把沿途的值也赋值为根节点的值,避免重复搜索
    int findRoot(int* parent, int tofind){
    	if(parent[tofind] != tofind){
    		parent[tofind] = findRoot(parent, parent[tofind]);
    	}
    	return parent[tofind];
    }
};

这里需要注意一些点:

1.题目要求如果有多条边,那么返回最后出现的那条,之所以找环过程中一找到就可以确定是最后一条是因为我们已经确定只有一个环了,那么我们可以确定这条边在环里面,那么前面的那些边自然就已经找到了,所以在整个数组里面这条边自然是环的所有边里面最后出现的。

2.之所以要采用一个parent数组来存目前找到的此节点的根节点,是因为如果每次都要一步步向上去寻找根节点会浪费非常多的时间,比如1->2->3,如果我确定了2是1的孩子,那儿么如果3是2的孩子,那么3自然是1的后代,而对于找环来说存储3是2的孩子这个信息并没有直接作用,还不如直接存储3是1的后代,中间的节点并不重要。

#leetcode133.Clone Graph

Given the head of a graph, return a deep copy (clone) of the graph. Each node in the graph contains a label (int) and a list (List[UndirectedGraphNode]) of its neighbors. There is an edge between the given node and each of the nodes in its neighbors.

题目本身并不难理解,就是要深复制一个图,我一开始觉得只要先浅复制一个节点,然后对于邻居节点递归调用深复制函数就好了,但是这样交上去后发现run time error,后面仔细想了后发现这不一定是一个无环图,如果有环的话就会出现递归循环调用的情况,导致RE, 所以其实就针对这一点做一些判断就好了,我采用了一个map来判断某个节点是否已经被产生过,如果已经被产生了就直接用之前产生的节点地址就好了,而不需要再去调用深复制函数,具体代码如下:

/**
 * Definition for undirected graph.
 * struct UndirectedGraphNode {
 *     int label;
 *     vector<UndirectedGraphNode *> neighbors;
 *     UndirectedGraphNode(int x) : label(x) {};
 * };
 */
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
    	if(!node){
    		return nullptr;
    	}
    	UndirectedGraphNode* ans;
    	if(labelMap.count(node->label)){
    		ans = labelMap[node->label];
    		return ans;
    	}
        ans = new UndirectedGraphNode(node->label);
        labelMap[node->label] = ans;
        for(int i = 0; i <(int)node->neighbors.size(); i++){
        	UndirectedGraphNode* tmp = cloneGraph(node->neighbors[i]);
        	if(tmp){
        		ans->neighbors.push_back(tmp);
        	}
        }
        return ans;
    }
private:
	map<int,UndirectedGraphNode*> labelMap;
};

不过这种做法的缺点是在调用完一次之后会有“后遗症”,需要对其中的labelMap进行清空,一种比较可行的做法是再封装一个函数f,让函数f来调用上述函数并清空labelMap即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值