Union-find


终于抽出时间总结回顾一下Union-find了!

《算法》书中,从quick_find,quick_union到weighted_quick_union到path compression,一步步深入。下面是用C++实现的版本。

quick find

O(1)时间查找一个节点所属的组,平均用O(n)时间合并两个节点。思路是这样的:将N个点所属的组(id)初始为相应的标号:0~N-1。之后,若碰到合并两个节点时,则将每个组内的所有点的id更新为同一个值。

#include <vector>
#include <time.h>
#include <fstream>using namespace std;
/*
O(1)查找两个节点是否连通,O(n)时间Union
相当于给每个节点进行分组,每个组内节点的标号是一致的
*/class QuickFindUF
{
public:QuickFindUF(int N);int get_count();int find(int p);bool connected(int p, int q);void Union(int p, int q);private:vector<int> id;int count; //连通分量数
};QuickFindUF::QuickFindUF(int N)
{count = N;id = vector<int>(N);for (int i = 0; i < N; i++){id[i] = i;}
}int QuickFindUF::get_count()
{return count;
}int QuickFindUF::find(int p)
{return id[p];
}bool QuickFindUF::connected(int p, int q)
{return find(p) == find(q);
}void QuickFindUF::Union(int p, int q)
{int pID = find(p);int qID = find(q);//先检查是否连通。所以当需要连接两个点时,不需要在外面检测是否连通if (pID == qID)return;int sz = id.size();for (int i = 0; i < sz; i++){if (find(i) == pID)id[i] = qID;}count--;
}//0.01s
int main()
{ifstream cin("mediumUF.txt");ofstream cout("outmediumUF.txt");int N;cin >> N;QuickFindUF qfUF(N);clock_t start, end;start = clock();while (cin){int p, q;cin >> p >> q;if (qfUF.connected(p, q)) continue;qfUF.Union(p,q);}end = clock();double totoal_time = (double)(end-start)/CLK_TCK;cout << qfUF.get_count() << endl;cout << "quick find total time: " << totoal_time << "s";cin.close();cout.close();
}

quick union

quick union为了提高union的速度,改变了union策略。对于每个点而言,存放它的父结点。初始时,每个点的父节点是本身。当合并两个节点的时候,先找出这个点的祖先节点rootP,rootQ,若两者不等,则将rootP的父节点设置成rootQ。所以,parent数组里保存的是每个点的父节点,而当一个节点的父节点是自身的时候,那么这个节点即是根节点。对于查找一个节点的父节点,时间复杂度为树的高度;而union两个节点的复杂度也是树的高度,因为union的时候,需要先找出p,q两个节点的根节点。union之后,原来以rootP为根结点的树直接挂到rootQ上,因此所有节点的高度加1。

#include <vector>
#include <fstream>
#include <time.h>using namespace std;/*
parent[]数组用父链接的形式组成了一个森林。
*/class QuickUnionUF
{
public:QuickUnionUF(int N);int get_count();int find(int p);bool connected(int p, int q);void Union(int p, int q);private:vector<int> parent;int count;};QuickUnionUF::QuickUnionUF(int N)
{parent = vector<int>(N);count = N;for (int i = 0; i < N; i++){parent[i] = i;}
}int QuickUnionUF::get_count()
{return count;
}int QuickUnionUF::find(int p)
{while (p != parent[p]){p = parent[p];}return p;
}bool QuickUnionUF::connected(int p, int q)
{return find(p) == find(q);
}void QuickUnionUF::Union(int p, int q)
{int rootP = find(p);int rootQ = find(q);if (rootP == rootQ)return;parent[rootP] = rootQ; //以rootP为根的树的高度加1.count--;
}//0.051s
int main()
{ifstream cin("mediumUF.txt");ofstream cout("outmediumUF.txt");int N;cin >> N;QuickUnionUF quUF(N);clock_t start, end;start = clock();while (cin){int p, q;cin >> p >> q;if (quUF.connected(p, q)) continue;quUF.Union(p,q);}end = clock();double totoal_time = (double)(end-start)/CLK_TCK;cout << quUF.get_count() << endl;cout << "quick union total time: " << totoal_time << "s";cin.close();cout.close();
}

weighted quick union

quick union算法有一点不好的是,parent数组表示的树可能非常不平衡。因此提出一种改进的算法。改进的地方在于:每次union的时候,不是盲目地直接将rootP的父节点设置成rootQ,而是根据两棵树的大小:树小的连接到树大的根结点上。这样,树就会一直保持平衡。要实现这一点,要添加一个变量来保持树的大小。

