刷leetcode——除法求值 除法程序c++代码
bigegpt 2024-10-30 01:52 6 浏览
这道题主要涉及的是对树的理解,相关的算法是BFS、DFS、并查集。
原题
给出方程式 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的情况,且不存在任何矛盾的结果。
原题url:https://leetcode-cn.com/problems/evaluate-division/
解题
BFS或DFS
一般而言,如果我们知道了a/b和b/c,求a/c的话,可以通过a/b * b/c求得结果。联想成树的话,也就是节点与节点之间是否相连。总的来说,我们需要进行关系的转换。
利用递归的话,可以很好写出代码,我提供一个 DFS 的例子:
class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
// 记录出现了哪些字符串
Set<String> keySet = new HashSet<>();
// 记录等式关系,第一层key为除数,第二层key为被除数,第二层value为结果
Map<String, Map<String, Double>> map = new HashMap<>();
// 当前是第几个等式,也代表当前要去取第几个结果
int count = 0;
// 遍历等式
for (List<String> equation : equations) {
// 除数
String divisor = equation.get(0);
keySet.add(divisor);
// 被除数
String divided = equation.get(1);
keySet.add(divided);
// 结果
double value = values[count];
// 赋值
putValue(value, divisor, divided, map);
count++;
}
// 计算结果
double[] result = new double[queries.size()];
count = 0;
for (List<String> query : queries) {
// 除数
String divisor = query.get(0);
// 被除数
String divided = query.get(1);
// 求结果
result[count] = cal(divisor, divided, map, new HashSet<>(), keySet);
count++;
}
return result;
}
public double cal(
String divisor,
String divided,
Map<String, Map<String, Double>> map,
Set<String> divisorSet,
Set<String> keySet) {
// 但凡除数、被除数有一个不存在,则直接返回-1.0
if (!keySet.contains(divisor) || !keySet.contains(divided)) {
return -1.0;
}
// 根据除数,获取valueMap
Map<String, Double> valueMap = map.get(divisor);
// 查找是否有现成结果
Double result = valueMap.get(divided);
// 如果有就直接返回
if (result != null) {
return result;
}
// 没有就进行计算
for (String key : keySet) {
Double value = valueMap.get(key);
if (value == null) {
continue;
}
// 如果为-1.0,说明无法计算
if (value == -1.0) {
continue;
}
// 原本是计算"divisor/divided",现在可以尝试求出"divisor/key"和"key/divided"
// 如果key的数据已经计算过,则不再重复计算
if (divisorSet.contains(key)) {
continue;
}
divisorSet.add(key);
// DFS
double tempResult = cal(key, divided, map, divisorSet, keySet);
// 记录中间结果
putValue(tempResult, key, divided, map);
// 如果为-1.0,说明无法计算
if (tempResult == -1.0) {
continue;
}
// 记录最终结果
putValue(value * tempResult, divisor, divided, map);
// 返回结果
return value * tempResult;
}
// 说明无法计算
putValue(-1.0, divisor, divided, map);
return -1.0;
}
public void putValue(
double value,
String divisor,
String divided,
Map<String, Map<String, Double>> map) {
// 根据除数赋值
Map<String, Double> valueMap = map.get(divisor);
if (valueMap == null) {
valueMap = new HashMap<>();
map.put(divisor, valueMap);
// 记录"divisor/divisor = 1.0"
valueMap.put(divisor, 1.0);
}
valueMap.put(divided, value);
// 根据被除数赋值
valueMap = map.get(divided);
if (valueMap == null) {
valueMap = new HashMap<>();
map.put(divided, valueMap);
// 记录"divided/divided = 1.0"
valueMap.put(divided, 1.0);
}
valueMap.put(divisor, 1.0 / value);
}
}
提交OK,大家可以尝试写一个 BFS(广度优先搜索) 的版本,需要借用队列记录中间遍历过的节点。
并查集
首先,我们需要了解什么是并查集,可以参考这一篇博客:https://www.cnblogs.com/noKing/p/8018609.html
我的理解是:当我们知道了一堆元素里某几个之间的关联关系,可以将所有元素归并到一个集合中,这个集合中所有元素都是有关系的。
虽然并查集在构造时复杂,消耗一定的时间,但它可以提高了查找的效率。
针对这道题目,我们不仅需要记录 数字 与 数字 之间是否存在关联,还需要记录具体的倍数关系。其实你可以简单理解为:
当我们知道了:
a / b = 3
b / d = 2
c / d = 4
我们可以将 d 看成是根节点,它有子节点 b、c,b有子节点 a
这样是不是好理解多了。
我是利用一个 HashMap 存储了节点之间是否关联,用另一个 HashMap 存储了节点之间的倍数关系,代码如下:
class Solution {
/**
* 并查集
* key : 当前节点
* value : 其父节点(也可以认为是大哥节点)
*/
private Map<String, String> parents = new HashMap<>();
/**
* 并查集
* key : 当前节点
* value : 父节点(也可以认为是大哥节点) /当前节点
*/
private Map<String, Double> values = new HashMap<>();
private void add(String x) {
// 添加节点x
if (!parents.containsKey(x)) {
parents.put(x, x);
values.put(x, 1.0);
}
}
private void union(String parent, String child, double value) {
// 添加节点
add(parent);
add(child);
// 找到parent和child的最终父节点
String p1 = find(parent);
String p2 = find(child);
// 如果两个结果不等
if (!p1.equals(p2)) {
// 记录p1、p2的关系
parents.put(p2, p1);
values.put(p2, value * (values.get(parent) / values.get(child)));
}
}
/**
* 找到x的最终父节点
*/
private String find(String x) {
while (!parents.get(x).equals(x)) {
x = parents.get(x);
}
return x;
}
/**
* 一直计算到和根节点的关联
*/
private double cal(String x) {
double v = values.get(x);
while (!parents.get(x).equals(x)) {
x = parents.get(x);
v *= values.get(x);
}
return v;
}
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
// 构建并查集
for (int i = 0; i < equations.size(); i++) {
union(equations.get(i).get(0), equations.get(i).get(1), values[i]);
}
// 最终的结果
double[] answer = new double[queries.size()];
// 计算
for (int i = 0; i < queries.size(); i++) {
// 需要计算的两个数
String c1 = queries.get(i).get(0);
String c2 = queries.get(i).get(1);
// 如果有一个是不存在的,则没有计算的必要
if (!parents.containsKey(c1) || !parents.containsKey(c2)) {
answer[i] = -1;
continue;
}
// 如果两者相等,则返回1
if (c1.equals(c2)) {
answer[i] = 1;
continue;
}
// 找到两者的最终父节点,也就是各自的根节点?
String p1 = find(c1);
String p2 = find(c2);
// 如果两者不等,说明两个节点无法构成关联
if (!p1.equals(p2)) {
answer[i] = -1;
continue;
}
// 计算两者的结果
answer[i] = cal(c2) / cal(c1);
}
return answer;
}
}
提交OK,我的这个写法中,并查集是没有进行路径压缩的,有兴趣的同学可以在此之上进行优化,这样当 queries 越大时,查找的效率会越高。
总结
以上就是这道题目我的解答过程了,不知道大家是否理解了。这道题主要涉及的是对树的理解,相关的算法是BFS、DFS、并查集。
有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。
https://death00.github.io/
公众号:健程之道
相关推荐
- 悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)
-
新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...
- 高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源
-
凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...
- 微服务架构实战:商家管理后台与sso设计,SSO客户端设计
-
SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...
- 还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑
-
在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...
- Seata源码—6.Seata AT模式的数据源代理二
-
大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...
- 30分钟了解K8S(30分钟了解微积分)
-
微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
-
一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...
- 一招解决所有依赖冲突(克服依赖)
-
背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...
- 你读过Mybatis的源码?说说它用到了几种设计模式
-
学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...
- golang对接阿里云私有Bucket上传图片、授权访问图片
-
1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...
- spring中的资源的加载(spring加载原理)
-
最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...
- Android资源使用(android资源文件)
-
Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...
- 如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)
-
深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...
- @Autowired与@Resource原理知识点详解
-
springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...
- java的redis连接工具篇(java redis client)
-
在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)