LeetCode_547. 省份数量(并查集,零基础)

在这里插入图片描述

并查集介绍及实现

这道题是一道对于无向图的连通问题的判断。有一个典型的数据结构可以解决这类问题 – 并查集

并查集 - 基本概念

  1. 并查集是一种数据结构
  2. 并(Union): 代表合并
  3. 查(Find): 代表查询
  4. 集(Set): 代表这是以字典为基础数据结构。它的基本功能是合并集合中的元素,查找集合中的元素。
  5. 并查集的典型应用就是有关连通分量的问题
  6. 并查集解决单个问题(添加,合并,查找)的时间复杂度都是O(1)

并查集 - 实现

  1. 数据结构:并查集类似于树结构,但是与树结构相反。在并查集中,每个节点会记录其对应的父节点。
 		// 记录 当前节点 及其 父节点
        private Map<Integer, Integer> father;

在这里插入图片描述
如果两个节点是相互连通的,那么他们在同一个树中,或者说他们在同一个集合中,即他们具有相同的祖先 (上图中,7和3是连通的,但是7和8则不连通)

  1. 添加元素:讲一个新节点添加到并查集中,此时它的父节点应该为空,即它是独立的节点
		// 当把一个新节点添加到并查集中,它的父节点应该为空
        public void add(int x) {
            if (!father.containsKey(x)) {
                father.put(x, null);
                numOfSets++;
            }
        }

在这里插入图片描述
在这里插入图片描述

  1. 合并两个节点:如果发现两个节点是连通的,就要把他们合并,即他们的祖先是相同的。(因为是无向图,所以父节点是谁无所谓)
		// 如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。
		// 这里究竟把谁当做父节点一般是没有区别的。
        public void merge(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);

            if (rootX != rootY) {
                father.put(rootX, rootY);
                numOfSets--;
            }
        }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 判断两节点是否连通:如果两个节点的祖先相同,则他们就是连通的
		// 我们判断两个节点是否处于同一个连通分量的时候,就需要判断它们的祖先是否相同
        public boolean isConnected(int x, int y) {
            return find(x) == find(y);
        }
  1. 查找祖先(重点):如果当前节点的父节点不为空,则迭代向上找,知道父节点为空时,说明到达了祖先节点
		// 查找祖先的方法是:如果节点的父节点不为空,那就不断迭代。
        public int find(int x) {
            int root = x;
            while (father.get(root) != null) {
                root = father.get(root);
            }
            return root;
        }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

并查集基本模版(java)

	class UnionFind {
        // 记录 当前节点 及其 父节点
        private Map<Integer, Integer> father;
        // 记录集合的数量
        private int numOfSets = 0;

        public UnionFind() {
            father = new HashMap<>();
            numOfSets = 0;
        }

        // 当把一个新节点添加到并查集中,它的父节点应该为空
        public void add(int x) {
            if (!father.containsKey(x)) {
                father.put(x, null);
                numOfSets++;
            }
        }

        // 如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。这里究竟把谁当做父节点一般是没有区别的。
        public void merge(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);

            if (rootX != rootY) {
                father.put(rootX, rootY);
                numOfSets--;
            }
        }

        // 我们判断两个节点是否处于同一个连通分量的时候,就需要判断它们的祖先是否相同
        public boolean isConnected(int x, int y) {
            return find(x) == find(y);
        }

        // 查找祖先的方法是:如果节点的父节点不为空,那就不断迭代。
        public int find(int x) {
            int root = x;
            while (father.get(root) != null) {
                root = father.get(root);
            }
            return root;
        }
		
		// 返回连通分量数量
        public int getNumOfSets() {
            return numOfSets;
        }
    }

思路

  1. 本体要考察的就是连通分量的数量,我们增加一个返回连通分量数量的方法(有多少个集合或者说树)
  2. 当我们发现两个城市有连通时,就将两个分量连接在一起,合并到一个集合中
  3. 增加分量的时候,分量数量加一;合并的时候,分量数量减一

实现代码(java)

class Solution {

    class UnionFind {
        // 记录 当前节点 及其 父节点
        private Map<Integer, Integer> father;
        // 记录集合的数量
        private int numOfSets = 0;

        public UnionFind() {
            father = new HashMap<>();
            numOfSets = 0;
        }

        // 当把一个新节点添加到并查集中,它的父节点应该为空
        public void add(int x) {
            if (!father.containsKey(x)) {
                father.put(x, null);
                numOfSets++;
            }
        }

        // 如果发现两个节点是连通的,那么就要把他们合并,也就是他们的祖先是相同的。这里究竟把谁当做父节点一般是没有区别的。
        public void merge(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);

            if (rootX != rootY) {
                father.put(rootX, rootY);
                numOfSets--;
            }
        }

        // 我们判断两个节点是否处于同一个连通分量的时候,就需要判断它们的祖先是否相同
        public boolean isConnected(int x, int y) {
            return find(x) == find(y);
        }

        // 查找祖先的方法是:如果节点的父节点不为空,那就不断迭代。
        public int find(int x) {
            int root = x;
            while (father.get(root) != null) {
                root = father.get(root);
            }
            return root;
        }

        public int getNumOfSets() {
            return numOfSets;
        }
    }
    
    
    public int findCircleNum(int[][] isConnected) {
        UnionFind uf = new UnionFind();
        for (int i = 0; i < isConnected.length; i++) {
            uf.add(i);
            for (int j = 0; j < i; j++) {
                if (isConnected[i][j] == 1) {
                    uf.merge(i, j);
                }
            }
        }
        return uf.getNumOfSets();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值