邻接矩阵和邻接表
在讲数据结构的那一节里我们提到了图这种数据结构,并且介绍了两种用来表示图的方法:邻接矩阵和邻接表。接下来我们来分别看看它们是如何表示一张图的吧。
对于图而言,节点之间的连接是不受限制的,即一个节点可以连接任意节点(包括自身),并且还具有方向性。所以,我们可以用 0 和 1 两种状态来表示一个节点
根据上面的矩阵,我们可以得知节点
如果我们用一个链表的形式来表示一个节点到与之相连的全部节点,那么我们就可以得到一个由多个链表构成的数组。举个例子,上面的图中,节点
深度优先遍历
在了解两种遍历算法之前,我们先要知道什么是遍历。遍历(traversal)是对树或图这种相对复杂的数据结构中的每一个节点进行逐一访问的过程。
先来介绍一下深度优先遍历(depth-first search),顾名思义它与深度有关系。其主要思想是:从一个节点出发,我们需要尽可能远地探索未访问的节点,直到不能继续探索为止,也就是遇到了“死胡同”(dead end),举个例子,我们要对下面的图进行遍历。
假设从节点
访问完节点
当节点
然后重复上述的过程,直到我们退回到了节点
从上面的过程我们可以看出,深度优先遍历是需要用递归或者栈来实现的(因为后加入的节点要先离开),要实现这个代码,首先我们要用一个类来表示图中的节点。
class
然后实现深度优先遍历的过程:
def
我们看到,由于节点
def
因此,我们要从节点
广度优先遍历
和深度优先遍历不同的是,广度优先遍历(breadth-first search)尽可能广(多)地遍历其他相邻的节点,也就是说节点是一层一层遍历的。
还是那上面的图为例,要实现广度优先遍历,需要用到队列这种数据结构。
首先是节点
接下来
此时节点
然后我们重复这个过程,最后得到:
下面是代码实现过程:
def
同样,对于那些没有相互连接的图,我们处理的方式跟上面是一样的。
def
因此我们要再从节点
那么图的遍历跟暴力求解有什么关系呢?事实上,不管是深度优先遍历还是广度优先遍历,我们可以发现,每一个节点都执行了一次遍历算法,正是通过这种方式,才保证了图的每一个节点能够被访问,达到遍历的效果。后面我们讲到的暴力搜索也是同样的思想。
复杂度分析
下面来谈谈遍历图的复杂度。我们知道图可以由邻接矩阵或邻接表来表示,因此我们要分别来讨论:
如果我们用邻接矩阵来表示,在执行遍历算法的过程中,由于每个节点都要查看其他节点是否与自己相连,即矩阵中每一行的 0 和 1,所以对于一个有
邻接表由于只记录了每个节点和与之相连节点的信息,即每条边的信息。所以在遍历的过程中,我们需要
→本节全部代码←
← 字符串匹配(BF)| 算法与复杂度zhuanlan.zhihu.com