python数据结构之图

入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。

目录

一、图的相关概念

1、图

2、无向图和有向图

3、邻接矩阵

4、邻接表和加权邻接字典

二、图的遍历

1、DFS(深度优先搜索)

2、BFS(广度优先搜索)

三、python实现图的遍历(基于邻接表)

1、深度搜索(分析见注释)实现查找所有路径以及最短路径

2、广度搜索(分析见注释)遍历输出图的数据

四、最短路径Djkstra算法

1、思路:

2、python实现Djkstra算法


一、图的相关概念

1、图

表示多对多的关系

一组顶点,通常用V(Vertex)表示顶点集合

一组边,通常用E(Edge)表示边的集合,边是顶点对,分为有向边和无向边

下图即为图。

2、无向图和有向图

🌳无向图:边没有方向的图。(即从A可以直接到B,B也可以直接到A)

🌳有向图:边有方向的图。(即从A可以直接到B,但B不能直接到A)

🌳入度:以顶点A为头的弧的数目称为A的入度。如上图所示:A的入度为0。

🌳出度:以顶点A为尾的弧的数目称为A的出度。如上图所示:A的出度为2。

3、邻接矩阵

存储着边的信息的矩阵,而顶点则用矩阵的下标表示,连通为1,不连通为0。

注:当边有权值时,1全部变为对应的权值。

如下图所示:邻接矩阵为:

01234
000000
100110
210000
300001
410000

4、邻接表和加权邻接字典

使用邻接矩阵实现图结构,能直观体现顶点之间的关系,也能很好地计算相应顶点的出度和入度,但对于无向图来说,邻接矩阵会同时保存两个顶点之间的两条边关系,会造成存储浪费。

所以,可以使用一维列表进行存储,减少一半的存储关系,使用邻接表的方式来实现图结构。

邻接表:针对每个节点设置一个邻接列表。

还是直接看例子吧。

邻接表如下:(a,b,c,d,e分别表示0,1,2,3,4)

a, b, c, d, e = list(range(8))
N = [
    {},
    {c, d},
    {a},
    {e},
    {a},
]

如果边有权值的话,则多加上权值的表示,构成加权邻接字典。


二、图的遍历

1、DFS(深度优先搜索)

与树的先序遍历相同,DFS要设置相应的顶点访问标识,已经访问过的结点不再访问,实现过程最重要的是状态的回溯,一般使用递归。

简单来说就是一条道走到底(选择),不行就回去(回溯),走过的不再重复(状态标识)。 

2、BFS(广度优先搜索)

与树的层序遍历相同,使用队列对顶点进行存储搜索,然后先进后出的对顶点的有效边顶点进行搜索,搜索过的就不再搜索。因此,要标识顶点的搜索状态。


三、python实现图的遍历(基于邻接表)

1、深度搜索(分析见注释)实现查找所有路径以及最短路径

首先深度搜索主体

def dfs(graph, path, end,results,w,weights): #找出从start到end的所有路径
    s = path[-1]
    if s == end: #判断是否走到指定位置
        results.append(path) #添加路径
        weights.append(w) #添加路径权重
        return 
    for x in graph[ord(s) - 65]:
        if x not in path: #走过的不再走
            dfs(graph,path + [x],end,results,w + graph[ord(s) - 65][x],weights)
            #这里注意不能path.append(x),因为path会跟着变

这里我们就得到所有路径和权值,接下来就对这些路径的长短进行一个排序,就可以找到最短路径。

def search(graph, path, end):
    results = []
    w = 0
    weights = []
    dfs(graph, path, end, results,w,weights)
    a=sorted(enumerate(weights), key=lambda x:x[1]) #进行一个排序
    print(results) #输出所有路径
    print(results[a[0][0]]) #输出最短路径

具体调用

Graph = [{'B':3,'C':4},
            {'A':1,'D':5},
            {'A':6,'E':2},
            {'A':2,'B':9,'C':4},
            {'C':3,'D':5}]
r = search(Graph,['A'],'D')

输出

所有路径:[['A', 'B', 'D'], ['A', 'C', 'E', 'D']]
最短路径:['A', 'B', 'D']

2、广度搜索(分析见注释)遍历输出图的数据

广搜主体

from queue import Queue
def bfs(graph,start): #遍历图
    queue = Queue() 
    search = [] #用来记录已经搜索过的
    queue.put(start)
    print(start)
    search.append(start)
    while not queue.empty():
        s = queue.get()
        for i in graph[ord(s) - 65]:
            if i not in search:
                queue.put(i)
                print(i)
                search.append(i)

具体调用

Graph = [{'B':3,'C':4},
            {'A':1,'D':5},
            {'A':6,'E':2},
            {'A':2,'B':9,'C':4},
            {'C':3,'D':5}]
bfs(Graph,'B')

输出

BADCE


四、最短路径Djkstra算法

由上面的最短路径算法补充Djkstra算法。

1、思路:

核心思想:贪心算法(即总是做出在当前看来是最好的选择)

不断地从start开始找路,分别找出start到各点距离最短的路径,不断更新。

具体流程如下图所示:

从1出发:

(1,2)(1,3)(1,4)(1,5)(1,6)
第一次24
第二次2458
第三次245810

2、python实现Djkstra算法

分析请见注释

def dj(start, mgraph):
    nopass = [x for x in range(len(mgraph)) if x != ord(start)-65]#初始未到达的各个点
    dis = mgraph[ord(start) - 65].copy() #获取初始距离,并且不改变原来graph的值
    path = []#记录路径
    for i in range(len(mgraph)):
        path.append([start])
    #一个一个点突破
    while len(nopass):
        idx = nopass[0] #开始寻找第一个点
        for i in list(dis.keys()):
            i = ord(i) - 65
            if i in nopass:
                if chr(i+65) not in dis.keys():
                    dis.update({chr(i+65):100000000000})
                if dis[chr(i+65)] < dis[chr(idx+65)]: #更新,找到前期,这是为了找出有没有比直接到这个点更近的可能,如果没有说明这个点目前的路径就是最短路径
                    idx = i
        nopass.remove(idx)#通过该点,将他从搜索列表中去除
        if path[idx] == [start]:
            path[idx] = [start,chr(idx + 65)]#路径节点添加
        flag = 1
        for i in nopass: #找剩下没通过的点中有没有需要经过idx的
            if chr(i+65) not in dis.keys():
                    dis.update({chr(i+65):100000000000})
            if chr(i+65) in mgraph[idx].keys() and dis[chr(idx+65)] + mgraph[idx][chr(i+65)] < dis[chr(i+65)]: 
                flag = 0
                dis[chr(i+65)] = dis[chr(idx+65)] + mgraph[idx][chr(i+65)]
                if((flag == 0) & (path[idx] == [start])):
                    path[idx] = [start,chr(idx + 65)]
                path[i] = [path[idx] ,chr(i+65)]
    print(start)
    print("path is ")
    print(path)
    print("dis is ")
    print(dis)
    print("\n")
Graph = [{'B':3,'C':4},
            {'A':1,'D':5},
            {'A':6,'E':2},
            {'A':2,'B':9,'C':4},
            {'C':3,'D':5}]
dj('B',Graph)

输出

B
path is 
[['B', 'A'], ['B'], [['B', 'A'], 'C'], ['B', 'D'], [[['B', 'A'], 'C'], 'E']]
dis is 
{'A': 1, 'D': 5, 'C': 5, 'E': 7}

欢迎大家在评论区批评指正,谢谢~

  • 4
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tt丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值