当前位置: 首页 > article >正文

【算法】并查集基础讲解

一、定义

一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,只要找到了某个元素的的树根,就能确定它在哪个集合里。

二、例题

让我们一起通过一道题目理解一下并查集解决的问题:
https://leetcode.cn/problems/satisfiability-of-equality-equations/description/

leetcode990
在这里插入图片描述

分析上述问题,如果我们用脑子去想的话,该问题是很简单的,但是我们如何用程序解决该问题呢,就需要并查集了

三、核心思想

节点

并查集是一个森林,森林由树组成,树由节点构成,节点就是并查集的元素。

对本题来讲,以 示例1 为例:a 和 b 就是并查集中的两个元素,即两个节点。

树根(父节点)

节点有其父节点,节点组成树结构,树结构有根节点。如果两个节点具有相同的根节点,那么他们就位于同一树中,即两个节点位于同一集合中。

对本题来讲,以 示例4 为例:

  • a==b
  • b!=c
  • c==a

a,b,c 组成一个 ==(等于) 树 ,可以将 a 看作根节点。

所以并查集解决了什么问题?

通过上面的分析,我们可以看出构建并查集后,解决的是 元素是否位于同一集合问题

并查集的构建

下面以 Java 代码为例,解释并查集的构建过程。

初始化并查集

  • 使用 parent 数组表示节点的父节点
  • 初始化并查集的父节点为其自身
public class UnionFind {
    // 表示元素(节点)i的父亲节点
    // parent[0] = 1 , 即 节点0 的 父亲节点是 1
    int[] parent;

    // 初始化并查集元素的父亲节点为其自身
    public void init(int n){
        for (int i=0; i<n; i++){
            parent[i] = i;
        }
    }
}

找到根节点

  • 通过上文,我们得知了如果判断两个元素是否位于同一集合的方法为:判断两个元素是否具有相同的根节点
  • 根节点的判断:根节点的根节点为其自身
  • 寻找根节点的过程使用递归
    public int findRoot(int u){
        // 如果节点 u 的父亲节点为其自身,说明当前节点为根节点
        if(u == parent[u]){
            return u;
        }
        // 递归寻找根节点
        return findRoot(parent[u]);
    }

合并元素

  • 如果节点 u 与 v 具有关联关系(位于同一集合),就需要合并元素 u 和 v
  • 合并过程:将节点 v 的根设置为节点 u 的根节点
    public void merge(int u, int v){
        u = findRoot(u);
        v = findRoot(v);
        parent[v] = u;
    }

四、综合解决问题

通过上面的内容已经对并查集有了基础了解,下面我们来看如何使用上面的内容,结合一点思考,完整解决例题。

分析

  • 构建等式并查集
  • 遍历不等式,如若不等式 b != a,但 b 与 a 位于同一等式集合(树),则说明不可能成立。
  • 一些细节处理

代码

package leet;

import java.util.HashMap;
import java.util.Map;

class Solution {
    // 表示元素(节点)i的父亲节点
    // parent[0] = 1 , 即 节点0 的 父亲节点是 1
    int[] parent;

    // 初始化并查集元素的父亲节点为其自身
    public void init(int n){
        for (int i=0; i<n; i++){
            parent[i] = i;
        }
    }

    public int findRoot(int u){
        // 如果节点 u 的父亲节点为其自身,说明当前节点为根节点
        if(u == parent[u]){
            return u;
        }
        // 递归寻找根节点
        return findRoot(parent[u]);
    }

    // 合并元素 u v 到同一集合
    public void merge(int u, int v){
        u = findRoot(u);
        v = findRoot(v);
        parent[v] = u;
    }

    // 核心方法
    public boolean equationsPossible(String[] equations) {
        // 得到并查集元素数量 n 并进行编号
        Map<Character, Integer> map;
        map = countN(equations);
        int n = map.size();
        // 初始化并查集
        parent = new int[n];
        init(n);
        // 构建并查集,建立等式之间的元素关系
        for (int i=0; i<equations.length; i++){
            char a = equations[i].charAt(0);
            char b = equations[i].charAt(3);
            if(equations[i].charAt(1) == '=') {
                merge(map.get(a), map.get(b));
            }
        }
        // 遍历所有不等式
        for (int i=0; i<equations.length; i++) {
            char a = equations[i].charAt(0);
            char b = equations[i].charAt(3);
            // 第一种不成立情况 a != a (自身不等于自身)
            if( a == b && equations[i].charAt(1) == '!'){
                return false;
            }
            // 如果元素未出现在等式中,第一次出现在不等式中 无影响 即:a == b  b != c —— c 不在 map 中,c 也不影响结果
            if (map.containsKey(a) && map.containsKey(b)) {
                if (equations[i].charAt(1) == '!') {
                    // 判断 a,b 是否在同一集合
                    // a,b在同一相等集合中,则说明不成立
                    if (findRoot(map.get(a)) == findRoot(map.get(b))) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    // 工具类
    // 为字符编号
    public Map<Character, Integer> countN(String[] equations){
        Map<Character, Integer> map = new HashMap<>();
        int index = 0;
        for (int i=0; i<equations.length; i++){
            char a = equations[i].charAt(0);
            char b = equations[i].charAt(3);
            if(equations[i].charAt(1) == '='){
                if(!map.containsKey(a)){
                    map.put(a, index);
                    index++;
                }
                if(!map.containsKey(b)){
                    map.put(b, index);
                    index++;
                }
            }
        }
        return map;
    }
}

http://www.kler.cn/a/623252.html

相关文章:

  • TCP协议与wireshark抓包分析
  • 现代优雅杂志海报徽标设计手写英文字体安装包 Attomes – Brush Handwritten Font
  • 【Prompt实战】邮件意图分类助手
  • git | 版本切换的相关指令
  • 深度学习入门(二):从感知机到神经网络
  • (三)物理设备
  • 创作领域“<em >一</em><em>分</em><em>快</em><em>3</em><em>官</em><em>网
  • 关于参加CSP-J/S认证需符合年龄条件的公告(2025年起)
  • 漏洞挖掘---灵当CRM客户管理系统getOrderList SQL注入漏洞
  • 保存预测图像时出现的文件名错误
  • Kubernetes 存储
  • NQA 网络质量分析协议
  • uniapp uni-swipe-action滑动内容排版改造
  • 未来已来,机器人周边行业和配套业务方向
  • QtWebApp使用
  • Cursor软件设置中文版教程
  • 五.ubuntu20.04 - ffmpeg推拉流以及Nginx、SRS本地部署
  • 知能行综测
  • 马达加斯加企鹅字幕
  • Epub转PDF软件Calibre电子书管理软件