1.图的概念
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图中G中顶点的集合,E是图G中边的集合。
图有两种:无向图和有向图。
1.1 无向图
无向图:若顶点
v
i
v_i
vi 和
v
j
v_j
vj之间没有方向,则称这条边为无向边。如果图中任意两个顶点之间的边都是无向边,则称该图为无向图(如下图所示)。在无向图中,若任意两个顶点之间都存在边,则称为无向完全图。
1.2 有向图
有向图:若顶点 v i v_i vi 和 v j v_j vj之间有方向,则称这条边为有向边,称该图为有向图(如下图所示)。
2. 图的表示方法
实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法
图G=(V,E)有两种标准的表示方法,即邻接表和邻接矩阵。这两种表示法既可用于有向图,也可用于无向图。通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑。但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示。
** 无向图的邻接表和邻接矩阵表示:**
有向图的邻接表和邻接矩阵表示:
下面给出有向图的邻接矩阵和邻接表的代码实现,无向图的邻接矩阵是关于有向图矩阵的原矩阵与转置矩阵相加。
例如:
import numpy as np
#每个顶点使用字典来跟踪它连接的顶点和每个边的权重。这个字典称为'connectedTo'。
class Vertex:
def __init__(self,key):
self.id = key
self.connectedTo = {}
#从这个顶点添加一个连接到另一个
def addNeighbor(self,nbr,weight = 0):
self.connectedTo[nbr] = weight
def __str__(self):
return str(self.id) + 'connectedTo' + str([x.id for x in self.connectedTo])
#返回邻接表中的所有的项点
def getConnections(self):
return self.connectedTo.keys()
def getId(self):
return self.id
#返回从这个顶点到作为参数顶点的边的权重
def getweight(self,nbr):
return self.connectedTo[nbr]
class Graph:
def __init__(self):
self.vertList = {}
self.numVertices = 0
def addVertex(self,key):
self.numVertices = self.numVertices + 1
newVertex = Vertex(key)
self.vertList[key] = newVertex # 添加一个node
# print(self.vertList)
def getVertex(self,n):
if n in self.vertList:
return self.vertList[n]
else:
return None
def __contains__(self, n):
return n in self.vertList
def addEdge(self,f,t,const = 0): # 添加边
if f not in self.vertList:
self.addVertex(f) # 添加该node 到vertlist中
if t not in self.vertList:
self.addVertex(t)
self.vertList[f].addNeighbor(self.vertList[t],const)
def getVertices(self):
return self.vertList.keys()
def __iter__(self):
return iter(self.vertList.values())
def Print_AdjMatrix(self,g,flag):
arr=np.zeros((6,6))
for v in g:
for w in v.getConnections():
if flag:
arr[int(v.getId())][int(w.getId())]=int(v.getweight(w)) #邻接矩阵 打印有权有向图
else:
arr[int(v.getId())][int(w.getId())]=1 # 邻接矩阵 打印无权有向图
print(arr)
return arr
# In[]
if __name__ == '__main__':
g = Graph()
for i in range(6):
g.addVertex(i)
# print(g.vertList)
g.addEdge(0,1,5)
g.addEdge(0,5,2)
g.addEdge(1,2,4)
g.addEdge(2,3,9)
g.addEdge(3,4,7)
g.addEdge(3,5,3)
g.addEdge(4,0,1)
g.addEdge(5,4,8)
g.addEdge(5,2,1)
print("打印输出无权有向图的邻接矩阵:")
arr=g.Print_AdjMatrix(g,flag=0)
print("打印输出无权无向图的邻接矩阵:")
print(arr.T+arr)
print("打印输出有权有向图的邻接矩阵:")
arr=g.Print_AdjMatrix(g,flag=1)
print("打印输出有权无向图的邻接矩阵:")
print(arr.T+arr)
以上代码的参考链接:www.cnblogs.com/chay/articles/10259422.html
3.图的搜索
实现图的深度优先搜索、广度优先搜索
3.1深度优先搜索:
深度优先搜索也称为回溯(back tracking),其基本思想:
(1) 任意选择图G的一个顶点V0作为根,通过相继地添加边来形成在顶点V0开始的路,其中每条新边都与路上的最后一个顶点以及不在路上的一个顶点相关联。
(2) 继续尽可能多地添加边到这条路。若这条路经过图G的所有顶点,则这条路即为G的一棵生成树;
(3)若这条路没有经过G的所有顶点,不妨设形成这条路的顶点顺序V0,V1,…,Vn。则返回到路里的次最后顶点V(n-1).
若有可能,则形成在顶点v(n-1)开始的经过的还没有放过的顶点的路;
否则,返回到路里的顶点v(n-2)。
(4) 然后再试。重复这个过程,在所访问过的最后一个顶点开始,在路上次返回的顶点,只要有可能就形成新的路,直到不能添加更多的边为止。
用深度优先搜索来找出图3-9所示图G的生成树,任意地从顶点d开始,生成步骤显示在图3-10。
3.2 广度优先搜索:
可用广度优先搜索(breadth first search)来产生连通简单图的生成树。
其基本思想:
(1) 从图的顶点中任意第选择一个根,然后添加与这个顶点相关联的所有边,在这个阶段添加的新顶点成为生成树里1层上的顶点,任意地排序它们。
(2) 下一步,按照顺序访问1层上的每一个顶点,只要不产生回路,就添加与这个顶点相关联的每个边。这样就产生了树里2的上的顶点。遵循同样的原则继续下去,经有限步骤就产生了生成树。
用广度优先搜索找出图3-9所示图G的生成树,选择顶点f作为根:
参考链接:
https://www.cnblogs.com/5poi/p/7466760.html
4. Dijkstra算法与 A* 算法
实现 Dijkstra 算法、A* 算法
实现拓扑排序的 Kahn 算法、DFS 算法
对应的 LeetCode 练习题
Number of Islands(岛屿的个数)
英文版:https://leetcode.com/problems/number-of-islands/description/
中文版:https://leetcode-cn.com/problems/number-of-islands/description/
Valid Sudoku(有效的数独)
英文版:https://leetcode.com/problems/valid-sudoku/
中文版:https://leetcode-cn.com/problems/valid-sudoku/
参考链接
https://blog.csdn.net/qq_35295155/article/details/78639997
https://www.cnblogs.com/dzkang2011/p/graph_1.html
https://blog.csdn.net/x_i_y_u_e/article/details/46488815
www.cnblogs.com/chay/articles/10259422.html