LeetCode算法题(Go语言实现)_46

题目

给你一个变量对数组 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 的情况,且不存在任何矛盾的结果。

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

一、代码实现

go 复制代码
func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
    // 创建变量名到唯一ID的映射
    idMap := make(map[string]int)
    id := 0
    for _, eq := range equations {
        a, b := eq[0], eq[1]
        if _, exists := idMap[a]; !exists {
            idMap[a] = id
            id++
        }
        if _, exists := idMap[b]; !exists {
            idMap[b] = id
            id++
        }
    }

    // 初始化并查集的父节点和权重数组
    parent := make([]int, id)
    weight := make([]float64, id)
    for i := 0; i < id; i++ {
        parent[i] = i
        weight[i] = 1.0
    }

    // 查找根节点并路径修正返回值括号)
    var find func(int) (int, float64)
    find = func(x int) (int, float64) { // 此处添加返回值括号
        if parent[x] != x {
            root, w := find(parent[x])
            parent[x] = root
            weight[x] *= w
        }
        return parent[x], weight[x]
    }

    // 合并节点
    for i, eq := range equations {
        a, b := eq[0], eq[1]
        x, y := idMap[a], idMap[b]
        val := values[i]

        rootX, wX := find(x)
        rootY, wY := find(y)
        if rootX != rootY {
            parent[rootX] = rootY
            weight[rootX] = (val * wY) / wX
        }
    }

    // 处理查询
    results := make([]float64, len(queries))
    for i, q := range queries {
        c, d := q[0], q[1]
        idC, okC := idMap[c]
        idD, okD := idMap[d]
        if !okC || !okD {
            results[i] = -1.0
            continue
        }
        rootC, wC := find(idC)
        rootD, wD := find(idD)
        if rootC != rootD {
            results[i] = -1.0
        } else {
            results[i] = wC / wD
        }
    }

    return results
}

二、算法分析

1. 核心思路
  • 带权并查集:将每个变量视为节点,维护其到父节点的权重(即变量间的比值)。
  • 路径压缩优化:在查找根节点时,直接更新路径上所有节点的父节点为根节点,并同步更新权重。
  • 动态合并:根据等式关系调整不同集合的根节点权重,确保比值计算正确。
2. 关键步骤
  1. 初始化:为每个变量创建独立集合,父节点初始化为自身,权重为1.0。
  2. 合并集合:遍历等式,通过路径压缩找到变量的根节点,并根据等式值调整根节点权重。
  3. 查询处理:检查变量是否存在,若存在且同根则返回权重比值,否则返回-1.0。
3. 复杂度
指标 说明
时间复杂度 O(α(n)) α为阿克曼函数的反函数,接近常数
空间复杂度 O(n) 存储n个变量的父节点和权重

三、图解示例

四、边界条件与扩展

1. 特殊场景验证
  • 变量未定义 :查询中出现未在等式中出现的变量(如x/x),返回-1.0。
  • 自反查询 :查询相同变量(如a/a),路径压缩后权重为1.0,返回1.0。
  • 多级路径 :链式关系(如a→b→c→d)通过路径压缩优化为直接连接。
2. 扩展应用
  • 动态更新等式:支持实时添加新等式,动态调整并查集结构。
  • 图论连通性:判断变量间是否存在连通路径(若根相同则连通)。
  • 复杂数学关系:处理包含加减乘除的复合表达式(需扩展权重计算逻辑)。
3. 多语言实现
python 复制代码
class Solution:
    def calcEquation(self, equations, values, queries):
        parent = {}
        weight = {}
        
        # 初始化并查集
        for a, b in equations:
            if a not in parent:
                parent[a] = a
                weight[a] = 1.0
            if b not in parent:
                parent[b] = b
                weight[b] = 1.0
        
        # 路径压缩的查找函数
        def find(x):
            if parent[x] != x:
                orig_parent = parent[x]
                parent[x] = find(parent[x])
                weight[x] *= weight[orig_parent]  # 路径压缩时更新权重
            return parent[x]
        
        # 合并操作
        for (a, b), val in zip(equations, values):
            ra, rb = find(a), find(b)
            if ra != rb:
                parent[ra] = rb
                weight[ra] = (val * weight[b]) / weight[a]  # 根据等式调整根节点权重
        
        # 处理查询 (修正continue语句语法错误)
        res = []
        for c, d in queries:
            # Case 1: 变量未定义
            if c not in parent or d not in parent:
                res.append(-1.0)
                continue  # 移至下一循环
            
            # Case 2: 计算路径权重
            rc, rd = find(c), find(d)
            if rc != rd:
                res.append(-1.0)
            else:
                res.append(weight[c] / weight[d])
        return res
