【每天一道算法题】除法求值

本文首发于我的公众号码农之屋(id: Spider1818),专注于干货分享,包含但不限于Java编程、网络技术、Linux内核及实操、容器技术等。欢迎大家关注,二维码文末可以扫。

 

题目描述

给出方程式 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直接按照代入数字算除法。

  1. 将每个字符映射成集合中的一个节点 比如 [a,c] 他们对应的values的值是 3 ,那我不管你a和c到底是什么,我可以假设你a为3 c为1,只要满足a/c = 3就行了。

  2. 这样我可以给每个字符都设置一个对应的数字,我设的数字只要满足题目给定的所有关系就ok,最后求结果我只要把字符对应的数字代进去直接除就行了。

  3. 但是有个问题,这样搞的话,假设[a,b]的values为2 设置a=2,b=1 [c,d]的values为3 c=3 d=1 那这时候4个字符都有数字了,结果要你求a/c,a/c实际上是无法求出来的,这你如果还直接代进去的话那就GG了。。。

  4. 那什么样的是算不出来的? 两者没有关联关系的,毫不相干的,就无法计算,那判断两个点有没有连接。。。这不就是并查集吗(好吧第一反应是无向图...,对其dfs或者bfs也可判断,但我看别人答案都是并查集,所以直接抄的并查集。。。╮(╯▽╰)╭)

并查集的两个作用:

  1. 查询元素a和元素b是否属于同一组

  2. 合并元素a和元素b所在的组

(好像就这两个作用,没有其他的了,有的话请大佬留言指出)

  1. 接下来根据所有的关联关系equations来构建集合就行,这个构建好的集合(树)就是一个叫并查集的东西

  2. 构建完之后按照假设的数字带入算除法就行,并查集就是用来判断是不是同一个集合用的

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}

 

我的公众号「码农之屋」(id: Spider1818) ,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值