ch10并查集
CH10+多元尺度法
10.2 資料編碼與SPSS輸入
要使受試者對若干個品牌建立其偏好次序,最簡 單的方法就是要他們排出次序。但是品牌數目一 多時,受試者便不易排出次序了。這時候,我們 可用成對比較法或三比法(三個、三個比較), 來評定他們的偏好次序。這種比較的缺點是在於 當品牌數目很多時,必須做很多的比較,使得受 試者失去耐心
10.3 非計量多元尺度法
大海行銷公司受委託進行四種品牌的定位 分析,並在產品空間中做出偏好圖,以作 為行銷定位策略的參考
圖10-5 「Multidimensional Scaling: Data Format」視窗設定
按〔Define〕(界定),出現「Multidimensional Scaling(Proximities in Matrices Across Columns)」視窗, 如圖10-4所示。將左邊的變數(甲牌、乙牌、丙牌、丁牌) 選入「Proximities」(相近性)下方的方格內。此視窗的目 的是要讓我們選擇變數,並對於Model(模式)、 Restrictions(限制)、Options(選項)、Plots(繪圖)、 Output(輸出)做交代。
圖10-5 「Multidimensional Scaling: Model」視窗設定
限制 在「Multidimensional Scaling(Proximities in Matrices Across Columns)」視窗中按 〔Restrictions〕,就會出現「Multidimensional Scaling: Restrictions」視窗,如圖10-6所示。 此視窗的目的是要我們設定一些限制條件。我們依 照SPSS的內定值作為現制條件,故選擇「No restrictions」(無限制)。
CA一句话题解
1563:四边形不等式推出决策单调,二分决策点.
1568:李超线段树.
1588:权值平衡树.
1798:线段树双标记.但是我是拿LCT写的.
1800:暴力
1821:二分答案+并查集
1853:爆搜
1857:三分套三分.
1875:矩乘优化DP
达式..
2213:DP乱搞一下.附核心代码
for (int i=2;i<=n;++i) ch[i]=getchar(),maxn=max(maxn,ch[i]-'a');
for (int i=1;i<=n;++i)
{
2222:似乎数据有问题还是什么?面向数据了一波.
2223:主席树
2229:GHTree
2241:暴力枚举答案然后检测,检测时候需要特技,感觉理论复杂度并不对但是过了
2243:链剖或者LCT,我写的链剖
2244:cdq分治做三维偏序的DP,答案实际上可能非常大会爆…但是出题人十分懒惰没管那样的数据,所以double就过了
2151:堆+贪心
2154:莫比乌斯反演,有很多种公式化法,我的好像是比较傻逼的那种..
2157:LCT,随便打标记
2163:直接最小割过了,其实应该转对偶图然后最短路才对.
2186:考察线性的逆元求法.
2190:反演分析一下,最后却发现答案是个跟phi有关的表
1007:半平面交
1008:组合数学,需要高精
1010:斜率优化/四边形不等式推决策单调性
并查集超级易懂讲解
高级数据结构设计--并查集及实现学习笔记(有趣篇)并查集的程序设计:为了解释并查集的原理,我将举一个更有趣的例子。
话说江湖上散落着各式各样的大侠,有上千个之多。
他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架。
但大侠们有一个优点就是讲义气,绝对不打自己的朋友。
而且他们信奉“朋友的朋友就是我的朋友”,只要是能通过朋友关系串联起来的,不管拐了多少个弯,都认为是自己人。
这样一来,江湖上就形成了一个一个的群落,通过两两之间的朋友关系串联起来。
而不在同一个群落的人,无论如何都无法通过朋友关系连起来,于是就可以放心往死了打。
但是两个原本互不相识的人,如何判断是否属于一个朋友圈呢?我们可以在每个朋友圈内推举出一个比较有名望的人,作为该圈子的代表人物,这样,每个圈子就可以这样命名“齐达内朋友之队”“罗纳尔多朋友之队”……两人只要互相对一下自己的队长是不是同一个人,就可以确定敌友关系了。
但是还有问题啊,大侠们只知道自己直接的朋友是谁,很多人压根就不认识队长,要判断自己的队长是谁,只能漫无目的的通过朋友的朋友关系问下去:“你是不是队长?你是不是队长?”这样一来,队长面子上挂不住了,而且效率太低,还有可能陷入无限循环中。
于是队长下令,重新组队。
队内所有人实行分等级制度,形成树状结构,我队长就是根节点,下面分别是二级队员、三级队员。
每个人只要记住自己的上级是谁就行了。
遇到判断敌友的时候,只要一层层向上问,直到最高层,就可以在短时间内确定队长是谁了。
由于我们关心的只是两个人之间是否连通,至于他们是如何连通的,以及每个圈子内部的结构是怎样的,甚至队长是谁,并不重要。
所以我们可以放任队长随意重新组队,只要不搞错敌友关系就好了。
于是,门派产生了。
下面我们来看并查集的实现。
int pre[1000];这个数组,记录了每个大侠的上级是谁。
大侠们从1或者0开始编号(依据题意而定),pre[15]=3就表示15号大侠的上级是3号大侠。
ch10
OSLec101操作系统第十讲王海鹏计算机学院OSLec102信号量和PV 原语操作经典进程同步问题ReviewOSLec1033.6.3 进程间通信IPC, INTER-PROCESS COMMUNICATION进程间通信类型消息缓冲通信的实现信箱通信的实现管道通信的实现OSLec104基本概念进程通信是指进程之间可直接以较高的效率传递较多数据的信息交换方式。
低级通信:重在传递控制信息,进行简单的信号交换。
优点:速度快缺点:传送信息量小:效率低,每次通信传递的信息量固定,若传递较多信息则需要进行多次通信。
编程复杂:用户直接实现通信的细节,易出错。
高级通信:高效、大量的数据传递。
OSLec105高级通信的特征通信链路(communication link):点对点/多点/广播 单向/双向有容量(链路带缓冲区)/无容量(发送方和接收方需自备缓冲区)数据格式:字节流(byte stream):各次发送之间的分界,在接收时不被保留,没有格式;报文(datagram/message):各次发送之间的分界,在接收时被保留,通常有格式,定长/不定长报文,可靠/不可靠报文。
收发操作的同步方式发送阻塞和不阻塞 接收阻塞和不阻塞由事件驱动收发:在允许发送或有数据可读时,才做发送和接收操作OSLec1063.6.3.1 进程通信类型共享存储器系统通过数据区的共享,写入与读出达到通信的目的消息传递系统直接通信方式:消息缓冲采用进程的消息缓冲队列消息发送者将消息直接放在接收者的消息缓冲队列间接通信方式:邮箱通信利用中间者——信箱、邮局来传递信件。
发送进程将消息发送到信箱中,接收进程从信箱中取出消息管道通信(共享文件方式)用以连接读、写进程的共享文件OSLec107基于共享数据结构的通信方式诸进程公用某些数据结构,进程通过它们交换信息。
如生产者-消费者问题中的有界缓冲区。
基于共享存储区的通信方式在存储器中划出一块共享存储区,进程通信前,向系统申请共享存储区中的一个分区,申请者把获得的共享存储分区连接到本进程上,此后可读写该分区。
acm 优先队列 并查集 最小生成树
Page 9
ACM暑假培训之优先队列
USC
2012.7.1
优先队列
优先队列,顾名思义,就是一种根据一定优先级存储和取 出数据的队列。它可以说是队列和排序的完美结合体,不 仅可以存储数据,还可以将这些数据按照我们设定的规则 进行排序。 优先队列支持的基本运算有: (1)Min(H):返回优先队列H中具有最小优先级的元素。 (2)Insert(x, H):将元素x插入优先队列H。 (3)DeleteMin(H):删除并返回优先队列H中具有最小优先
Page 15
优先队列的哈夫曼实现
while (!q.empty())//q是按优先值指从小到大的优先队列 {
int x = q.top(); q.pop();
int y = q.top(); q.pop(); sum += (x+y);
}
if (!q.empty())
ACM暑假培训之并查集
USC
2012.7.1
什么是并查集?
一种简单的用途广泛的集合. 并查集是若干个不相交集合, 能够实现较快的合并和判断元素所在集合的操作,应用很 多,如其求无向图的连通分量个数等。最完美的应用当属: 实现Kruskar算法求最小生成树。 (union-find sets)最重要的两个操作就是合并和查找。
8 4 a 8 h g 2
Page 19
b
2 i 7 6
c
7
d
9 14 10
11
4
e
f
最小生成树 求最小生成树的方法主要有以下两种贪心 策略: Prim算法 Kruskal算法
Page 20
Prim算法 贪心准则 加入后仍形成树,且耗费最小
并查集.ppt
路径压缩的递归算法: 如果采用递归算法,一遍即可完成。是边查找边压缩,查 找和压缩同时进行。 function find(i:integer):integer; //递归寻找结点i的树根,并对结点i //递归寻找结点i的树根,并对结点i所在的子树进行路径压 缩,返回调整后的i 缩,返回调整后的i的父指针(根) begin if a[i]=0 then exit(i); //若i为根,返回本身结点序号 //若 if a[a[i]]=0 then exit(a[i]); 若i的父结点为根,则返回父 结点 find:=find(a[i]); //递归找根结点 //递归找根结点 a[i]:=find; //路径压缩,直接指向根 //路径压缩 路径压缩, end;
【样例输入】 样例输入】 11 8 12 45 34 13 56 7 10 5 10 89 【样例输出】 样例输出】 3 样例说明:共三个集团。
【分析】 分析】 上述问题的模型就是求无向图的连通分量。 算法一:采用图的深度优先搜索算法,时间和空 间无法忍受。 算法二:有效算法 (1)开始把n个人看成n个独立集合(团伙)。 )开始把n个人看成n (2)每读入两个有联系(同一团伙)的人i和j, )每读入两个有联系(同一团伙)的人i 查找i 所在的集合f(i)和f(j),如果f(i)和f(j)是同一 查找i和j所在的集合f(i)和f(j),如果f(i)和f(j)是同一 个集合,不作处理;如果f(i)和f(j)属于不同的集合, 个集合,不作处理;如果f(i)和f(j)属于不同的集合, 则合并集合f(i)和f(j)为一个集合。(每个集合可以 则合并集合f(i)和f(j)为一个集合。(每个集合可以 找一个代表元素) (3)最后统计集合的个数即可得到问题的解
O(∑ i ) = O( n )
ch10 数据库系统概念(第6版)第十章存储结构和文件结构
当一个磁盘发生故障,在系统得到修复之前镜像磁盘也发生故障,则 会发生数据丢失
文件组织 – 根据数据访问的方式来组织磁盘的块 ,以优化块访问时间
例如,在相同或者相邻的柱面存储相关信息. 文件可能随着时间推移变得 碎片化 例如,如果数据被插入文件中或者从文件中删除 或者磁盘上的空闲块是分散的, 以致新创建的文件 的块在磁盘上分散分布 顺序存取一个碎片化的文件导致磁盘臂移动距离增 加 有些文件系统提供了碎片整理工具, 以加速文件存取
物理存储介质(续)
光盘存储
非易失性, 数据从旋转的盘上通过激光器进行读取 CD-ROM (640 MB) 和 DVD (4.7 to 17 GB) 是最常见 的格式 蓝光光碟: 27 GB to 54 GB 一次写, 多次读 (WORM) 的光盘用于档案存储 (CD-R, DVD-R, DVD+R) 也有允许多次写的版本 (CD-RW, DVD-RW, DVD+RW, and DVD-RAM) 读写速度比磁盘慢 光盘机系统, 有大量可移动光盘, 几个驱动器, 和用于自 动加载/卸载光盘的机制以存储大量数据
磁盘块存取的优化
块 – 一个磁道上的连续扇区
数据在磁盘和主存储器中通过块传输 大小从 512 至几千字节 小块: 需要更多次传输 大块: 部分填充的块会造成更多空间浪费 如今常见的块大小为 4 至 16 千字节
磁盘臂调度 算法为磁道访问进行排序,以最小化磁盘臂 的移动距离
磁盘块存取的优化(续)
数据结构Ch10习题答案
第十章内部排序一、择题1.用直接插入排序法对下面四个表进行(由小到大)排序,比较次数最少的是(B)。
A.(94,32,40,90,80,46,21,69)插32,比2次插40,比2次插90,比2次插80,比3次插46,比4次插21,比7次插69,比4次B.(21,32,46,40,80,69,90,94)插32,比1次插46,比1次插40,比2次插80,比1次插69,比2次插90,比1次插94,比1次C.(32,40,21,46,69,94,90,80)插40,比1次插21,比3次插46,比1次插69,比1次插94,比1次插90,比2次插80,比3次D.(90,69,80,46,21,32,94,40)插69,比2次插80,比2次插46,比4次插21,比5次插32,比5次插94,比1次插40,比6次2.下列排序方法中,哪一个是稳定的排序方法(BD)。
A.希尔排序B.直接选择排序C.堆排序D.冒泡排序下列3题基于如下代码:for(i=2;i<=n;i++){ x=A[i];j=i-1;while(j>0&&A[j]>x){ A[j+1]=A[j];j--;}A[j+1]=x}3.这一段代码所描述的排序方法称作(A)。
A.插入排序B.冒泡排序C.选择排序D.快速排序4.这一段代码所描述的排序方法的平均执行时间为(D)A.O(log2n) B.O(n) C.O(nlog2n) D.O(n2)5.假设这段代码开始执行时,数组A中的元素已经按值的递增次序排好了序,则这段代码的执行时间为(B)。
A.O(log2n) B.O(n) C.O(nlog2n) D.O(n2)6.在快速排序过程中,每次被划分的表(或了表)分成左、右两个子表,考虑这两个子表,下列结论一定正确是(B)。
A.左、右两个子表都已各自排好序B.左边子表中的元素都不大于右边子表中的元素C.左边子表的长度小于右边子表的长度D.左、右两个子表中元素的平均值相等7.对n个记录进行堆排序,最坏情况下的执行时间为(C)。
ch10图的基本概念
10.2 图与图模型
这样,一位教师如果给多个班级都授课,则在 讨论课时间安排方面则不能冲突,如教师1不能同 时参加班级c1与班级c2的讨论课。这种情况可以用 下图直观地表示。
c1 c2 c7 c6 c3 c4 c5
在上图中,共用了7个小圆圈来表示班级,圆 圈之间的线段表示存在同一个教师参加该二班级的 讨论课,这样就不能安排该二班级同时开展讨论课。 显然,这就给上述问题构建了一个直观的图的模型。
10.2 图与图模型
显然,子图或导出子图可以通过删除一
一个结点到第二个结点之间添加一条边。最后得到的图称为优先
图。下图就是一个优先图的例子。该图表明在执行语句S1、S2与 S4之前不能执行语句S5。
S1 S2 S3 S4 S5 S6 a=0 b=1 c=a+1 d=b+a e=d+1 e=c+d S1 S3 S2 S4
S6
S5
10.2 图与图模型
练习3 在晚会上有n个人,他们各自与自己相识的人 握一次手。已知每人与别人握手的次数都是奇数,
(3)指出与(v2,v3)邻接的边和与(v2,v3)关联的结点;
(4)该图是否有孤立结点和孤立边? ( 5 )求出各结点的度数,并判断是否是完全图和正 则图? (6)该(n,m)图中,n=?,m=?
10.2 图与图模型 图的边数与结点数的关系是图最为重要的属性, 结点的度数满足一个非常简单的关系,即图的每条
问n是奇数还是偶数。为什么?
解 n是偶数。用n个顶点表示n个人,顶点间的一条
边表示一次握手,可构成 一个无向图。若n是奇数,
那么该图的顶点度数之和为奇数个奇数的和,即为奇 数,与图性质矛盾,因此,n是偶数。
10.2 图与图模型 与集合论中研究子集,抽象代数中研究 子代数类似,图论中也研究一个图的子图。 一个图G的子图G′可以通过选取G中的部分 结点与边构成,但要求如果选择了G中的边,
ch树实用学习教程
• 几种特殊形式的二叉树
• 满二叉树定义:
一棵深度为k且有2k 1个结点的二叉树称为~
特点:每一层上的结点数都是最大结点数
❖完全二叉树
定义:深度为k,有n个结点的二叉树当且仅当其每一个结点都与深 度为k的满二叉树中编号从1至n的结点一一对应时,称为~
特点 叶子结点只可能在层次最大的两层上出现 对任一结点,若其右分支下子孙的最大层次为L,则其左分支 下子孙的最大层次必为L 或L+1
第34页/共104页
课堂练习
A B
C
D
F E
G
先序遍历: A B C D E G F 中序遍历: C B E G D F A 后序遍历: C G E F D B A 层次遍历: A B C D E F G
第35页/共104页
课堂练习
-
+ a*
/ ef
b-
cd
例:表达式 a + b * (c – d) – e / f
k
k
(第i层的最大结点数 ) 2i1 2k 1
i 1
i 1
第13页/共104页
❖性质3:对任何一棵二叉树T,如果其终端结点数为n0, 度为2的结点数为n2,则n0=n2+1
证明法一: 设二叉树中度为1的结点数为n1,总结点数为n,总度数 为k,则有下列表达式成立: n=n0+n1+n2 k=0*n0+1*n1+2*n2 k=n-1
}BiTNode, *BiTree;
A
B
C
D
EF
G
A^^
B
^C
^
D
^E
^F
^
^G
^
并查集
原创文章转载注明出处并查集考虑以下一个实际问题:假设一个国家有10个城市,分别编号为1-10。
给出几个城市互相连通的条件,如:给出1 和2 连通,4 和3 连通,3 和5 连通,10和3连通, 最后任意给出2个城市编号,问这2个城市之间是否可以相互到达。
若给出1 和2 则输出可以到达.若给出4 和10 则输出可以到达。
若给出1 和6 输出不可以到达。
我们首先想到可以通过搜索来判断连通性,可是这就意味着对于一个给定的很大的图,每次给出2个城市的编号都要对图进行一次搜索,并且搜索的过程耗时也非常惊人,这时候我们就可以用"并查集"来解决这个问题.并查集,又称不相交集,是一种树型数据结构,顾名思义用来处理不相交集合的求并/查找操作.考虑上面给出的例子, 若设初始时6个城市为6个单独的集合上:(1) (2) (3) (4) (5) (6) (7) (8) (9) (10)那么并差集大概工作方式如何呢?例如我们输入相互连通的城市号1 24 33 510 3我们把求并方法抽象成一个函数unionSets( int root1, int root2 );在unionSets ( 1, 2 ) 之后:(1) (3) (4) (5) (6) (7) (8) (9) (10)(2)然后unionSets (4,3 )unionSets( 3, 5 )(1) (4) (6) (7) (8) (9) (10)(2) (3)(5)然后unionSets (10,3 )(1) (4) (6) (7) (8) (9)(2) (3)(5)(10)其中最上一排的就是根.所以3 的根是4 ,5的根是4 ……9的根是9然后我们抽象出一个方法find ( int x ) 用以求x 的‘根’.最后如果我们执行find 操作: find ( 1 ) == 1; find ( 3 ) == 4, find (5) == 5 find (10) == 4于是find ( 4 ) == find ( 10 )所以根据判断城市4 和城市5 是可以到达的.要实现以上的数据结构其实很简单我们只需要一个数组,就可以模拟这个树形结构:初始时6个元素,定义一个数组,储存的初始状态时:下标: 1 2 3 4 5 6 7 8 9 10值:-1 -1 -1 -1 -1 -1 -1 -1 -1 -1在unionSets(1,2 )unionSets (4,3 )unionSets (3,5)unionSets (10,3)之后:下标: 1 2 3 4 5 6 7 8 9 10值:-1 1 4 -1 4 -1 -1 -1 -1 4所以find ( 4 ) 的值为4 ,find (10)的值为4 判断2者相等就等于证明2者处于一个集合当中。
数据结构_并查集基础
例2 银河英雄传说(NOI2002)
归并操作 Procedure merge(x,y:longint); var i,j:longint; begin i:=getfather(x); j:=getfather(y); father[i]:=j; before[i]:=before[i]+count[j]; count[j]:=count[j]+count[i];{做相应的
分隔。以下K行每行是三个正整 D,X, Y,两数之间用一个空格隔开,其中D 表示说法的种类。 若D=1,则表示X和Y是同类。 若D=2,则表示X吃Y。 输出文件 只有一个整数,表示假话的数目。
.
例3 食物链(NOI2001)
输入文件 100 7 1 101 1 212 223 233 113 231 155
路径压缩实际上是在找完根结点 之后,在递归回来的时候顺便把 路径上元素的父亲指针都指向根 结点
.
路径压缩示意图
1 234 5
由此我们得到了一个复杂度 只是O(1)的算法
.
程序清单
function getfather(v:integer):integer;
begin
if (father[v]=0) then
.
例3 食物链(NOI2001)
明确了上面这个关系,我们就不难从刚才的 算法扩展出另一种算法:对于目前关系未知 的动物X,我们为他新开辟一条食物链 A2,B2,C2,显然,在这个新的组中,动物X 所在的种类也是随意的,于是假设它在A2组, 这样,所有与X的关系就可用与算法1同样的 方式加入这个组中,而这个组与原先的组 A1,B1,C1的关系是不确定的。如此反复, 我们也可以得到组3、组4、组5……,一旦 有一句话牵涉到某两个组的成员之间的食物 链关系,我们就依据一定的换算规则将这两 个组合并起来,以保证关系网的完整性。
并查集算法详解
并查集算法详解并查集算法详解算法详解维护类型⾝为⼀个数据结构,我们的并查集,它的维护对象是我们的关注点.并查集适合维护具有⾮常强烈的传递性质,或者是连通集合性质.性质详解传递性质传递性,也就是具有传递效应的性质,⽐如说A传递给B⼀个性质或者条件,让B同样拥有了这个性质或者条件,那么这就是我们所说的传递性.连通集合性质连通集合性,和数学概念上的集合定义是差不多的, ⽐如说A和B同属⼀个集合,B和C同属⼀个集合,那么A,B,C都属于同⼀个集合.这就是我们所谓的连通集合性质.算法步骤⼀般来说数据结构算法,没有所谓的算法步骤,但是却有半确定的模块功能.初始化操作数据结构的初始化,通常都是有⼀个固定的模块,并查集也不例外.对于并查集⽽⾔,它的初始化,就是指向的⽗亲节点.我们可以想象集合就是⼀个⼩圈⼦,⽽没⼀个⼩圈⼦都得有⼀个圈主,那么显然所以⼈都是围绕着圈主⾏动的.⽐如说Acwing这个⼤圈⼦中,yxc 总裁就是我们的红太阳,圈主⼤⼈.同属于⼀个集合的⼈们,显然每⼀个⼈的指向⽬标,显然都是这个圈⼦的圈主.然⽽刚开始的时候,显然Acwing的成员们,在没有加⼊Acwing的时候,基本上都是素不相识的.因此呢,我们所有⼈肯定是都是属于⾃⼰的⼀个单⼈⼩圈⼦.⾃⼰显然就是⾃⼰这个⼩圈⼦的圈主.综上所述,我们刚开始,每⼀个⼈的指向数组,也就是father数组,肯定都是指向⾃⼰.合并操作两个⼈最远的距离,是沉默,⽽Acwing这个⼤家庭,让你我们更加亲近.海内存知⼰,天涯若⽐邻,⽹络世界的发展,Acwing⽹站的建⽴,沟通了⾝为程序员的你我他.现在你成为了Acwing的⼀员,⽽⼩A同学也成为了Acwing的⼀员.显然通过Acwing这个充满爱的⼤家庭,使得你和⼩A同学产⽣了联系,因此现在你和⼩A同学同属于⼀个名为Acwing的集合.因为你和⼩A同学,需要建⽴⼀种联系,让全世界都知道,你和⼩A同学都来⾃富有爱⼼的⽹站Acwing⼤家庭,所以我们就需要⽤合并操作.⼀个⼈的标签,就是⼀个⼈的指向数组,既然你想和⼩A同学缔结关系的话,那么你和⼩A同学的指向数组就需要开始变化了.⼩A同学是Acwing的⾦牌元⽼,他的指⽰数组就是Acwing,那么⾝为新成员的你需要修改⾃⼰的指向数组,指向⼩A的同学.说明你和⼩A同学存在着上下级关系.路径压缩Acwing是⼀个充满温情的⽹站,上下级这种关系显然⾮常的不友好,那么我们不得不需要斩断这种关系.你指向着⼩A同学,⼩A同学指向着Acwing.这个⼤圈⼦的名字就叫做Acwing,显然⼩A同学和你同属于Acwing⼤圈⼦.为了让上下级关系消失,我们不得不改变我们的集合指向⽅式.我们发现,如果说我们让所有Acwing成员,都指向Acwing这个⼤家庭的话,那么显然我们的上下级关系消失了,取⽽代之的则是我们的⼈⼈平等,互帮互助的友善关系.也就是我们的Acwing精神主旨之⼀.Acwing精神不仅仅使得⼈与⼈之间更加友好,⽽且⼤⼤提⾼了我们的⼯作效率.⽐如说如果说N个⼈,他们之间的关系统统都是上下级关系的话,那么显然我们的⼯作效率会⼤⼤降低.假如说同学6想要告诉Acwing⽹站的yxc总裁,⼀个地⽅有改进优化的建议,那么他需要不停地往上传递信息,效率是O(n)但是如果我们按照⼈⼈平等,互帮互助的Acwing精神主旨之⼀,来进⾏编排的话,那么显然效率会乘坐⽕箭,⼤⼤提⾼.此时我们发现提出⼀个建议的效率,会⼤⼤提⾼,我们⾮常完美的处理,让效率成为了O(1)题⽬选讲第⼀题题⽬描述有n个同学(编号为 1 到n)正在玩⼀个信息传递的游戏。
并查集——精选推荐
并查集并查集(Union-find Sets)是⼀种⾮常精巧⽽实⽤的数据结构,它主要⽤于处理⼀些不相交集合的合并问题。
⼀些常见的⽤途有求连通⼦图、求最⼩⽣成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。
使⽤并查集时,⾸先会存在⼀组不相交的动态集合 $S = \left\{ {{S_1},{S_2}, \cdots ,{S_k}} \right\}$,⼀般都会使⽤⼀个整数表⽰集合中的⼀个元素。
每个集合可能包含⼀个或多个元素,并选出集合中的某个元素作为代表。
每个集合中具体包含了哪些元素是不关⼼的,具体选择哪个元素作为代表⼀般也是不关⼼的。
我们关⼼的是,对于给定的元素,可以很快的找到这个元素所在的集合(的代表),以及合并两个元素所在的集合,⽽且这些操作的时间复杂度都是常数级的。
并查集的基本操作有三个:1. makeSet(s):建⽴⼀个新的并查集,其中包含 s 个单元素集合。
2. unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并。
3. find(x):找到元素 x 所在的集合的代表,该操作也可以⽤于判断两个元素是否位于同⼀个集合,只要将它们各⾃的代表⽐较⼀下就可以了。
并查集的实现原理也⽐较简单,就是使⽤树来表⽰集合,树的每个节点就表⽰集合中的⼀个元素,树根对应的元素就是该集合的代表,如图1 所⽰。
图 1 并查集的树表⽰图中有两棵树,分别对应两个集合,其中第⼀个集合为 $\left\{ a, b, c, d \right\}$,代表元素是 $a$;第⼆个集合为 $\left\{ e, f, g \right\}$,代表元素是 $e$。
树的节点表⽰集合中的元素,指针表⽰指向⽗节点的指针,根节点的指针指向⾃⼰,表⽰其没有⽗节点。
沿着每个节点的⽗节点不断向上查找,最终就可以找到该树的根节点,即该集合的代表元素。
并查集详细讲解(转载)模板
并查集详细讲解(转载)模板小小的得意一下,本人做的第一个并查集完全是自己想象出来的方法,用后感觉效率不错。
后来准备认真学习“标准的”并查集时,发现就是我原来自创的那个方法。
并查集,就是Union-Find Set,也称不相交集合 (Disjoint Set)。
这在数据结构中本是很简单的一种东西。
当然,我是指算法的实现简单,而非其理论分析简单。
但我今天还是要扯一下这个,因为我突然发现我这几天写的并查集都是错的,今天做题时才发现。
所以还是把这个很简单的数据结构总结一下。
正如它的名字揭示的,并查集的基本操作有两个:Union和Find。
其中Find(x)返回元素x所在的集合的编号,Union(i,j)将i与j所在的集合合并,也就是说在此之后对它们中的每一个调用Find的返回值必须是一样的。
实现时采用父亲表示法的树结构,保证所有在一个集合里的元素都在一棵树内,集合的代表元素就是根。
初始时所有节点的父节点都是自身,查找时只须沿着向上的路径查找即可;如果要将两棵树合并只须将其中一棵的根的父节点设为另一棵树的根即可。
最主要的优化是所谓路径压缩。
简言之,就是在Find的实现时并不简单的return find(U[x]); 而是return U[x]=find(U[x]); 这样就将当前节点到根的路径上的所有节点的父亲都设为了根,将原本可能比较长的路径“压缩”了。
当然这是递归的实现,非递归的话需要沿路径走两次,第一次找到根,第二次再改变。
union操作更简单,只是U[find(x)]=find(y);一句而已。
我前两天写Tarjan算法时把这一句错写成U[x]=find(y),竟然也AC了……汗……不过今天做另一道需要并查集的题目就没那么幸运了。
路径压缩的并查集n个操作的序列的均摊复杂度为O(nα(n)),其中α是阿柯曼函数的一个反函数,增长极慢,在可以想象的n的范围内不超过5,故可视为常数。
并查集的一般用途就是用来维护某种具有自反、对称、传递性质的关系的等价类。
集合及其表示等价类与并查集静态搜索叉搜索树最佳二叉
template <class Type> class SetList { private: SetNode<Type> *first, *last; //有序链表的表头指针, 表尾指针 public: SetList ( ) //构造函数 { first = last = new SetNode<Type>(0); } ~SetList ( ) { MakeEmpty( ); delete first; } void MakeEmpty ( ); //置空集合 int AddMember ( const Type& x ); int DelMember ( const Type& x );
பைடு நூலகம்};
使用示例
Set s1, s2, s3, s4, s5; int index, equal; for ( int k = 0; k < 10; k++ ) //集合赋值 { s1.AddMember( k ); s2.AddMember( k +7 ); } // s1 : { 0, 1, 2, …, 9 }, s2 : { 7, 8, 9, …, 16 }
void MakeEmpty ( ) { for ( int i = 0; i < MaxSize; i++ ) bitVector[i] = 0; } int GetMember ( const int x ) { return x >= 0 && x < MaxSize ? bitVector[x] : -1; } int AddMember ( const int x ); int DelMember ( const int x ); Set operator = ( Set& right ); Set operator + ( Set& right );
P3367【模板】并查集
P3367【模板】并查集题⽬描述如题,现在有⼀个并查集,你需要完成合并和查询操作。
输⼊输出格式输⼊格式:第⼀⾏包含两个整数N、M,表⽰共有N个元素和M个操作。
接下来M⾏,每⾏包含三个整数Zi、Xi、Yi当Zi=1时,将Xi与Yi所在的集合合并当Zi=2时,输出Xi与Yi是否在同⼀集合内,是的话输出Y;否则话输出N输出格式:如上,对于每⼀个Zi=2的操作,都有⼀⾏输出,每⾏包含⼀个⼤写字母,为Y或者N输⼊输出样例输⼊样例#1: 4 72 1 21 1 22 1 21 3 42 1 41 2 32 1 4输出样例#1:NYNY说明时空限制:1000ms,128M数据规模:对于30%的数据,N<=10,M<=20;对于70%的数据,N<=100,M<=1000;对于100%的数据,N<=10000,M<=200000。
代码#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<queue>using namespace std;int n,m,z,x,y,fa[10005];int find(int x) {if (x==fa[x]) return x;return fa[x]=find(fa[x]);}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;++i) {fa[i]=i;}while(m--){scanf("%d%d%d",&z,&x,&y); int a=find(x),b=find(y);if(z==1){fa[a]=b;}if(z==2){if(a==b) {printf("Y\n");}else {printf("N\n");}}}return 0;}先做⼀道模板题练练⼿。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
主要实现代码
/* 查找x元素所在的集合,回溯时压缩路径*/ int Find_Set(int x) { if (x != set[x]) set[x] = Find_Set(set[x]); return set[x]; }
主要实现代码
/* 按秩合并x,y所在的集合 下面的那个if else结构不是绝对的,具体根据情况变化 但是,宗旨是不变的即,按秩合并,实时更新秩。*/ void Union(int a, int b) { a = Find_Set(a); b = Find_Set(b); if (a == b) return; if (rank(a) == rank(b)) { rank (a) = rank (a) + 1; set[b] = a; } else if (rank(a) < rank (b)) set[a] = b; else set[b] = a; }
示例—畅通工程(HDOJ-1232)
题目描述: 某省调查城镇交通状况,得到现有城镇道路统计表,表中列 出了每条道路直接连通的城镇。省政府“畅通工程”的目标是 使全省任何两个城镇间都可以实现交通(但不一定有直接的 道路相连,只要互相间接通过道路可达即可)。问最少还需 要建设多少条道路?
/showproblem.php?pid=1232
示例—小希的迷宫(HDOJ-1272)
/showproblem.php?pid=1272 下面的例子,前两个是符合条件的,但是最后一个却有两种 方法从5到达8。
带路径压缩的查找算法(循环)
Find_Set(x) { r = x; while (set[r] !=r) //循环结束,则找到根节点 r = set[r]; i = x; while (i != r) //本循环修改查找路径中所有节点 { j = set[i]; set[i] = r; i = j; } }
Find_Set(x) { r = x; while (set[r] != r) r = set[r]; return r; } 最坏情况Θ(log N)
Θ(1)
进一步优化——路径压缩
思想:每次查找的时候,如果路径较 长,则修改信息,以便下次查找的时 候速度更快 步骤:
第一步,找到根结点 第二步,修改查找路径上的所有节点,将 它们都指向根结点
什么是并查集?
英文:Disjoint Set,即“不相交集合” 将编号分别为1…N的N个对象划分为不相交集合, 在每个集合中,选择其中某个元素代表所在集合。 常见两种操作: 合并两个集合 查找某元素属于哪个集合
所以,也称为“并查集”
实现方法(1)
每个集合用一个成员做为集合的代表,用 来标记这个集合; 定义一个数组 set[1..n] ,其中set[i] 表示 元素i 所在的集合;
i
Set(i) 1 2 3 4 5 6 7 8 9 10
1 2 1 4 2 6 1 6 2 2
不相交集合: {1,3,7}, {4}, {2,5,9,10}, {6,8}
方法(1)——效率分析
Union(a,b) { i = min(a,b); j = max(a,b); for (k=1; k<=N; k++) if (set[k] == j) set[k] = i; } Θ(N)
带路径压缩的查找算法(递归)
Find_Set(x) { if (set[x] !=x) set[x]=Find_Set(set[x]); return set[x]; }
路径压缩示意图
6 4 11 1 12 21 10 20 16 9 8 4 11 1
6
10 9
20
12 8 21 16
主要实现代码
优化后算法及效率
void Union(int a,int b) { a=Find_Set(a); b=Find_Set(b); if(a==b) return; if(rank[a] == rank[b]) { rank[a] = rank[a] + 1; set[b] = a; } else if(rank[a] < rank[b]) set[a] = b; else set[b] = a; }
并查集
(Disjoint S
导引问题
在某个城市里住着 n 个人,任何两个认识 的人不是朋友就是敌人,而且满足:
我朋友的朋友是我的朋友; 我敌人的敌人是我的朋友;
已知关于 n个人的m条信息(即某2个人是 朋友或者敌人),假设所有是朋友的人一 定属于同一个团伙,请计算该城市最多有 多少团伙?
如何实现?
困惑~~~
性能有本质改进?
如何避免最坏情况?
避免最坏情况
方法:将深度小的树合并到深度大的树 实现:假设两棵树的深度分别为h1和h2, 则合 并后的树的高度h是:
max(h1,h2), if h1!=h2. h1+1, if h1==h2.
效果:任意顺序的合并操作以后,包含k个节 点的树的最大高度不超过 lgk
i
Set(i)
1
2
3
2 2 4 7
1
3
4
3
3
4
1 5
3 10 6 8 9
方法(2)——效率分析
Union(a, b) { if (a<b) set[b] = a; else set[a] = b; } Θ(1)
Find_Set(x) { r = x; while (set[r] != r) r = set[r]; return r; } 最坏情况Θ(N) 一般情况是…?
Find_Set(x) { return set[x]; } Θ(1)
有待改进?
对于“合并操作”,必须搜索全部元素!
树结构如何?
实现方法(2)
每个集合用一棵“有根树”表示
定义数组 set[1..n]
set[i] = i , 则i表示本集合,并是集合对应树的根 set[i] = j, j<>i, 则 j 是 i 的父节点. 1 2 3 4 5 6 7 8 9 10
题目分析
最赤裸裸的并查集,无话可说~
附:参考源码(HDOJ-1232)
#include<stdio.h> #define MAX 1002 int set[MAX]; int rank[MAX];
int main() { int n,m,i,x,y,count; while(scanf("%d",&n),n) { for(i=1;i<=n;i++) Make_Set(i); for(scanf("%d",&m);m>0;m--) { scanf("%d %d",&x,&y); Union(x,y); } count=0; for(i=1;i<=n;i++) if(set[i] == i) count ++; printf("%d\n",count-1); }
int set[MAX]; int rank[MAX]; /* set[x]表示x的父节点*/ /* rank[x]表示x的秩*/
/* 初始化集合*/ void Make_Set(int x) { set[x] = x; //根据实际情况指定的父节点可变化 rank[x] = 0; //根据实际情况初始化秩也有所变化 }