图
图是一种数据结构,其中节点可以具有零个或者多个相邻的元素,两个节点之间的连接成为边。节点也可以成为顶点。
- 邻接表: 邻接表一般采用数组+链表的形式,数组表示各个顶点,链表中的元素表示该顶点与链表中的元素相连,与链表本身的指针没有关系。如上图 数组0 对应的链表1->3->4 表示0这个顶点与1 3 4这个顶点连接 数组1 表示1这个顶点与 0 2 4顶点相连以此类推
- 邻接矩阵和邻接表的区别 邻接矩阵会为每个顶点都分配N个边的空间,其实很多表都不存在,存在了空间的损失,邻接表值关心存在边,因此没有空间的损失。
常用概念
- 顶点 vertex 图中的每一个节点都是一个顶点 通常记做V
- 边 edge 两个顶点之间的连接 通常记做E
- 路径 从一个顶点到另一个顶点经过的边的总和,如果带权则是所有边的权重和
- 无向图 顶点与顶点之间没有方向,即可以从顶点A到顶点B,也可以动B到A
- 有向图 与无向图相反顶点与顶点之间存在明显的方向
- 带权图 边上面带有权值的图称为带权图
- 弧头 弧尾 在有向图中,无箭头端的顶点称为起始点或者弧尾,有箭头的顶点称为终端点或者弧头
- (V1,V2)和<V1,V2>的区别 ,(V1,V2)表示无向图中2个顶点V1和V2,<V1,V2>表示有向图V1到V2的单向关系
深度优先
图的深度优先算法 depth first search
1. 访问初始节点V,将V节点置为已访问状态
2. 查询V节点的第一个邻接节点W
3. 如果W不存在,则退回到第一步,从V的下一个节点继续调用
4. 如果W存在,判断W是否访问过
1. 如果W没有访问过,标记W为已访问,再以W为初始节点V继续递归深度算法
2. 如果W已经访问过,节点V的W邻接点查找下一个邻接点,然后回到第三步
广度优先
类似一个分层搜索,广度优先遍历需要使用一个队列来保持访问过的节点的顺序,以便按这个顺序来访问这些节点的邻接节点,
广度优先算法先将一个顶点的所有直接连接的顶点访问一遍,然后再访问下一个顶点所有直连的顶点依次循环
实现步骤
1. 1.访问初始节点V,标记节点V已访问
2. 节点V入队列
3. 当队列非空时,取对头节点,否则一直循环
4. 出队列得到队头节点U
5. 查找节点U的第一个邻接点W
6. 若节点U的邻接点W,判断W是否存在
1. W不存在访问第三步继续执行
2. W存在 若W未访问,标记W为已访问,W加入队列,将U节点邻接W节点的后一个邻接点赋值给W 重复第六步
class Graph(object):
def __init__(self, n):
# 顶点集合
self.vertexes = []
# 邻接矩阵
self.edges = [[0 for i in range(n)] for _ in range(n)]
self.edges_count = 0
def add_vertex(self, vertex):
"""
添加顶点
:param vertex:
:return:
"""
self.vertexes.append(vertex)
def insert_edge(self, v1, v2, weight):
"""
添加边
:param v1:
:param v2:
:return:
"""
self.edges[v1][v2] = weight
self.edges[v2][v1] = weight
def show(self):
print(end='\t')
for s in self.vertexes:
print(s, end='\t')
print()
for i in range(len(self.edges)):
edge = self.edges[i]
print(self.vertexes[i], end='\t')
for j in edge:
print(j, end='\t')
print()
def dfs(self):
visited = [False for i in range(len(self.vertexes))]
for i in range(len(self.vertexes)):
if visited[i] is False:
self._dfs(i, visited)
def get_first_neighbor(self, index):
"""
获取某一个顶点的第一个邻接节点
-1 表示没有任何节点与他连接
返回第一个邻接节点的索引位置
:return:
"""
first_neighbor_index = 0
while first_neighbor_index < len(self.vertexes):
if self.edges[index][first_neighbor_index] == 1:
return first_neighbor_index
else:
first_neighbor_index += 1
return -1
def get_next_neighbor(self, v1, v2):
"""
获取从v1节点从v2节点开始之后的一个邻接节点
:param v1:
:param v2:
:return:
"""
next_index = v2 + 1
for i in range(next_index, len(self.vertexes)):
if self.edges[v1][i] > 0:
return next_index
else:
next_index += 1
return -1
def _dfs(self, index, visited):
"""
图的深度优先算法 depth first search
1. 访问初始节点V,将V节点置为已访问状态
2. 查询V节点的第一个邻接节点W
3. 如果W不存在,则退回到第一步,从V的下一个节点继续调用
4. 如果W存在,判断W是否访问过
1. 如果W没有访问过,标记W为已访问,再以W为初始节点V继续递归深度算法
2. 如果W已经访问过,节点V的W邻接点查找下一个邻接点,然后回到第三步
:param index 访问顶点的索引位置
:param visited 记录是否访问过顶点的数组
:return:
"""
# 1.访问初始节点V,将V节点置为已访问状态
print(self.vertexes[index], end='->')
visited[index] = True
# 2.查询V节点的第一个邻接节点W
w = self.get_first_neighbor(index)
# 3. 如果W存在,判断W是否访问过
while w != -1:
# 1. 如果W没有访问过,标记W为已访问,再以W为初始节点V继续递归深度算法
if not visited[w]:
visited[w] = True
self._dfs(w, visited)
# 2. 如果W已经访问过,节点V的W邻接点查找下一个邻接点,然后回到第三步
else:
w = self.get_next_neighbor(index, w)
# 4.如果W不存在,则退回到第一步,从V的下一个节点继续调用
def bfs(self):
visited = [False for _ in range(len(self.vertexes))]
for i in range(len(self.vertexes)):
self._bfs(i, visited)
def _bfs(self, v, visited):
"""
广度优先遍历 board first search
类似一个分层搜索,广度优先遍历需要使用一个队列来保持访问过的节点的顺序,以便按这个顺序来访问这些节点的邻接节点,
广度优先算法先将一个顶点的所有直接连接的顶点访问一遍,然后再访问下一个顶点所有直连的顶点依次循环
实现步骤
1. 1.访问初始节点V,标记节点V已访问
2. 节点V入队列
3. 当队列非空时,取对头节点,否则一直循环
4. 出队列得到队头节点U
5. 查找节点U的第一个邻接点W
6. 若节点U的邻接点W,判断W是否存在
1. W不存在访问第三步继续执行
2. W存在 若W未访问,标记W为已访问,W加入队列,将U节点邻接W节点的后一个邻接点赋值给W 重复第六步
:return:
"""
# 访问初始节点V,标记节点V已访问
v_queue = []
visited[v] = True
print(self.vertexes[v], end='->')
vertexes.append(v)
# 当队列非空时,取对头节点,否则一直循环
while v_queue:
# 出队列得到队头节点U
u = v_queue.pop(0)
# 查找节点U的第一个邻接点W
w = self.get_first_neighbor(u)
while w != -1:
if not visited[w]:
# W存在 若W未访问,标记W为已访问
print(self.vertexes[w], end='->')
visited[w] = True
# W加入队列
v_queue.append(w)
# 将U节点邻接W节点的后一个邻接点赋值给W 重复第六步
w = self.get_next_neighbor(u, v)
else:
# W不存在访问第三步继续执行
pass
#
if __name__ == '__main__':
n = 5
graph = Graph(5)
vertexes = ["A", "B", "C", "D", "E"]
for v in vertexes:
graph.add_vertex(v)
graph.insert_edge(0, 1, 1) # A->B
graph.insert_edge(0, 2, 1) # A->C
graph.insert_edge(1, 2, 1) # B->C
graph.insert_edge(1, 3, 1) # B->D
graph.insert_edge(1, 4, 1) # B->E
graph.show()
graph.dfs()
print()
graph.bfs()