题目描述
给出方程式 A / B = k, 其中 A 和 B 均为代表字符串的变量, k 是一个浮点型数字。根据已知方程式求解问题,并返回计算结果。如果结果不存在,则返回 -1.0。
示例 :
给定 a / b = 2.0, b / c = 3.0
问题: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
返回 [6.0, 0.5, -1.0, 1.0, -1.0 ]
输入为: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries(方程式,方程式结果,问题方程式), 其中 equations.size() == values.size(),即方程式的长度与方程式结果长度相等(程式与结果一一对应),并且结果值均为正数。以上为方程式的描述。返回vector<double>类型。
基于上述例子,输入如下:
equations(方程式) = [ ["a", "b"], ["b", "c"] ],
values(方程式结果) = [2.0, 3.0],
queries(问题方程式) = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].
输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。
解决思路
思路:
将每一个字符都设置一个对应的数字,再用并查集合并有关联关系的集合,最后res直接按照代入数字算除法。
- 将每个字符映射成集合中的一个节点 比如 [a,c] 他们对应的values的值是 3 ,那我不管你a和c到底是什么,我可以假设你a为3 c为1,只要满足a/c = 3就行了。
- 这样我可以给每个字符都设置一个对应的数字,我设的数字只要满足题目给定的所有关系就ok,最后求结果我只要把字符对应的数字代进去直接除就行了。
- 但是有个问题,这样搞的话,假设[a,b]的values为2 设置a=2,b=1 [c,d]的values为3 c=3 d=1 那这时候4个字符都有数字了,结果要你求a/c,a/c实际上是无法求出来的,这你如果还直接代进去的话那就GG了。。。
- 那什么样的是算不出来的? 两者没有关联关系的,毫不相干的,就无法计算,那判断两个点有没有连接。。。这不就是并查集吗(好吧第一反应是无向图...,对其dfs或者bfs也可判断,但我看别人答案都是并查集,所以直接抄的并查集。。。╮(╯▽╰)╭)
并查集的两个作用:
- 查询元素a和元素b是否属于同一组
- 合并元素a和元素b所在的组
(好像就这两个作用,没有其他的了,有的话请大佬留言指出)
- 接下来根据所有的关联关系equations来构建集合就行,这个构建好的集合(树)就是一个叫并查集的东西
- 构建完之后按照假设的数字带入算除法就行,并查集就是用来判断是不是同一个集合用的
Go代码实现
//这里用struct来表示并查集的节点
type Node struct{
Value float64
Parent *Node
}
//初始化Node函数
func NewNode(value float64) *Node {
node := &Node{
Value:value,
}
node.Parent = node
return node
}
//万年不变的并查集特性之一findParent函数
func findParent(node *Node) *Node {
if node == node.Parent {
return node
}
node.Parent = findParent(node.Parent)
return node.Parent
}
//合并两棵树(或者说两个集合)为一棵树
func union(node1,node2 *Node, num float64, maps map[string]*Node){
p1,p2 := findParent(node1),findParent(node2)
if p1 != p2 { //当两个点不在同一个集合的时候才需要合并
//把一颗子树挂到另一棵树的时候,要把挂上去的树乘以一个和父树的比率
//这个比率要保证两棵树的所有节点的value相除都是正确的结果
//比如A树中 a/b = 3 其中 a初始化为3,b初始化为1
//B树中 c/d = 5 其中c为5,d为1
//这里设node1为A树节点,node2为B树节点,将A树挂到B树
ratio := node2.Value * num / node1.Value
//将A树所有节点整体扩大,这样A树里面的所有除法结果依然不变,并且能兼容B树的数字
for k,v := range maps { //找出所有A树上的节点,怎么找?同一个父亲就是同一个树(集合)里面的
if findParent(v) == p1 {
maps[k].Value *= ratio
}
}
//挂到p2上
p1.Parent = p2
}
}
func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
res := make([]float64,0)
maps := make(map[string]*Node)
for k,v := range equations {
v1,ok1 := maps[v[0]]
v2,ok2 := maps[v[1]]
if !ok1 && !ok2 {
//两个之前都没有出现过
p1,p2 := NewNode(values[k]),NewNode(1)
maps[v[0]],maps[v[1]] = p1,p2
p1.Parent = p2 //组成树
} else if !ok1 {
//ok1没有但是ok2有,把v1挂上去
p2 := findParent(v2)
//v1 的 值该设多少? v1 / v2 = k v1 = v2 * k
p1 := NewNode(v2.Value * values[k])
maps[v[0]] = p1
p1.Parent = p2
} else if !ok2 {
//ok1有 ok2没有,把ok2挂上去
p1 := findParent(v1)
//v1/v2 = k v2 = v1/k
p2 := NewNode(v1.Value / values[k])
maps[v[1]] = p2
p2.Parent = p1
} else {
//两个都有,合并
union(v1,v2,values[k],maps)
}
}
//合并完并查集之后开始查找
for _,v := range queries {
v1,ok1 := maps[v[0]]
v2,ok2 := maps[v[1]]
if ok1 && ok2 && findParent(v1) == findParent(v2) {
res = append(res,v1.Value/v2.Value)
} else {
res = append(res,-1.0)
}
}
return res
}