当前位置: 首页 > news >正文

除法求值[中等]

一、题目

给你一个变量对数组equations和一个实数值数组values作为已知条件,其中equations[i] = [Ai, Bi]values[i]共同表示等式Ai / Bi = values[i]。每个AiBi是一个表示单个变量的字符串。另有一些以数组queries表示的问题,其中queries[j] = [Cj, Dj]表示第j个问题,请你根据已知条件找出Cj / Dj = ?的结果作为答案。返回 所有问题的答案 。如果存在某个无法确定的答案,则用-1.0替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用-1.0替代这个答案。

注意:输入总是有效的。你可以假设除法运算中不会出现除数为0的情况,且不存在任何矛盾的结果。

注意:未在等式列表中出现的变量是未定义的,因此无法确定它们的答案。

示例 1:

输入:equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
输出:[6.00000,0.50000,-1.00000,1.00000,-1.00000]
解释:
条件: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 ]
注意:x是未定义的=> -1.0

示例 2:

输入:equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
输出:[3.75000,0.40000,5.00000,0.20000]

示例 3:

输入:equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]]
输出:[0.50000,2.00000,-1.00000,-1.00000]

1 <= equations.length <= 20
equations[i].length == 2
1 <= Ai.length, Bi.length <= 5
values.length == equations.length
0.0 < values[i] <= 20.0
1 <= queries.length <= 20
queries[i].length == 2
1 <= Cj.length, Dj.length <= 5
Ai,Bi,Cj,Dj由小写英文字母与数字组成

二、代码

广度优先搜索: 我们可以将整个问题建模成一张图:给定图中的一些点(变量),以及某些边的权值(两个变量的比值),试对任意两点(两个变量)求出其路径长(两个变量的比值)。因此,我们首先需要遍历equations数组,找出其中所有不同的字符串,并通过哈希表将每个不同的字符串映射成整数。

在构建完图之后,对于任何一个查询,就可以从起点出发,通过广度优先搜索的方式,不断更新起点与当前点之间的路径长度,直到搜索到终点为止。

