【高阶数据结构】并查集


并查集

1、概念

并查集(Union-Find)是一种可以用来判断同属一个集合中相互关联的元素属于几个集合,也可以用来判断图结构中的两点是否是连通, 它也是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

2、根据人找编号 / 根据编号找人(简单介绍一下并查集)

(1)代码展示

// UnionFindSet.h
#pragma once

#include <vector>
#include <map>

template <class T>
class UnionFindSet
{
private:
	std::vector<T> _a;			// 编号找人
	std::map<T, int> _indexmap; // 人找编号的映射关系
public:
	UnionFindSet(const T* a, size_t n)
	{
		for (size_t i = 0; i < n; i++)
		{
			_a.push_back(a[i]);  // 将传进来的值存入到vector中
			_indexmap[a[i]] = i; // 映射关系
		}
	}
};
// photo.cpp
#include <iostream>
#include "UnionFindSet.h"

int main()
{
	std::string s[] = { "张三", "李四", "王五", "赵六" };
	UnionFindSet<std::string> ufs(s, 4);
	return 0;
}

(2)调试结果

在这里插入图片描述

(3)优化1:小的往大的合并

在这里插入图片描述

(4)优化2:压缩路径

	// 找根
	int FindRoot(int x)
	{
		int root = x;
		while (_ufs[root] >= 0)
		{
			root = _ufs[root]; // 下标和值的关系
		}

		// 压缩路径
		while (_ufs[x] >= 0)
		{
			int parent = _ufs[x]; // 提前存一下父亲结点的值
			_ufs[x] = root; // 往上找
			x = parent;
		}
		return root;
	}

在这里插入图片描述

3、并查集操作和演示题目

(1)并查集操作

i、思路

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

ii、总体代码
class UnionFindSet
{
private:
	std::vector<int> _ufs;
public:
	UnionFindSet(size_t n)
		: _ufs(n, -1)
	{}
	// 合并
	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		// 俩根在一颗树上
		if (root1 == root2) return;

		// 更新
		_ufs[root1] += _ufs[root2]; // 前面的值+=后面的值
		_ufs[root2] = root1; // 更新后面的值为前面的值(双亲根)
	}
	// 找根
	int FindRoot(int x)
	{
		int parent = x;
		while (_ufs[parent] >= 0)
		{
			parent = _ufs[parent]; // 下标和值的关系
		}
		return parent;
	}
	// 判断是否是同一个树
	bool IsSet(int x1, int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}
	// 算树的数量
	int Size()
	{
		int n = _ufs.size();
		int size = 0;
		for (int i = 0; i < n; i++)
		{
			if (_ufs[i] < 0)
			{
				size++;
			}
		}
		return size;
	}
};

(2)演示题目:省份数量

i、做法一:自己写一个并查集

leetcode题目链接跳转
在这里插入图片描述

class UnionFindSet
{
private:
	std::vector<int> _ufs;
public:
	UnionFindSet(size_t n)
		: _ufs(n, -1)
	{}
	// 合并
	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		// 俩根在一颗树上
		if (root1 == root2) return;

		// 更新
		_ufs[root1] += _ufs[root2]; // 前面的值+=后面的值
		_ufs[root2] = root1; // 更新后面的值为前面的值(双亲根)
	}
	// 找根
	int FindRoot(int x)
	{
		int parent = x;
		while (_ufs[parent] >= 0)
		{
			parent = _ufs[parent]; // 下标和值的关系
		}
		return parent;
	}
	// 判断是否是同一个树
	bool IsSet(int x1, int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}
	// 算树的数量
	int Size()
	{
		int n = _ufs.size();
		int size = 0;
		for (int i = 0; i < n; i++)
		{
			if (_ufs[i] < 0)
			{
				size++;
			}
		}
		return size;
	}
};
class Solution 
{
public:
    int findCircleNum(vector<vector<int>>& isConnected) 
    {
        UnionFindSet ufs(isConnected.size());
        for (int i = 0; i < isConnected.size(); i++)
        {
            for (int j = 0; j < isConnected[i].size(); j++)
            {
                if (isConnected[i][j] == 1)
                {
                    ufs.Union(i, j);
                }
            }
        }
        return ufs.Size();
    }
};
ii、做法二:手动版本
class Solution 
{
public:
    int findCircleNum(vector<vector<int>>& isConnected) 
    {
        vector<int> ufs(isConnected.size(), -1);

		// lambda表达式
        auto FindRoot = [&ufs](int x) 
        {
            while (ufs[x] >= 0) x = ufs[x];
            return x;
        };
        for (int i = 0; i < isConnected.size(); i++)
        {
            for (int j = 0; j < isConnected[i].size(); j++)
            {
                if (isConnected[i][j] == 1)
                {
                    int root1 = FindRoot(i);
                    int root2 = FindRoot(j);
                    if (root1 != root2)
                    {
                        ufs[root1] += ufs[root2];
                        ufs[root2] = root1;
                    }
                }
            }
        }
        int n = 0;
        for (auto e : ufs)
        {
            if (e < 0)
                n++;
        }
        return n;
    }
};

(3)演示题目:等式方程可满足性

leetcode题目链接跳转

在这里插入图片描述
进行两次遍历,第一次遍历假如说是中间是等号的情况下的话,就将俩字母都放到同一个集合中,第二次遍历假如说是中间是不等号的情况下的话,就判断俩字母是否是在同一个集合中,在的话就返回false,不在的话就返回true。

class Solution 
{
public:
    bool equationsPossible(vector<string>& equations) 
    {
        vector<int> ufs(26, -1);

		// lambda表达式
        auto FindRoot = [&ufs](int x) 
        {
            while (ufs[x] >= 0) x = ufs[x];
            return x;
        };
        // 第一遍遍历将相同的字母都放到同一个集合中
        for (auto& str : equations)
        {
            if (str[1] == '=')
            {
                int root1 = FindRoot(str[0] - 'a');
                int root2 = FindRoot(str[3] - 'a');
                if (root1 != root2)
                {
                    ufs[root1] += ufs[root2];
                    ufs[root2] = root1;
                }
            }
        }
        // 第二遍遍历,遇到相同的俩字母在一个集合中就返回false
        for (auto& str : equations)
        {
            if (str[1] == '!')
            {
                int root1 = FindRoot(str[0] - 'a');
                int root2 = FindRoot(str[3] - 'a');
                if (root1 == root2)
                {
                    return false;
                }
            }
        }
        return true;
    }
};

相关推荐

  1. 数据结构-

    2024-05-13 03:44:03       33 阅读
  2. 数据结构-

    2024-05-13 03:44:03       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-13 03:44:03       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-13 03:44:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-13 03:44:03       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-13 03:44:03       20 阅读

热门阅读

  1. 【GoLang基础】select语句是什么?

    2024-05-13 03:44:03       13 阅读
  2. 31Windows精简系统下载推荐

    2024-05-13 03:44:03       15 阅读
  3. Redis——入门简介

    2024-05-13 03:44:03       11 阅读
  4. Alibaba Cloud Linux 安装mysql及注意事项

    2024-05-13 03:44:03       15 阅读
  5. [Linux深度学习笔记5.8]

    2024-05-13 03:44:03       13 阅读
  6. C++中合成的默认构造函数的访问权限

    2024-05-13 03:44:03       13 阅读
  7. 王者荣耀铭文说明

    2024-05-13 03:44:03       11 阅读
  8. Spring Boot的工作原理

    2024-05-13 03:44:03       11 阅读
  9. HTTP协议

    2024-05-13 03:44:03       12 阅读
  10. mybatis 模糊查询的几种方式

    2024-05-13 03:44:03       12 阅读