题目描述

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。 

 

示例 1:

输入:["a==b","b!=a"]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。


示例 2:

输入:["b==a","a==b"]
输出:true
解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。


示例 3:

输入:["a==b","b==c","a==c"]
输出:true


示例 4:

输入:["a==b","b!=c","c==a"]
输出:false


示例 5:

输入:["c==c","b==d","x!=z"]
输出:true

来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/satisfiability-of-equality-equations

解题思路

将 equations 中的算式根据 == 和 != 分成两部分,先处理 == 算式,使得他们通过相等关系连通在一起;然后处理 != 算式,检查不等关系是否破坏了相等关系的连通性。

思路参考:https://labuladong.gitbook.io/algo/shu-ju-jie-gou-xi-lie/shou-ba-shou-she-ji-shu-ju-jie-gou/unionfind-suan-fa-ying-yong

解题代码

class Solution {
    public boolean equationsPossible(String[] equations) {
        if (equations.length > 0) {
            // 26 个英文字母
            UnionFind uf = new UnionFind(26);
            // 先让相等的字母形成连通分量
            for (String eq : equations) {
                if (eq.charAt(1) == '=') {
                    uf.union(eq.charAt(0) - 'a', eq.charAt(3) - 'a');
                }
            }
            // 检查不等关系是否打破相等关系的连通性
            for (String eq : equations) {
                if (eq.charAt(1) == '!') {
                    boolean isConnected = uf.connected(
                            eq.charAt(0) - 'a', eq.charAt(3) - 'a');
                    // 如果相等关系成立,就是逻辑冲突
                    if (isConnected) {
                        return false;
                    }
                }
            }
        }
        return true;
    }


    /**
     * Union-Find 并查集算法
     */
    public class UnionFind {
        /**
        * 连通分量个数
        */
        private int count;
        /**
        * 存储一个棵树
        */
        private int[] parent;
        /**
        * 记录树的重量
        */
        private int[] size;

        /**
        * 构造函数
        * @param n
        */
        public UnionFind(int n) {
            this.count = n;
            this.parent = new int[n];
            this.size = new int[n];
            for (int i = 0; i < n; i++) {
                parent[i] = i;
                size[i] = 1;
            }
        }

        /**
        * 连通两个元素
        * @param p
        * @param q
        */
        public void union(int p, int q) {
            int rootP = find(p);
            int rootQ = find(q);
            if (rootP == rootQ) {
                return;
            }

            // 小树接到大树下,较平衡
            if (size[rootP] > size[rootQ]) {
                parent[rootQ] = rootP;
                size[rootP] += size[rootQ];
            } else {
                parent[rootP] = rootQ;
                size[rootQ] += size[rootP];
            }
            count--;
        }

        /**
        * 判断两个元素是否连通
        * @param p
        * @param q
        * @return
        */
        public boolean connected(int p, int q) {
            int rootP = find(p);
            int rootQ = find(q);
            return rootP == rootQ;
        }

        /**
        * 查找元素的根结点
        * @param x
        * @return
        */
        public int find(int x) {
            while (parent[x] != x) {
                // 进行路经压缩
                parent[x] = parent[parent[x]];
                x = parent[x];
            }
            return x;
        }

        /**
        * 连通分量个数
        * @return
        */
        public int count() {
            return count;
        }
        
    }
}