class Solution {public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {int nvars = 0;Map<String, Integer> variables = new HashMap<String, Integer>();int n = equations.size();for (int i = 0; i < n; i++) {if (!variables.containsKey(equations.get(i).get(0))) {variables.put(equations.get(i).get(0), nvars++);}if (!variables.containsKey(equations.get(i).get(1))) {variables.put(equations.get(i).get(1), nvars++);}}// 对于每个点,存储其直接连接到的所有点及对应的权值List<Pair>[] edges = new List[nvars];for (int i = 0; i < nvars; i++) {edges[i] = new ArrayList<Pair>();}for (int i = 0; i < n; i++) {int va = variables.get(equations.get(i).get(0)), vb = variables.get(equations.get(i).get(1));edges[va].add(new Pair(vb, values[i]));edges[vb].add(new Pair(va, 1.0 / values[i]));}int queriesCount = queries.size();double[] ret = new double[queriesCount];for (int i = 0; i < queriesCount; i++) {List<String> query = queries.get(i);double result = -1.0;if (variables.containsKey(query.get(0)) && variables.containsKey(query.get(1))) {int ia = variables.get(query.get(0)), ib = variables.get(query.get(1));if (ia == ib) {result = 1.0;} else {Queue<Integer> points = new LinkedList<Integer>();points.offer(ia);double[] ratios = new double[nvars];Arrays.fill(ratios, -1.0);ratios[ia] = 1.0;while (!points.isEmpty() && ratios[ib] < 0) {int x = points.poll();for (Pair pair : edges[x]) {int y = pair.index;double val = pair.value;if (ratios[y] < 0) {ratios[y] = ratios[x] * val;points.offer(y);}}}result = ratios[ib];}}ret[i] = result;}return ret;}
}class Pair {int index;double value;Pair(int index, double value) {this.index = index;this.value = value;}
}

时间复杂度: O(ML+Q⋅(L+M)),其中M为边的数量,Q为询问的数量,L为字符串的平均长度。构建图时,需要处理M条边,每条边都涉及到O(L)的字符串比较;处理查询时,每次查询首先要进行一次O(L)的比较,然后至多遍历O(M)条边。
空间复杂度: O(NL+M),其中N为点的数量,M为边的数量,L为字符串的平均长度。为了将每个字符串映射到整数,需要开辟空间为O(NL)的哈希表;随后,需要花费O(M)的空间存储每条边的权重;处理查询时,还需要O(N)的空间维护访问队列。最终,总的复杂度为O(NL+M+N)=O(NL+M)

【2】Floyd 算法: 对于查询数量很多的情形,如果为每次查询都独立搜索一次,则效率会变低。为此,我们不妨对图先做一定的预处理,随后就可以在较短的时间内回答每个查询。在本题中,我们可以使用Floyd算法,预先计算出任意两点之间的距离。

class Solution {public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {int nvars = 0;Map<String, Integer> variables = new HashMap<String, Integer>();int n = equations.size();for (int i = 0; i < n; i++) {if (!variables.containsKey(equations.get(i).get(0))) {variables.put(equations.get(i).get(0), nvars++);}if (!variables.containsKey(equations.get(i).get(1))) {variables.put(equations.get(i).get(1), nvars++);}}double[][] graph = new double[nvars][nvars];for (int i = 0; i < nvars; i++) {Arrays.fill(graph[i], -1.0);}for (int i = 0; i < n; i++) {int va = variables.get(equations.get(i).get(0)), vb = variables.get(equations.get(i).get(1));graph[va][vb] = values[i];graph[vb][va] = 1.0 / values[i];}for (int k = 0; k < nvars; k++) {for (int i = 0; i < nvars; i++) {for (int j = 0; j < nvars; j++) {if (graph[i][k] > 0 && graph[k][j] > 0) {graph[i][j] = graph[i][k] * graph[k][j];}}}}int queriesCount = queries.size();double[] ret = new double[queriesCount];for (int i = 0; i < queriesCount; i++) {List<String> query = queries.get(i);double result = -1.0;if (variables.containsKey(query.get(0)) && variables.containsKey(query.get(1))) {int ia = variables.get(query.get(0)), ib = variables.get(query.get(1));if (graph[ia][ib] > 0) {result = graph[ia][ib];}}ret[i] = result;}return ret;}
}

时间复杂度: O(ML+N3+QL)。构建图需要O(ML)的时间;Floyd算法需要O(N^3)的时间;处理查询时,单次查询只需要O(L)的字符串比较以及常数时间的额外操作。
空间复杂度: O(NL+N^2)

http://www.lryc.cn/news/260763.html

相关文章:

  • 新时代商业市场:AR技术的挑战与机遇并存
  • RHEL8中ansible的使用
  • 【1.6计算机组成与体系结构】存储系统
  • TCP/UDP 协议
  • 如何正确理解和使用 Golang 中 nil ?
  • IDEA新建jdk8 spring boot项目
  • Qt/C++音视频开发59-使用mdk-sdk组件/原qtav作者力作/性能凶残/超级跨平台
  • 智安网络|企业网络安全工具对比:云桌面与堡垒机,哪个更适合您的需求
  • Git忽略已经提交的文件
  • MVVM和MVC以及MVP的原理以及它们的区别
  • WeChatMsg: 导出微信聊天记录 | 开源日报 No.108
  • Python学习之复习MySQL-Day3(DQL)
  • AI超级个体:ChatGPT与AIGC实战指南
  • SpringBoot集成websocket(5)|(使用OkHttpClient实现websocket以及详细介绍)
  • Kafka-Kafka基本原理与集群快速搭建(实践)
  • Elasticsearch 进阶(索引、类型、字段、分片、副本、集群等详细说明)-06
  • hive的分区表和分桶表详解
  • verilog语法进阶-分布式ram
  • HarmonyOS使用HTTP访问网络
  • GZ015 机器人系统集成应用技术样题1-学生赛
  • 计算机毕业设计 基于SpringBoot的日常办公用品直售推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • uniapp:使用fixed定位,iOS平台的安全区域问题解决
  • 三层交换机原理与配置
  • Linux-----5、文件系统
  • 电脑自动关机怎么设置?
  • MS5602视频 8 位数模转换器,可替代TLC5602
  • Logistic Regression——逻辑回归
  • 跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)
  • Todesk、向日葵等访问“无显示器”主机黑屏问题解决
  • maven打包插件maven-jar-plugin与spring-boot-maven-plugin