并查集平摊代价分析

并查集平摊代价分析

1.并查集链表(Linked-list representation of disjoint sets)

1.1 最糙的链表

链表的每个结点有三个域,分别是head,rep,tail
head是个指针,指向这个集合的第一个元素,tail也是个指针,指向这个集合的最后一个元素,rep是某个元素的代表值图1-1
如上图所示,这是一个S1∪S2的操作,即将S2的每个结点的head指针都指向S1的第一个元素即f,将S1的每个结点的尾指针都指向S2的最后一个元素即h
这么操作的时间复杂度是 θ ( n 2 ) \theta(n^2) θ(n2)
假设我们要合并n个集合,每个集合都刚刚初始化为仅含有1个元素因为合并集合是两两进行合并的,每合并一次都要修改i次头指针或者i次尾指针,(i为二者元素的个数的最大值)即每次合并都要更新i个域,那么进行n-1次合并:
∑ i = 1 n − 1 i = θ ( n 2 ) \sum^{n-1}_{i=1}i=\theta(n^2) i=1n1i=θ(n2)

1.2 做一些优化:基于权值的启发式并集链表(weighted-union heuristic)

每个链表中的域添加一个length,这样每次都可以将小链表接到大链表的后面
我们先仅仅考虑一个元素: x i x_i xi
一开始的时候它是一个元素,然后它会与另一个只有一个元素的集合合并,这个时候这个集合是两个元素;当它下一次加入到别的集合的时候,他一定是比较小的那个,即另一个集合一定有大于等于两个元素,这就意味着每次合并,集合元素数量针对于小的那个集合一定会翻倍
因此如果最后那个集合有n个集合元素的话,那个集合中每个元素都做了 l g n lgn lgn次合并,那么n个元素就是nlgn,即合并操作花费 n l g n nlgn nlgn时间
如果进行了m次Make_Set操作,所有的花费时间就是 θ ( m + n l g n ) \theta(m+nlgn) θ(m+nlgn)

2 并查集森林(disjoint-set forest)

2.1 两个启发式方法

2.1.1. 根据秩合并(union by rank)

秩(rank):是一棵树的层数的上界,意思就是这棵树有多少层,秩就是多少

2.1.2. 路径压缩(path compression)

执行Find(x)操作的时候,从x到根节点路径上的元素所组成的集合中的所有元素都指向一个最后找到的那个节点
简单来说就是这条路径上的元素从x开始一直到终点r之前的那个元素在Find(x)之后都指向r,这样就方便了后续的查找
Find操作不改变任何结点的秩
在这里插入图片描述
如图所示,Find(a)之前集合是长成左图(a)的样子,Find(a)之后集合就长成了右图(b)的样子

2.2 维护秩

秩依据以下三点进行维护:
1.MakeSet(x)操作执行时定义结点的秩为0(即初始化时结点的秩为0)
2.合并时如果rank(x) = rank(y),那么让x指向y的代表元素,并且y的秩+1
3.合并时如果rank(x) < rank(y),那么rank(y)和rank(x)的秩保持不变,并且让x指向y或者y指向x
注意:以上操作其他结点的秩不发生改变
在这里插入图片描述
图中情形a、b分别代表了合并时的两种情况

2.3 对于使用两种方法的并查集森林的平摊分析

2.3.1 阿克曼函数:

简单说一下这个函数:
对于任何整数j:
A 1 ( j ) = A 0 ( j + 1 ) ( j ) = A 0 ( A 0 ( A 0 . . . A 0 ( j ) ) . . . ) = j + ( 1 + . . . + 1 ) = 2 j + 1 \begin{aligned} A_1(j) &amp;= A_0^{(j+1)}(j)\\&amp;=A_0(A_0(A_0...A_0(j))...) \\&amp;= j+(1+...+1)\\&amp;= 2j+1\\ \end{aligned} A1(j)=A0(j+1)(j)=A0(A0(A0...A0(j))...)=j+(1+...+1)=2j+1
A 2 ( j ) = A 1 ( j + 1 ) ( j ) = A 1 ( A 1 ( A 1 . . . A 1 ( j ) ) . . . ) = 2 j A 1 ( j ) + 2 j − 1 + . . . 2 + 1 = 2 j + 1 ( j + 1 ) − 1 \begin{aligned} A _2(j) &amp;= A_1^{(j+1)}(j)\\&amp;=A_1(A_1(A_1...A_1(j))...)\\&amp;=2^jA_1(j)+2^{j-1}+...2+1\\&amp;=2^{j+1}(j+1)-1 \\\end{aligned} A2(j)=A1(j+1)(j)=A1(A1(A1...A1(j))...)=2jA1(j)+2j1+...2+1=2j+1(j+1)1
A 3 ( 1 ) = A 2 ( 2 ) ( 1 ) = A 2 ( A 2 ( 1 ) ) = A 2 ( 7 ) = 2047 \begin{aligned} A_3(1)&amp;=A_2^{(2)}(1)\\&amp;=A_2(A_2(1))\\&amp;=A_2(7) \\&amp;= 2047\\ \end{aligned} A3(1)=A2(2)(1)=A2(A2(1))=A2(7)=2047
A 4 ( 1 ) = A 3 ( 2 ) = A 3 ( 2047 ) = A 2 ( 2048 ) ( 2047 ) &gt; &gt; A 2 ( 2047 ) = 2 2048 ⋅ 2048 − 1 &gt; 2 2048 = 1 6 512 &gt; &gt; 1 0 80 \begin{aligned} A_4(1)&amp;=A_3^{(2)} \\&amp;= A_3(2047)\\&amp;=A_2^{(2048)}(2047)\\&amp;&gt;&gt;A_2(2047)\\&amp;=2^{2048}·2048-1\\&amp;&gt;2^{2048} \\&amp;= 16^{512}\\&amp;&gt;&gt;10^{80} \end{aligned} A4(1)=A3(2)=A3(2047)=A2(2048)(2047)>>A2(2047)=2204820481>22048=16512>>1080
因此在宇宙范围之内我们能遇到的数字都不会超过 A 4 ( 1 ) A_4(1) A4(1)

2.3.2 α ( n ) \alpha(n) α(n)

定义 A ( k ) 的 逆 函 数 α ( n ) = m i n { k ∣ A k ( 1 ) ≥ n } A(k)的逆函数\alpha(n)=min\{k|A_k(1)\ge{n\}} A(k)α(n)=min{ kAk(1)n}
在这里插入图片描述
阿克曼函数是严格单调递增的且增长速度非常快, A k ( n ) &gt; n A_k(n)&gt;n Ak(n)>n
α ( n ) \alpha(n) α(n)是单调不减的且增长速度缓慢

2.3.3 秩的性质

定义 x . p x.p x.p为指向 x x x的父节点的指针

引理1:对于每个结点x, x . p . r a n k ≥ x . r a n k x.p.rank \ge x.rank x.p.rankx.rank,之后 x . r a n k x.rank x.rank不再发生改变,且 x . p . r a n k x.p.rank x.p.rank单调不减

显然,x与x的父节点合并之前有两种情况:
1. r a n k ( x ) = r a n k ( y )

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值