并查集复习+Leetcode下并查集相关题目

 

目录

一、并查集介绍及其模板

1、并查集介绍

2、并查集模板

二、Leetcode下并查集题目汇总

547. 朋友圈

题目:

解析:


一、并查集介绍及其模板

1、并查集介绍

并查集又名不相交集合,实质上是父指针表示法的一般树,每个节点都有指向其父亲的指针(根节点除外),通常用一维数组实现。并查集最常见的操作有两种,一是union(x, y),表示将x节点和y节点都指向同一根节点;二是find(x),表示找到x节点的根节点。通过两种操作,可以将一个集合分为几个子集合。
在union操作中,有两种常见的优化策略:一是路径压缩策略,即当调用一次find(x)时,顺带将x指向根节点;二是按秩合并,即将有较少节点的树的根指向具有较多节点的树的根。

2、并查集模板

从1开始到num:

int num;
int* father = new int[100005];

void init(int x)
{
    num = x;
    for(int i = 1; i <= num; i++)
        father[i] = i;
}

int find(int x)
{
    if(father[x] != x)
        father[x] = find(father[x]);
    return father[x];
}

void merge(int x, int y)
{
    int a = find(x);
    int b = find(y);
    if(a != b)
        father[b] = a;
}

int getSubsetNum()
{
    int res = 0;
    for(int i = 1; i <= num; i++)
        if(father[i] == i)
            res++;
    return res;
}

二、Leetcode下并查集题目汇总

547. 朋友圈

题目:

班上有 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

解析:

典型的并查集问题,实质上是求无向图的连通分支数。只需要将原始的集合分成几个子集合,每个子集合代表一个朋友圈即可。

int findCircleNum(vector<vector<int>>& M)
{
	if (M.size() == 0)   return 0;
	num = M.size();
	init();
	for (int i = 0; i < M.size(); i++)
		for (int j = i + 1; j < M.size(); j++)
		{
			if (M[i][j])
				merge(i + 1, j + 1);
		}
	return getSubsetNum();
}

另外,也可使用dfs进行求解(参考https://www.cnblogs.com/grandyang/p/6686983.html)的代码:

    int findCircleNum(vector<vector<int>>& M) {
        int n = M.size(), res = 0;
        vector<bool> visited(n, false);
        for (int i = 0; i < n; ++i) {
            if (visited[i]) continue;
            helper(M, i, visited);
            ++res;
        }
        return res;
    }
    void helper(vector<vector<int>>& M, int k, vector<bool>& visited) {
        visited[k] = true;
        for (int i = 0; i < M.size(); ++i) {
            if (!M[k][i] || visited[i]) continue;
            helper(M, i, visited);
        }
    }

684.冗余连接

题目:

在本问题中, 树指的是一个连通且无环的无向图。

输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以组成的二维数组。每一个的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v

解析:

很简单的并查集应用。对于每条边,若邻接两顶点不连通,则union,否则返回这条边。遍历的顺序是从0开始,刚好满足题目条件。

vector<int> findRedundantConnection(vector<vector<int>>& edges) 
{
    if(edges.size() == 0)
        return vector<int>{};
    num = edges.size();
    init(num);
    for(int i = 0; i < num; i++)
    {
        if(find(edges[i][0]) != find(edges[i][1]))
            merge(edges[i][0], edges[i][1]);
        else
            return edges[i];
    }
}

200. 岛屿的个数

题目:

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

解析:

在这里用并查集实现。先令并查集内个节点的值为0,每当遇到一块陆地时,如果它未被放入并查集,则放入并查集;然后遍历四周陆地,如果未放入并查集,也将其放入;然后union两块陆地。最后并查集中子集个数即为答案

int numIslands(vector<vector<char>>& grid) 
{
    if(grid.size() == 0)
        return 0;
    int direction[4][2] = {1,0,0,1,-1,0,0,-1};
    num = grid.size() * grid[0].size();
    int row = grid.size();
    int column = grid[0].size();
    memset(father, 0, sizeof(father));
    for(int i = 0; i < row; i++)
        for(int j = 0; j < column; j++)
            if(grid[i][j] == '1')
            {
                int x = 1 + i * column + j;
                if(father[x] == 0)
                    father[x] = x;
                for(int k = 0; k < 4; k++)
                {
                    int curx = i + direction[k][0], cury = j + direction[k][1];
                    if(curx < 0 || curx >= row || cury < 0 || cury >= column || grid[curx][cury] == '0')
                        continue;
                    else
                    {
                        int x1 = 1 + curx * column + cury;
                        if(father[x1] == 0)
                            father[x1] = x1;
                        merge(x, x1);
                    }
                }
            }
    return getSubsetNum();
}

猜你喜欢

转载自blog.csdn.net/hlk09/article/details/81462771
今日推荐