除法求值——带权并查集

192 阅读4分钟

image.png

图二:

image.png 因为from不管通过哪条路径相对于根节点t的权值都是不会变的所以可以得出
w[from] * w[f] == val * w[to]

`

`

方法 —— 带权并查集:

因为元素与元素之间存在除法关系,所以可以用并查集来处理元素之间的联系,但又因为使除法,所以元素自身带有权值,所以在使用并查集时,应将权值关系所考虑进去

  1. 使用map将字符串映射为数字
  2. 因为元素之间存在权值关系,所以我们在使用fm切片进行表示各个元素的父节点是谁的时候,也要使用w切片来表示各个元素与根节点的权值关系
  3. fm,w两个切片分配空间,其中他们的下标所表示的就是各个元素,其长度就是元素的种类即len(id)
  4. 初始化两个切片,初始化后的结果就是各个元素的父节点是自己,即并查集的原始状态,各个元素没有联系,因为都指向自己,那么权值自然都是默认的1
  5. find函数返回当前节点的根节点,如何判断是根节点即其父节点是自身
  6. 如果当前节点不是根节点,则递归查找,直到找到其根节点f,在递归的同时我们还要维护当前节点的权值,因为我们w切片中保存的只是当前节点相对于父节点的权值,我们需要的是当前节点相对于根节点的权值
  7. 如何更新w,自然是在我们递归find的时候,当我们递归到最深处往回返的时候, w[index] *= w[fm[index]]使路径上的所有w节点得到更新,使它们都更新为相对于根节点的权值,因为是往回返的时候更新,所以代码自然写在 f := find(fm[index])递归代码的后面
  8. merge函数则是通过除法关系来更新fm,w两个并查集切片工具
  9. 更新自然是使from的根节点的父节点变为to的根节点,从而更新fm
  10. 以及通过除法的结果来更新from根节点的相对于to根节点的权值,计算方法我们可以通过图二得出w[f] = val * w[to] / w[from] ,从而更新w
  11. 然后我们只需要通过遍历equations得到各个元素之间的除法关系,然后传入merge中,对fm,w两个切片进行初步更新,也许它还没有到达最终我们想要的结果,但这无关紧要,因为我们在每次使用find函数时,都会为我们更新这两个切片
  12. 最后我们生成我们的答案切片ans,长度自然就是问题queries的长度
  13. 最后一个for循环,将我们的问题元素拿出来,首先判断问题元素是否在我们的id map中出现过,如果没有出现过那我们自然也无法求值,将相应的ans置为-1,如果两个元素的根节点不是同一个,那么说明这两个元素之间没有关系,我们自然也无法求值
  14. 如果以上情况都没有出现,那么我们只需要ans[i] = w[a] / w[b],因为我们w中存放的正是相对于根节点的权值,可以之间相除
  15. 有没有可能w中存放的不是相对于根节点权值,当然不可能,因为我们在相除之前就使用过find函数,我们的w会自动更新。
func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
    //给方程组中的每个变量编号
    id := make(map[string]int)
    for _,v := range equations {
        a, b := v[0],v[1]
        if _,has := id[a]; !has {
            id[a] = len(id)
        }
        if _,has := id[b]; !has {
            id[b] = len(id)
        }
    }
    fm := make([]int,len(id))
    w := make([]float64,len(id))
    for i := range fm {
        fm[i] = i
        w[i] = 1
    }

    var find func (int) int 
    find = func (index int) int {
        if fm[index] != index {
            f := find(fm[index])
            w[index] *= w[fm[index]]
            fm[index] = f
        }
        return fm[index]
    }
    merge := func (from, to int, val float64) {
        f,t := find(from),find(to)
        fm[f] = t
        w[f] = val * w[to] / w[from] 
    }
    for i,v := range equations {
        merge(id[v[0]],id[v[1]],values[i])
    }
    ans := make([]float64, len(queries))
    for i,v := range queries {
        a, has1 := id[v[0]] 
        b, has2 := id[v[1]]
        if has1 && has2 && find(a) == find(b) {
            ans[i] = w[a] / w[b]
        }else {
            ans[i] = -1
        }
    }
    return ans
}