算法分析学习笔记(一) - 动态连通性问题的并查集算法(上)

一. 写在前面的话

     “算法分析学习笔记”系列是我在Coursera上选修Sedgewick教授的“Algorithms”公开课过程中积累的一些学习心得。本篇是该系列的第一篇,主题是动态连通性问题(Dynamic Connectivity)。大概在三年以前,当我正头痛于CLRS上晦涩的红黑树介绍时,无意中在网上发现了Sedgewick教授的一篇讲红黑树的ppt,把个稀奇古怪的红黑树讲得浅显易懂,那个时候我花了一个月的时间啃通了老教授的课件,并用C语言自己实现了一遍,才彻底弄懂红黑树那些旋转操作是怎么来的,以及有什么意义。三年后,我已经告别USTC并工作了一年,成为了一名软件工程师。软件开发行业是一个学无止境的行业,因为我们必须要靠自己的知识来为社会创造价值,所以,我们必须保持一颗如饥似渴的心态,不断地正视自身的缺陷和不足,然后想办法弥补。算法一直是我的薄弱环节,听闻老教授终于在Coursera上开课讲算法了,于是买来Algorithms 4ed,准备跟着讲义把老教授的算法课程扎扎实实地再巩固一遍,于是便有了这一个系列的学习笔记。听一遍课,看一遍书,做一遍习题,再总结和整理一遍学习笔记,辅之以有效的学习方法,一定会收到良好的效果,因为“人一能之,己百之;人十能之,己千之。果能此道矣,虽愚必明,虽柔必强”。
     “理论上解决问题”与“实际上解决问题”是有区别的。而学习算法设计与分析的主要目的,就是为了培养解决实际问题的能力。对于某一具体问题而言,一般都能第一时间想到一个“笨方法”来解决,比如简单粗暴地遍历一遍或者穷举所有可能,但解决实际问题时要处理的数据往往很大,如果给出的方法要花几十年或者上百年才算得出结果,这跟没有解决问题有什么区别?学习算法分析与设计,就是学习如何在尽可能快的时间内对(规模较大的)实际问题给出正确结果。
     作为开宗明义的第一篇,本文主要包括两个方面。首先是通过动态连通性问题来展示从问题定义到算法实现再到效率优化最后付诸实际应用的全过程。介绍算法设计与分析的系统化方法。我们先从问题的说明开始,一步一步建立模型,总结该问题解决的实际过程是怎样的,不同解决方案所采用的策略以及它们的效率,最后给出一个该问题在实际生活中的应用场景。本文的第二个部分则围绕“举一反三”展开,谈谈知识迁移能力在算法学习中发挥的作用。介绍了怎样将匈牙利数学家乔治波利亚的方法用于算法学习,并以二叉搜索算法举例如何将已有的知识用于解决新问题,最后给出一个简单的关于“怎样学习算法才有效”的答案。

二. 动态连通性问题

     动态连通性问题的输入是一个整数的点对序列“p q”,它们各自代表某一物体,每一个整数的点对“p q”表示的含义是“p连接到q”。我们的任务就是,写一个程序,以这一点对序列“p q”为输入,当发现“p q”没有连接在一起时,将它们连接起来并在屏幕上输出;否则就忽略掉,继续处理下一项。
     举个例子来说明吧,假设我们有10个物体编号为0到9,一开始这10个物体之间应该是互相没有连接的,而当处理如图2-1所示的点对序列之后,这10个物体最后的连接形式就如图2-2所示,其中灰色数字表示被忽略掉的点对,即已经建立了连接的点对(两幅图均来自于Sedgewick教授算法公开课的课件,下同)。

图2-1 点对序列

图2-2 处理完点对序列后各物体的连接情况

图2-3 实际可能遇到的动态连通性问题
     当然,面对这样一个微不足道,只有10个节点的问题,用算法解决你可能觉得牛刀杀鸡了——凭肉眼我都能自己判断出来。但如果是形如图2-3所示包含成千上万个节点的复杂网络呢?我闭着眼睛随便找两个节点,问你它们之间有没有通路,你还能用肉眼看得出来吗?当然就看不出来了,这个时候对于这种规模庞大的问题,就要用我们的算法来解决了。
     当我们把“物体”这个有点抽象的词,替换为某些我们看得见,摸得着的具体事物的时候,我们就能发现这个抽象问题不同的应用场景。场景一:计算机网络。若这些数字编号的物体是大型计算机网络中的各计算机设备的话,点对序列即表示了计算机之间的通信连接。因此我们的算法能够判断对于某两台计算机p和q,要使它们能够互相通信,是需要建立新的连接,还是可以利用已有的线路建立一个通信路径?场景二:社交网络。数字编号的物体代表Facebook上的用户,而点对表示朋友关系,那么我们的算法就可以在两个用户之间建立朋友关系,或者查找他们俩有没有“可能认识的人”。场景三:变量名的等价性。在某些编程语言中,可以用我们的算法来检测某两个变量是否是等价的(都指向同一对象)。场景四:数学上的集合论。可以判断某两个整数是否属于同一集合。
     看了问题的定义和这么多实用的应用场景,下面我们就要多迈出几步,深入探讨一下动态连通性问题是怎么解决的。这类定义良好的问题已经得到了很好的解决,解决它们的算法被命名为“并查集算法”。当然,仅仅知道一个名字是远远不够的——这个算法怎么想出来的?它解决问题的策略是什么?有什么招数值得一学?OK,下面我们先从算法分析与设计的系统化方法开始讲起。

2.1 算法分析与设计的系统化方法

     Sedgewick教授有在课件中提到一个系统的,算法分析与设计相辅相成的步骤:
  • Model the system.
  • Find an algorithm to solve it.
  • Fast enough? Fits in memory?
  • If not, figure out why.
  • Find a way to address the problem.
  • Iterate until satisfied.
     对问题进行建模其实是最关键的一步,它的意义在于把有助于解决该问题的信息抽取出来,把与问题无关的一些细节忽略掉(比如节点到底表示变量名还是表示计算机设备),建立抽象的数学模型。之后,我们的注意力就转移到这个数学模型上。设计一个算法来处理问题,算法够快吗?所占用的内存合理吗?若分析得到的结论不尽如人意,就要弄清楚为什么会这样,找到问题的根源然后想办法解决它。之后重新评估,一直不停地迭代这一过程,知道满足要求为止。下面我们就结合动态连通性问题实践一下系统化方法。

2.2 建立问题模型

     把一切无关的细节忽略掉,这个问题其实包含了两个要害:一是物体;二是连接。从问题的描述中可以看到,物体是用整数代表的,且N个物体的编号为0到N-1——自然联想到使用一个数组id[]来建模这些物体;那么连接呢
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值