Python数据结构之图基础
什么是图?
- 表示多对多的关系
- 一组顶点,通常用V(Vertex)表示顶点集合
- 一组边,通常用E(Edge)表示边的集合,边是顶点对,分为有向边和无向边
图的创建
邻接矩阵
- 代码
class Graph:
    """创建图"""
    def __init__(self, n):
        
        self.vertex_list = []
        self.edges = [[0 for i in range(n)] for j in range(n)]  # 初始化图
        self.num_of_edges = 0  # 记录有效边数目
    def get_num_of_vertex(self):
        """获取顶点数目"""
        return len(self.vertex_list)
    def get_val_by_index(self, index):
        """返回结点的下标"""
        return self.vertex_list[index]
    def get_wight(self, v1, v2):
        """获取边的权重"""
        return self.edges[v1][v2]
    def show_graph(self):
        for col in self.edges:
            print(col)
    def insert_edge(self, v1, v2, weight):
        """插入边"""
        self.edges[v1][v2] = weight
        self.edges[v2][v1] = weight
        self.num_of_edges += 1
    def insert_vertex(self, vertex):
        """插入顶点"""
        
        self.vertex_list.append(vertex)
        
        
if __name__ == "__main__":
    graph = Graph(5)
    print("原始图结构:")
    vertex_val = ["A", "B", "C", "D", "E"]
    for vertex in vertex_val:
        graph.insert_vertex(vertex)
    graph.show_graph()
    graph.insert_edge(1, 2, 5)
    graph.insert_edge(2, 4, 6)
    graph.insert_edge(3, 1, 4)
    graph.insert_edge(2, 2, 5)
    print("增加边的结构:")
    graph.show_graph()- 结果
原始图结构:
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
增加边的结构:
[0, 0, 0, 0, 0]
[0, 0, 5, 4, 0]
[0, 5, 5, 0, 6]
[0, 4, 0, 0, 0]
[0, 0, 6, 0, 0]- 结论
 使用邻接矩阵的形式实现图结构,能够更加直观的理解顶点之间的关系,也能够很好的计算相应顶点的出度和入度,但是,对于无向图而言,邻接矩阵的使用,会同时保存两个顶点之间的的两条边关系,造成存储浪费,所以,可以使用一维列表之间进行存储,减少一半的存储关系或者使用邻接表的方式。
邻接表
- 代码
class Vertex:
    """创建顶点类"""
    def __init__(self, key):
        self.id = key
        self.connectedTo = {}
    def add_neighbor(self, nbr, weight):
        self.connectedTo[nbr] = weight
    def __str__(self):
        return str(self.id) + " connected to " + str([ for x in self.connectedTo])
    def get_connections(self):
        return self.connectedTo.keys()
    def get_id(self):
        return self.id
    def get_weight(self, nbr):
        return self.connectedTo[nbr]
class Graph:
	"""创建图"""
    
    def __init__(self):
        self.vertList = {}
        self.numVertice = 0
    def add_vertex(self, key):
        self.numVertice += 1
        newVertex = Vertex(key)
        self.vertList[key] = newVertex  # 添加新的顶点
        return newVertex
    def get_vertex(self, n):
        if n in self.vertList:
            return self.vertList
        else:
            return None
    def __contains__(self, item):
        return n in self.vertList
    def add_edge(self, f, t, cost=0):
        if f not in self.vertList:
            nv = self.add_vertex(f)
            
        if t not in self.vertList:
            nv = self.add_vertex(t)
            
        self.vertList[f].add_neighbor(self.vertList[t], cost)
    def get_vertics(self):
        return self.vertList.keys()
    def __iter__(self):
        return iter(self.vertList.values())
if __name__ == "__main__":
    g = Graph()
    for i in range(6):
        g.add_vertex(i)
    g.add_edge(0,1,5)
    g.add_edge(0,5,2)
    g.add_edge(1,2,4)
    g.add_edge(2,3,9)
    g.add_edge(3,4,7)
    g.add_edge(3,5,3)
    g.add_edge(4,0,1)
    g.add_edge(5,4,8)
    g.add_edge(5,2,1)
    for x in g:
        print(x)- 结果
0 connected to [1, 5]
1 connected to [2]
2 connected to [3]
3 connected to [4, 5]
4 connected to [0]
5 connected to [4, 2]- 结论
 在python中可以使用字典来实现图邻接表的形式,对其而言,能较为方便的查找任一顶点的所有邻接点,减少了稀疏图(点多边少)占存储空间的麻烦,可以直观的计算出任一顶点的出度(有向图),但是,入度无法很好计算得出。
图的遍历
DFS(深度优先搜索)
DFS的实现过程与树的先序号遍历相同,DFS的实现过程中要设置相应的顶点访问标识,已经访问过的结点不在访问,实现过程最重要的是状态的回溯,可以使用递归。算法通俗理解:一条道走到黑(选择),不行就撤(回溯),已走过不在重复(状态标识)。
- 代码
def get_first_neighbor(self, index):
        """查询第一关系边顶点"""
        
        for j in range(self.get_num_of_vertexs()):
            if self.edges[index][j] > 0:
                return j
        return -1
    def get_next_neighbor(self, v1, v2):
        """查询第二个结点"""
        for j in range(v2 + 1, self.get_num_of_vertexs()):
            if self.edges[v1][j] > 0:
                return j
        return -1
    
    def dfs(self, is_visited, i):
        """深度优先查找"""
        print(self.get_val_by_index(i), "->", end=" ")
        self.is_visited[i] = True
        first = self.get_first_neighbor(i)
        while first != -1:
            if not self.is_visited[first]:
                self.dfs(is_visited, first)  # 递归遍历
     			
            first = self.get_next_neighbor(i, first)
    def dfs_override(self):
        """遍历每一个子图"""
        
        for i in range(self.get_num_of_vertexs()):
            if not self.is_visited[i]:
                self.dfs(self.is_visited, i)BFS(广度优先搜素)
BFS相当于树的层序遍历,可以使用队列对顶点进行存储搜索,让后先进后出的对顶点的有效边顶点进行搜索,已搜索过的便不再搜素,所以,要标识顶点的搜索状态。
- 代码
def get_first_neighbor(self, index):
        """查询第一关系边顶点"""
        
        for j in range(self.get_num_of_vertexs()):
            if self.edges[index][j] > 0:
                return j
        return -1
    def get_next_neighbor(self, v1, v2):
        """查询第二个结点"""
        for j in range(v2 + 1, self.get_num_of_vertexs()):
            if self.edges[v1][j] > 0:
                return j
        return -1
    def bfs(self, is_visited, i):
        """宽度优先搜索"""
        queue = []
        print(self.get_val_by_index(i) + "->", end=" ")
        self.is_visited[i] = True
        queue.append(i)
        while queue:
            u = queue.pop(0)
            w = self.get_first_neighbor(u)
            while w != -1:
                if not is_visited[w]:
                    print(self.get_val_by_index(w), "->", end=" ")
                    self.is_visited[w] = True
                    queue.append(w)
                w = self.get_next_neighbor(u, w)
    def bsf_override(self):
        for j in range(self.get_num_of_vertexs()):
            if not self.is_visited[j]:
                self.bfs(self.is_visited, j)
 
 
                     
            
        













 
                    

 
                 
                    