Leetcode 685
我写的代码比较丑陋,等会再放上来。
在树中添加一条附加边,一共会有以下几种情况发生:
第一种,只有环。这种情况下,把导致环路的边删除即可。由于附加边只有一条,所以环也只能有一个,所以发现环路时,立刻记下当时处理的边。
第二种,有一个节点存在两个父节点,但是没有环路存在。此时删除后来的那个父节点对应那条边即可。
第三种,既有环路,又有冲突。此时需要找出哪个父节点存在于环路中,并将对应边删除。
注意图示只是一个简单的示例,你应该意识到某个节点还可以向外延伸出各种边。重点在于,我们需要记录【冲突的两条边】和【导致环路的一条边】。
同时,因为树的父节点只能有一个,所以【导致冲突的边】是不被记录到树结构上的。
具体到写代码,我们使用并查集。记父节点数组为 father,当前处理的边为 edges[i],边的两边节点为 <from, to>。
>>> 当 Find(from) == to,我们认为这条边导致了环,记下当前边的 i 为 cycleIndex。
>>> 当 Find(to) != to,我们认为 <from, to> 导致了冲突,并记下当前边的 i 为 conflictIndex。
>>> 如果都不符合,正常地连接两个节点,father[to] = from。
经过以上处理,我们就得到了【冲突的两条边】和【导致环路的一条边】。
如果是第一种或者第二种情况,事情就好办多了。关键是,既有冲突又有环的情况下,如何才能知道冲突的两条边谁在环上呢?
我自己采取的办法是,从两条边选出一条,然后递归地求其父节点。
过程中,如果求到了根节点,那么这条边就不必删除。
否则,一定会求到冲突的子节点(也就是上图中的 B 节点)。那么这条边就需要被删除。
int p = edges[conflictIndex][0]; bool isConflictEdge = false; while(father[p] != -1){ // 我个人用-1代表根节点 p = father[p]; if(p == edges[conflictIndex][1]){ isConflictEdge = true; break; } }
最后,代码的话还是建议看官方题解的代码。