1.5案例研究:union-find算法

案列研究的目的:
★ 优秀的算法因为能够解决实际问题而变得更为重要;
★ 高效算法的代码也可以很简单;
★ 理解某个实现的性能特点是一项有趣而令人满足的挑战;
★ 在解决同一个问题的多种算法之间进行选择时,科学方法是一种重要的工具;
★ 迭代式改进能够让算法的效率越来越高 。

1.5.1 动态连通性

       问题的输入是一列整数对,其中每个整数都表示一个某种类型的对象,一对整数 p、q 可以被理解为“ p 和 q 是相连的”。 我们假设“相连”是一种等价关系,这也就意味着它具有:
★ 自反性:p 和 p 是相连的;
★ 对称性:如果 p 和 q 是相连的,那么 q 和 p 也是相连的;
★ 传递性:如果 p 和 q 是相连的且 q 和 r 是相连的,那么 p 和 r 也是相连的 。
       等价关系能够将对象分为多个等价类 。 在这里,当且仅当两个对象相连时它们才属于同一个等价类 。 我们的目标是编写一个程序来过滤掉序列中所有无意义的整数对(两个整数均来自于同一个等价类中)。 换句话说,当程序从输入中读取了整数对 p q 时,如果已知的所有整数对都不能说明 p和 q 是相连的,那么则将这一对整数写入到输出 中 。 如果已知的数据可以说明 p 和 q 是相连的,那么程序应该忽略 p q这对整数并继续处理输入中的下一对整数。
算法4-1.5案例研究:union-find算法

1.5.1.1 网络

       输入中的整数表示的可能是一个大型计算机网络中的计算机,而整数对则表示网络中的连接 。这个程序能够判定我们是否需要在 p 和 q 之间架设一条新的连接才能进行通信,或是我们可以通过己有的连接在两者之间建立通信线路;或者这些整数表示的可能是电子电路中的触点,而整数对表示的是连接触点之间的电路;或者这些整数表示的可能是社交网络中的人,而整数对表示的是朋友关系 。 在此类应用中,我们可能需要处理数百万的对象和数十亿的连接 。

1.5.1.2 变量名等价性

       某些编程环境允许声明两个等价的变量名(指向同一个对象的多个引用)。 在一系列这样的声明之后 ,系统需要能够判别两个给定的变量名是否等价 。 这种较早出现的应用(如 FORTRAN 语言)推动了我们即将同论的算法的发展 。

1.5.1.3 数学集合

       在更高的抽象层次上,可以将输入的所有整数看做属于不同的数学集合 。 在处理一个整数对 p q 时,我们是在判断它们是杏属于相同的集合 。 如果不是 ,我们会将 p 所属的集合和 q 所属的集合归并到同一个集合 。
       将对象称为触点,将整数对称为连接,将等价类称为遥远分量或是简称分量 。
       我们在设计算法时面对的第一个任务就是精确地定义问题 。 我们希望算法解决的问题越大,它完成任务所需的时间和空间可能就越多 。 我们不可能预先知道这其间 的量化关系, 而且我们通常只会在发现解决问题很困难 , 或是代价巨大,或是在幸运地发现算法所提供的信息比原问题所需要的更加有用时修改 问题 。 例如 ,连通性问题只要求我们 的程序能够判别给定的整数对 p q 是否相连,但并没有要求给 出 两者之间的通路上 的所有连接 。 这样的要求会使问题更加 困难,并得到另一组不同的算法。
算法4-1.5案例研究:union-find算法
       如果两个触点在不同 的分盘中,union() 操作会将两个分量归并 。find () 操作会返回给定触点所在的连通分量的标识符。connected () 操作能够判断两个触点是否存在于同一个分量之中 。count () 方法会返回所有连通分量的数量 。 一开始我们有 N 个分量 ,将两个分量归并的每次union () 操作都会使分量总数减一。
       数据结构的性质将直接影响到算法的效率 ,因此数据结构和算法的设计是紧密相关的 。API 已经说明触点和分量都会用int值表示,所以我们可以用一个以触点为索引的数组 id[ ]作为基本数据结构来表示所有分量 。 我们将使用分量中的某个触点的名称作为分量的标识符,因此你可以认为每个分量都是由它的触点之一所表示的 。 一开始,我们有 N 个分量 , 每个触点都构成了一个只含有它自己的分量,因此我们将id[ ]的值初始化为i,其中i 在 0 到 N- 1 之间 。 对于每个触点 1 ,我们将 find () 方法用来判定它所在的分量所需的信息保存在id[i]之中。
算法4-1.5案例研究:union-find算法
算法4-1.5案例研究:union-find算法
union-find 的成本模型: 在研究实现 union-find 的 API的各种算法时,我们统计的是数组的访问次数(访问任意数组元素的次数,无论读写)。

1.5.2 实现
1.5.2.1 quick-find 算法

       一种方法是保证当且仅当 id [p ] 等于 id [q ] 时 p 和 q 是连通的 。 换句话说,在同一个连通分盘中的所有触点在 id[ ]中的值必须全部相同 。 这意味着 connected(p,q ) 只需要判断id[p] ==id [q ] ,当且仅当 p 和 q 在同一连通分量中该语句才会返回 true 。 为了调用 union(p ,q ) 确保这一点,我们首先要检查它们是否已经存在于同一个连通分量之中 。 如果是我们就不需要采取任何行动,否则我们面对的情况就是 p 所在的连通分量中的所有触点的 id[ ]值均为同一个值,而 q 所在的连通分盘中的所有触点的id[ ]值均为另 一个值。 要将两个分量合二为一 ,我们必须将两个集合中所有触点所对应的id[ ]元素变为同一个值。为此,我们需要遍历整个数组,将所有和id [p ] 相等的元素的值变为 id [q ] 的值。 我们也可以将所有和 id [q ] 相等的元素的值变为id [p]的值一一两者皆可 。
算法4-1.5案例研究:union-find算法

相关文章: