《剑指 Offer》专项突破版 - 面试题 116、117、118 和 119 : 详解并查集(C++ 实现)

目录

前言

面试题 116 : 朋友圈

面试题 117 : 相似的字符串

面试题 118 : 多余的边

面试题 119 : 最长连续序列


 


前言

并查集是一种树形的数据结构,用来表示不相交集合的数据并查集中的每个子集是一棵树,每个元素是某棵树中的一个节点。树中的每个节点有一个指向父节点的指针,树的根节点的指针指向它自己。例如,下图 (a) 所示是一个由两棵树组成的并查集。

并查集支持两种操作,即合并查找合并操作将两个子集合并成一个集合,只需要将一个子集对应的树的根节点的指针指向另一个子集对应的树的根节点。将下图 (a) 中的并查集的两个子集合并之后的并查集如下图 (b) 所示。

另一种操作是查找,即确定某个元素 v 处于哪个子集中。并查集的子集由对应的树的根节点代表。从元素 v 对应的节点开始沿着指向父节点的指针一直找到树的根节点,即节点的祖先节点。并查集的查找操作经常用来判断两个元素是否属于同一个子集。如果两个元素的祖先节点相同,那么它们属于同一个子集

并查集经常用来解决图的动态连接问题假设一个图中有 n 个节点,最开始的时候这 n 个节点互不连通,形成 n 个只有一个节点的子图。每次从图中选取两个节点,如果这两个节点不在同一个子图中,添加一条边连接着两个节点,那么它们所在的子图也就连通了。在添加了 m 条边之后,这个图中子图的数目是多少?最大的子图有多少个节点?这类问题都可以用并查集解决。图中的每个子图对应并查集中的子集,判断图中的两个节点是否在同一个子图就是判断它们对应的元素是否在并查集的同一个子集中,连通图中的两个子图就是合并并查集中的两个子集


面试题 116 : 朋友圈

题目

假设一个班级中有 n 个学生。学生之间有些是朋友,有些不是。朋友关系是可以传递的,例如,A 是 B 的直接朋友,B 是 C 的直接朋友,那么 A 是 C 的间接朋友。定义朋友圈就是一组直接朋友或间接朋友的学生。输入一个 n x n 的矩阵 M 表示班上的朋友关系,如果 M[i][j] = 1,那么学生 i 和 j 是直接朋友。请计算该班级中朋友圈的数目。

例如,输入数组 [[1, 1, 0], [1, 1, 0], [0, 0, 1]],学生 0 和学生 1 是朋友,他们组成一个朋友圈;学生 2 一个人组成一个朋友圈。因此该班级中朋友圈的数目是 2。

分析

朋友关系是对称的,也就是说,A 和 B 是朋友,那么 B 和 A 自然也是朋友,因此,输入的矩阵 M 是沿着对角线对称的。一个人和他自己是朋友,也就是说矩阵 M 中对角线上的所有数字都是 1

朋友的关系可以用图表示,每个学生就是图中的一个节点,而直接朋友就是图中的边。如果学生 i 和学生 j 是直接朋友,就在节点 i 和节点 j 之间添加一条边。输入的矩阵是图邻接矩阵。矩阵 [[1, 1, 0], [1, 1, 0], [0, 0, 1]] 转化成图之后如下图所示,不难发现这个图由两个子图组成,每个子图都是一个朋友圈,因此这个班有两个朋友圈。

应用图搜索解决问题

一个班级可以包含一个或多个朋友圈,对应的图中可能包含一个或多个子图,每个朋友圈对应一个子图。因此,这个问题转化为如何求图中子图的数目

图的搜索算法(广度优先搜索和深度优先搜索)可以用来计算图中子图的数目。扫描图中所有节点。如果某个节点 v 之前没有访问过,就搜索它所在的子图。当所有节点都访问完之后,就可以知道图中有多少子图

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        vector<bool> isVisited(n, false);
        int result = 0;
        for (int i = 0; i < n; ++i)
        {
            if (!isVisited[i])
            {
                dfs(isConnected, isVisited, i);
                ++result;
            }
        }
        return result;
    }
private:
    void dfs(vector<vector<int>>& isConnected, vector<bool>& isVisited, int i) {
        isVisited[i] = true;
        for (int j = 0; j < isConnected.size(); ++j)
        {
            if (isConnected[i][j] == 1 &&
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值