LeetCode题练习与总结:除法求值--399
一、题目描述
给你一个变量对数组 equations
和一个实数值数组 values
作为已知条件,其中 equations[i] = [Ai, Bi]
和 values[i]
共同表示等式 Ai / Bi = values[i]
。每个 Ai
或 Bi
是一个表示单个变量的字符串。
另有一些以数组 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
由小写英文字母与数字组成
二、解题思路
-
将所有等式转化为图的形式,其中每个变量是一个节点,等式表示两个节点之间的有向边,边的权重为等式右边的值。
-
对于每个查询,可以将其看作在图中寻找一条从起点到终点的路径,路径上的边权重乘积即为所求的结果。
-
使用深度优先搜索(DFS)或广度优先搜索(BFS)在图中寻找这样的路径。如果在搜索过程中找到了终点,则返回路径上所有边的权重乘积;如果搜索完成后仍未找到终点,则返回-1.0。
-
在搜索过程中,为了避免重复搜索,需要记录已经访问过的节点。
三、具体代码
import java.util.*;
public class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
// 构建图
Map<String, Map<String, Double>> graph = buildGraph(equations, values);
// 结果数组
double[] results = new double[queries.size()];
// 遍历每个查询
for (int i = 0; i < queries.size(); i++) {
List<String> query = queries.get(i);
String start = query.get(0);
String end = query.get(1);
// 如果查询中的变量未在图中出现,直接返回-1.0
if (!graph.containsKey(start) || !graph.containsKey(end)) {
results[i] = -1.0;
} else if (start.equals(end)) { // 如果起点和终点相同,结果为1.0
results[i] = 1.0;
} else {
// 使用DFS寻找路径
Set<String> visited = new HashSet<>();
results[i] = dfs(graph, start, end, 1.0, visited);
}
}
return results;
}
private double dfs(Map<String, Map<String, Double>> graph, String current, String target, double value, Set<String> visited) {
// 标记当前节点为已访问
visited.add(current);
// 如果找到了目标节点,返回累计的值
if (graph.containsKey(current) && graph.get(current).containsKey(target)) {
return value * graph.get(current).get(target);
}
// 遍历当前节点的邻接节点
for (Map.Entry<String, Double> entry : graph.get(current).entrySet()) {
String next = entry.getKey();
if (!visited.contains(next)) {
double result = dfs(graph, next, target, value * entry.getValue(), visited);
if (result != -1.0) {
return result;
}
}
}
// 如果没有找到路径,返回-1.0
return -1.0;
}
private Map<String, Map<String, Double>> buildGraph(List<List<String>> equations, double[] values) {
Map<String, Map<String, Double>> graph = new HashMap<>();
for (int i = 0; i < equations.size(); i++) {
List<String> equation = equations.get(i);
String var1 = equation.get(0);
String var2 = equation.get(1);
double value = values[i];
graph.putIfAbsent(var1, new HashMap<>());
graph.putIfAbsent(var2, new HashMap<>());
graph.get(var1).put(var2, value);
graph.get(var2).put(var1, 1 / value);
}
return graph;
}
}
该代码首先构建了一个图,然后对每个查询使用深度优先搜索来寻找路径,并计算路径上所有边的权重乘积。如果在图中找到了对应的路径,则返回计算结果;否则,返回-1.0。
四、时间复杂度和空间复杂度
1. 时间复杂度
-
构建图的时间复杂度:对于每个等式,我们需要在图中添加两个节点和它们之间的两条边。由于每个等式需要常数时间操作,构建图的时间复杂度为 O(E),其中 E 是等式的数量。
-
查询处理的时间复杂度:对于每个查询,我们使用深度优先搜索(DFS)来寻找从起点到终点的路径。在最坏的情况下,DFS 可能会遍历图中的所有节点。如果图是完全连通的,则每个查询的时间复杂度为 O(V),其中 V 是图中节点的数量。由于我们需要对每个查询都执行一次 DFS,因此总的时间复杂度为 O(Q * V),其中 Q 是查询的数量。
-
综合时间复杂度:构建图的时间复杂度为 O(E),查询处理的时间复杂度为 O(Q * V)。如果每个变量都至少出现在一个等式中,则 V 的数量最多为 2E(因为每个等式有两个变量),因此 V 可以认为是 O(E)。所以,总的时间复杂度为 O(E + Q * E) = O((E + Q) * E)。
2. 空间复杂度
-
图的空间复杂度:图由节点和边组成,每个节点关联一个映射,映射中的每个键值对代表一条边。因此,图的空间复杂度为 O(V + E),其中 V 是节点数量,E 是边数量。
-
DFS 的空间复杂度:在 DFS 过程中,我们使用了一个集合来记录访问过的节点,这个集合的空间复杂度为 O(V)。此外,DFS 的递归调用栈的深度最多为 V,因此递归栈的空间复杂度也是 O(V)。
-
结果数组的空间复杂度:结果数组的大小与查询的数量 Q 成正比,因此空间复杂度为 O(Q)。
-
综合空间复杂度:图的空间复杂度为 O(V + E),DFS 的空间复杂度为 O(V),结果数组的空间复杂度为 O(Q)。因为 V 可以认为是 O(E),所以总的空间复杂度为 O(E + Q)。
五、总结知识点
-
数据结构:
List
:用于存储等式和查询。Map
:用于构建图,其中键是变量名,值是另一个映射,映射的键是相邻节点,值是边的权重。Set
:用于在深度优先搜索(DFS)中记录已访问的节点,避免重复访问。
-
算法:
- 图的构建:通过遍历等式和值数组,将每个等式转换为图中的一条边。
- 深度优先搜索(DFS):用于在图中寻找从起点到终点的路径,并计算路径上所有边的权重乘积。
-
Java编程:
- 泛型:在集合中使用泛型来保证类型安全。
- 方法重载:
putIfAbsent
方法在Map
中用于添加键值对,如果键不存在则添加,否则不添加。 - 递归:在
dfs
方法中使用递归来实现深度优先搜索。 - 异常处理:通过检查
Map
中是否包含特定的键来避免NullPointerException
。
-
逻辑控制:
- 条件语句:使用
if-else
语句来处理不同的情况,例如检查查询中的变量是否存在于图中。 - 循环:使用
for
循环来遍历等式、查询和图的邻接节点。
- 条件语句:使用
-
数学概念:
- 分数和比例:代码的核心是处理变量之间的比例关系,即等式中的除法操作。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
原文地址:https://blog.csdn.net/weixin_62860386/article/details/143810391
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!