#include <vector>
#include <iostream>
#include <fstream>
#include <time.h>using namespace std;/*
使得树更平衡,查找次数减少
*/class WeightedQuickUnion
{public:WeightedQuickUnion(int N);int get_count();int find(int p);bool connected(int p, int q);void Union(int p, int q);private:vector<int> parent;vector<int> size;int count;
};WeightedQuickUnion::WeightedQuickUnion(int N)
{parent = vector<int>(N);size = vector<int>(N);count = N;for (int i = 0; i < N; i++){parent[i] = i;size[i] = 1;}
}int WeightedQuickUnion::get_count()
{return count;
}int WeightedQuickUnion::find(int p)
{while (p != parent[p]){parent[p] = parent[parent[p]]; //path compressionp = parent[p];}return p;
}bool WeightedQuickUnion::connected(int p, int q)
{return find(p) == find(q);
}void WeightedQuickUnion::Union(int p, int q)
{int rootP = find(p);int rootQ = find(q);if (rootP == rootQ) //必须加上。如果p,q本来相连,后面的代码不能执行.return;if (size[rootP] < size[rootQ]){parent[rootP] = rootQ;size[rootQ] += size[rootP];}else{parent[rootQ] = rootP;size[rootP] += size[rootQ];}count--;
}

path compression

实现了树的平衡还没完,还可以在查找一个节点的根结点的过程中,进一步减小树的深度。即把树flatten。理想情况下,只有一个根结点,所有的孩子在高度为1的地方,这样find起来就会比较快。可以用two-pass来完成,即找到p的根结点后,把路径上的点的根结点都设为根结点。

但是我们最后采用了一个简便的方法:如果一个节点p的父节点parent[p] != p,即p不是根结点,那么就把p的父节点设置成p的爷爷节点。只需要在find的while循环中加一行代码就可以实现了。代码见weighted quick union代码中注释为//path compression的地方。达到的效果是把树的高度减半。

Union-find相关推荐

  1. Python type hints 之 Optional,Union

    1,前言 type hint 在pep484加入,我个人觉得这种类似于类型约束的(机制)有点违背了python简单.简洁的初衷,在慢慢向c# java 这种强类型语言看齐的节奏. 不过好在不强制使用, ...

  2. C++ 共用体union 的使用

    共用体是什么 共用体将不同的数据类型组织为一个整体, 需要注意的是,共用体在同一时刻只能存储一个数据成员的值., 共用体变量的地址和它的格式成员的地址都是同一地址 共用体的一般形式 union 共用体 ...

  3. 关于 并查集(union find) 算法基本原理 以及 其 在分布式图场景的应用

    二月的最后一篇水文-想写一些有意思的东西. 文章目录 环检测在图数据结构中的应用 深度/广度优先 检测环 并查集数据结构 (Union-Find) 基本概念 初始化 合并 union 查找祖先 优化1 ...

  4. 联合体union和大小端(big-endian、little-endian)

    1.联合体union的基本特性--和struct的同与不同 union,中文名"联合体.共用体",在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(s ...

  5. struct和union的大小问题

    union类型以其中size最大的为其大小 struct类型以其中所有size大小之和为其大小 #include<iostream> using namespace std; int ma ...

  6. 利用c语言结构体和union实现类似c++的public,private的实现

    最近在看strongswan源代码,看到strongswan的代码框架很有意思,用C语言实现类的思想.当我们编写完一个模块,我们需要提供的是H的文件给其他模块使用,我们希望H文件中就只能包含一些公有函 ...

  7. php union all,Union与Union All的区别

    Union与Union All的区别 如果我们需要将两个select语句的结果作为一个整体显示出来,我们就需要用到union或者union all关键字.union(或称为联合)的作用是将多个结果合并 ...

  8. mysql join union_MySQL中union和join语句使用区别的辨析教程

    union和join是需要联合多张表时常见的关联词,具体概念我就不说了,想知道上网查就行,因为我也记不准确. 先说差别:union对两张表的操作是合并数据条数,等于是纵向的,要求是两张表字段必须是相同 ...

  9. C++/C union使用记一下锅

    //首先,学习编程一定要记得加几个群或者加几个讨论组,因为这样你才能不断地进步还有吵架/滑稽 记一下 关于使用union结构体时遇到的一些坑 To zero-initialize an object ...

  10. 索引系列八--索引特性之有序难优化union

    ----UNION 是需要排序的 drop table t1 purge; create table t1 as select * from dba_objects where object_id i ...

最新文章

  1. python的基本语句_Python的基本语句
  2. Bootstrap3.0学习第九轮(CSS补充)
  3. 访问Webservice错误
  4. java足球经理2010下载_apk是什么文件?apk文件怎么打开?
  5. 在公司里,谁的不可代替性最强
  6. python不同目录调用_python3 不同目录间模块调用
  7. adb 多点触碰_无法触及的神话
  8. pycharm 2020 版取消鼠标悬停显示说明文档的方法
  9. 分享按钮 html代码,超简洁微博分享按钮代码
  10. margin和padding的四种写法
  11. 手动同步OCS的通讯簿
  12. onenote快捷键_onenote快捷键的高效用法
  13. 洛谷P1306 斐波那契公约数
  14. 基于 Spring Boot + Vue.js + MySQL 的 QQ 登陆实战
  15. 基于单片机的火灾消防系统设计(#0480)
  16. 搭建安卓开发环境 GIT配色 terminator SecureCRT source insight Notepad++安装
  17. 全球及中国钢铁行业投资产量趋势及营销盈利模式研究报告2021版
  18. 背课文记单词,读课文记单词,读文章记单词;40篇文章搞定3500词;71篇文章突破中考单词;15篇文章贯通四级词汇;15篇文章贯通六级词汇
  19. 【Python+Appium】开展自动化测试(八)swipe()滑动页面
  20. 学python用虚拟机还是双系统_Macbook双系统虚机-win10篇

热门文章

  1. ncbi查找目的基因序列_教你如何利用NCBI寻找目的基因
  2. mysql主从配置原理_MySQL主从复制原理
  3. qt撤销与回退_Qt动画框架
  4. Asp.net MVC Filter监控页面性能和运行时间
  5. spring 依赖注入总结
  6. 步步为营 .NET三层架构解析 四、Model设计(四种设计方式)
  7. 配置Hyper-V Server 资源计量
  8. PHPUnit 3.4.10 在windows上配置
  9. 6月共处理钓鱼网站8186个:非CN域名达8029个
  10. 给NavigationCtrl 增强动画.