记录 1 道算法题
冗余连接 ii
685. 冗余连接 II - 力扣(LeetCode) (leetcode-cn.com)
从一个根节点出发连接子节点,在连接中添加一个边,这条边会造成环或者冲突,冲突指子节点存在两个父节点。找出这条边,并返回。
自身是一个满足树状结构的连接,那么多出来的边要么是子节点指向其他子节点或者指向根节点。当指向根节点的时候一定会形成一个环。删除形成环的边就可以。当指向其他节点的时候,可能会指向一个有父元素的节点,同时也可能形成一个环。比如下面这张图,[3,5] 形成了一个环,并且让 3 有了两个父节点。这时候要先处理形成父节点的边。一般来说添加一条边,环和冲突只会出现一个,当同时出现的时候,优先解决冲突。而且要解决形成环的那个冲突。
首先让边进行连线。将冲突的边和形成环的边记录下来。使用并查集进行连接,这样能找到形成冲突的在环内的边。由于是树状结构,有明确的父节点,所以不能使用路径压缩,于是还需要有一个数组来记录实际画上的连接。
有 n 个节点就有 n - 1 条边。
function findRedundantDirectedConnection(edges) {
const n = edges.length
const parent = Array.from(new Array(n + 1), (_, i) => i)
const link = Array.from(new Array(n + 1), (_, i) => i)
let conflict,circle
for(let i = 0; i < n; i++) {
const [a, b] = edges[i]
let pIdx = link[b]
// 如果有两个父节点
// 没有冲突就有环
if (child !== b) {
conflict = pIdx
} else {
// 连接
link[b] = a
// 比较并查集根节点
pIdx = find(parent, a)
const childIdx = find(parent, b)
if (pIdx === childIdx) {
circle = i
} else {
// 并入
parent[childIdx] = pIdx
}
}
}
if (conflict === undefined) {
// 只有冲突的时候,去掉环就可以
return edges[circle]
} else {
// 有环又有冲突的时候,还要再判断
if (circle === undefined) {
return edges[conflict]
} else {
// 又有环又有冲突的时候,
// 要找在环内的造成冲突的边,
// 即从子节点,找连接他的两个父节点的边,
// 哪一条边在环内。
// 如果冲突了就不会进行 link,所以当冲突的边在环内的时候,
// 一定是已经 link 造成的,所以可以直接 link[b],找到那个父节点。
const [a, b] = edges[conflict]
return [link[b], b]
}
}
}
function find(parent, i) {
if (parent[i] !== i) {
parent[i] = find(parent, parent[i])
}
return parent[i]
}