java 复制代码
class Solution {
    Map<String, String> parent = new HashMap<>();
    Map<String, Double> weight = new HashMap<>();

    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        // 初始化并查集
        for (List<String> eq : equations) {
            String a = eq.get(0), b = eq.get(1);
            parent.putIfAbsent(a, a);
            parent.putIfAbsent(b, b);
            weight.putIfAbsent(a, 1.0);
            weight.putIfAbsent(b, 1.0);
        }
        
        // 合并操作
        for (int i = 0; i < equations.size(); i++) {
            String a = equations.get(i).get(0), b = equations.get(i).get(1);
            union(a, b, values[i]);
        }
        
        // 处理查询
        double[] res = new double[queries.size()];
        for (int i = 0; i < queries.size(); i++) {
            String c = queries.get(i).get(0), d = queries.get(i).get(1);
            if (!parent.containsKey(c) || !parent.containsKey(d)) {
                res[i] = -1.0;
                continue;
            }
            String rc = find(c), rd = find(d);
            if (!rc.equals(rd)) res[i] = -1.0;
            else res[i] = weight.get(c) / weight.get(d);
        }
        return res;
    }

    private String find(String x) {
        if (!x.equals(parent.get(x))) {
            String origParent = parent.get(x);
            parent.put(x, find(origParent));
            weight.put(x, weight.get(x) * weight.get(origParent));
        }
        return parent.get(x);
    }

    private void union(String a, String b, double val) {
        String ra = find(a), rb = find(b);
        if (ra.equals(rb)) return;
        parent.put(ra, rb);
        weight.put(ra, (val * weight.get(b)) / weight.get(a));
    }
}

五、总结与优化

1. 方法对比
方法 优势 劣势 适用场景
带权并查集 查询高效,支持动态合并 实现复杂度较高 大规模数据集和频繁查询
DFS/BFS 实现简单 重复计算,时间复杂度高 小规模数据或低频查询
2. 工程优化
  • 路径压缩:确保查找操作接近常数时间复杂度。
  • 按秩合并:优化树的高度(可选,Java示例未实现)。
  • 内存管理:使用哈希表动态扩展,避免预分配固定大小。
相关推荐
uhakadotcom6 分钟前
BentoML远程代码执行漏洞(CVE-2025-27520)详解与防护指南
后端·算法·面试
_x_w8 分钟前
【16】数据结构之基于树的排序算法篇章
开发语言·数据结构·python·算法·链表·排序算法
西门吹雪分身23 分钟前
Redis之RedLock算法以及底层原理
数据库·redis·算法
一路向北he1 小时前
杰理10k3950温度测量
java·数据结构·算法
描绘一抹色1 小时前
力扣-hot100(最长连续序列 - Hash)
数据结构·算法·leetcode
黄昏ivi1 小时前
事件触发控制与响应驱动控制的定义、种类及区别
人工智能·分布式·学习·算法·机器学习
温文尔雅透你娘1 小时前
摄像头在自动驾驶中的核心应用:感知算法与技术方案深度解析
人工智能·算法·计算机视觉·目标跟踪·自动驾驶
Vacant Seat1 小时前
贪心算法-跳跃游戏
算法·游戏·贪心算法
是小满满满满吗1 小时前
基础贪心算法集合2(10题)
算法·贪心算法
小林熬夜学编程1 小时前
【高阶数据结构】第三弹---图的存储与遍历详解:邻接表构建与邻接矩阵的BFS/DFS实现
c语言·数据结构·c++·算法·深度优先·图论·宽度优先