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示例未实现)。
  • 内存管理:使用哈希表动态扩展,避免预分配固定大小。
相关推荐
励志要当大牛的小白菜31 分钟前
ART配对软件使用
开发语言·c++·qt·算法
qq_5139704434 分钟前
力扣 hot100 Day56
算法·leetcode
PAK向日葵2 小时前
【算法导论】如何攻克一道Hard难度的LeetCode题?以「寻找两个正序数组的中位数」为例
c++·算法·面试
Hello.Reader3 小时前
Go-Elasticsearch v9 安装与版本兼容性
elasticsearch·golang·jenkins
爱喝矿泉水的猛男4 小时前
非定长滑动窗口(持续更新)
算法·leetcode·职场和发展
YuTaoShao4 小时前
【LeetCode 热题 100】131. 分割回文串——回溯
java·算法·leetcode·深度优先
五岁小孩4 小时前
实操使用 go pprof 对生产环境进行性能分析(问题定位及代码优化)
性能优化·golang·pprof
YouQian7724 小时前
Traffic Lights set的使用
算法
go54631584656 小时前
基于深度学习的食管癌右喉返神经旁淋巴结预测系统研究
图像处理·人工智能·深度学习·神经网络·算法
aramae6 小时前
大话数据结构之<队列>
c语言·开发语言·数据结构·算法