最短路及图遍历

  • 图遍历算法及最短路算法

一、图的表示

图由点、边组成(网格可以看作特殊的图),有多种表示方式,常用的为邻接矩阵、邻接链表

1.1、邻接矩阵

邻接矩阵是用于表示图的一种常见方法

  • 邻接矩阵是一个二维数组,其中行和列分别表示图中的顶点,数组中的元素表示两个顶点之间是否存在边。

    • 如果顶点之间存在边,则邻接矩阵中第i行第j列的元素为1或非零值,表示连接;否则,元素为0或空,表示没有连接。

    • 对于邻接矩阵 A A A a i j a_{ij} aij表示节点 i 、 j i、j ij是否相连,不相连则 a i j = 0 a_{ij}=0 aij=0,否则 a i j = 1 a_{ij}=1 aij=1(如果是加权图,则为权重值)

a i j = { 1 i , j 相邻 0 i , j 不相邻 \begin{equation} a_{ij}=\left\{ \begin{aligned} 1 & \quad i,j相邻\\ 0 & \quad i,j不相邻\\ \end{aligned} \right . \end{equation} aij={10i,j相邻i,j不相邻

邻接矩阵可以用于描述有向图和无向图

  • 对于无向图,邻接矩阵是对称的,即 a i j = a j i a_{ij}=a_{ji} aij=aji;对于有向图,邻接矩阵不一定对称。

使用邻接矩阵可以方便地进行图的操作和分析,比如查找两个顶点之间是否存在边、计算顶点的度数(即与之相连的边的数量)等。

邻接矩阵在表示稀疏图(边的数量相对较少)时可能会占用较大的空间

1.2、邻接链表

邻接链表是另一种用于表示图的常见数据结构

  • 邻接链表使用链表来表示图中的边,从而更有效地表示稀疏图(边的数量相对较少)
  • 在邻接链表中,每个顶点都有一个相邻顶点的链表,链表中的节点表示与该顶点相连的边。

邻接矩阵可以用于描述有向图和无向图

  • 对于无向图,每条边都会在两个顶点的邻接链表中各出现一次;而对于有向图,则会分别在起点和终点的邻接链表中出现。

相比邻接矩阵,邻接链表更适合表示稀疏图,因为它只存储图中存在的边,而不用预先分配所有可能的边空间。

在进行图的遍历和搜索时,邻接链表通常也更有效率,因为只需操作与相邻顶点直接相关的边。

python中,没有直接内置的链表数据结构,需要自己实现***【下面的代码仅实现基本功能】***

import numpy as np


class Node:
    def __init__(self, data):
        self.data = data
        self.next = None


class LinkedList(object):
    def __init__(self):
        self.head = None

    def is_empty(self):
        return self.head is None

    def add_node(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
        else:
            current = self.head
            while current.next:
                current = current.next
            current.next = new_node

    def print_linkedlist(self):
        current = self.head
        print("链表各节点的值如下:")
        while current:
            print(current.data)
            current = current.next


if __name__ == '__main__':
    # 构建邻接矩阵
    node_num = 6
    adjacent_matrix = np.zeros((node_num, node_num))

    adjacent_matrix[0, 1] = 6
    adjacent_matrix[0, 5] = 4
    adjacent_matrix[1, 2] = 2
    adjacent_matrix[1, 4] = 3
    adjacent_matrix[1, 5] = 1
    adjacent_matrix[2, 3] = 7
    adjacent_matrix[4, 2] = 3
    adjacent_matrix[4, 3] = 2
    adjacent_matrix[5, 4] = 2

    print(adjacent_matrix)

    # 根据邻接矩阵构建邻接链表
    linkedlist_list = []
    for i in range(node_num):
        linkedlist = LinkedList()
        linkedlist.add_node(i)
        for j in range(node_num):
            if adjacent_matrix[i][j] > 0:
                linkedlist.add_node(j)
        linkedlist_list.append(linkedlist)
        # 打印链表信息
        if not linkedlist.is_empty():
            linkedlist.print_linkedlist()

1.3、图的展示

使用networkx库进行图的绘制

在这里插入图片描述

import networkx as nx
import matplotlib.pyplot as plt
import numpy as np


def add_node_and_edge(G, adjacent_matrix):
    """
    根据邻接矩阵向图中添加节点及边
    :param G: 图
    :param adjacent_matrix: 邻接矩阵
    :return: 图
    """
    for i in range(len(adjacent_matrix)):
        G.add_node(i)

    for i in range(len(adjacent_matrix)):
        for j in range(len(adjacent_matrix)):
            length = adjacent_matrix[i][j]
            if length > 0:
                G.add_edge(i, j, weight=length)
    return G


def draw_network(G, node_feature):
    """
    绘图图,边的权重越大,则连线越粗
    :param G: 图
    :param node_feature: 节点属性(绘图)
    :return:
    """
    # 获取边的权重值
    weights = nx.get_edge_attributes(G, 'weight').values()

    # 绘制图形
    pos = nx.spring_layout(G)  # 定义节点的布局
    nx.draw(G, pos, with_labels=True,
            node_size=node_feature['node_size'], node_color=node_feature['node_color'], font_size=node_feature['font_size'],
            linewidths=node_feature['linewidths'], edge_color=node_feature['edge_color'])

    # 根据权重设置边的粗细
    # arrows=True能确保箭头绘制在节点之上(避免被遮盖)
    # arrowstyle能设置箭头的大小
    nx.draw_networkx_edges(G, pos, width=list(weights), arrows=True,
                           arrowstyle='fancy,head_length=1,head_width=0.8,tail_width=0.4')

    # 显示图形
    plt.axis("off")
    plt.show()


if __name__ == '__main__':

    # 创建一个有向图
    G = nx.DiGraph()

    # 构建邻接矩阵
    node_num = 6
    adjacent_matrix = np.zeros((node_num, node_num))

    adjacent_matrix[0, 1] = 6
    adjacent_matrix[0, 5] = 4
    adjacent_matrix[1, 2] = 2
    adjacent_matrix[1, 4] = 3
    adjacent_matrix[1, 5] = 1
    adjacent_matrix[2, 3] = 7
    adjacent_matrix[4, 2] = 3
    adjacent_matrix[4, 3] = 2
    adjacent_matrix[5, 4] = 2

    # 图中添加节点及边
    G = add_node_and_edge(G, adjacent_matrix)

    # 绘图
    node_feature = {'node_size': 500, 'node_color': 'lightblue', 'font_size': 10, 'linewidths': 0.5, 'edge_color': 'gray'}
    draw_network(G, node_feature)

二、图遍历算法

“王婆卖瓜一下”,该部分内容可以看我之前的博文***【内容非常的详细、丰富】***:

三、最短路算法

图的最短路算法是解决图中两个顶点之间最短路径的问题,地图导航等领域有着广泛的应用。

对于已知的静态图(点、边关系确定且保持不变),最短路算法可以分为单源最短路算法和多源最短路算法两大类。

  • 单源最短路径算法:是指从图中的一个顶点到所有其他顶点的最短路径问题

    • 常用的单源最短路径算法包括:
      1. Dijkstra算法:适用于非负权重的有向图或无向图,基于贪心策略,每次选择当前最短路径的顶点进行扩展;
      2. Bellman-Ford算法:适用于有向图或无向图,能够处理负权边,通过对图进行松弛操作来求解最短路径;
      3. A*算法:适用于非负权重的有向图或无向图,通常用于在加权图中从一个特定起点到一个指定终点找到最短路径
        • 通过结合 Dijkstra 算法的精确性和启发式方法(如贪心算法)的速度优势来优化路径搜索过程。
  • 多源最短路径算法:是指从图中任意一个顶点到任意一个顶点的最短路径问题。

    • 常用的多源最短路径算法包括:
      1. Floyd-Warshall算法:适用于有向图或无向图,能够处理负权边,通过动态规划的方式计算任意两点之间的最短路径;
      2. Johnson算法:结合了Dijkstra算法和Bellman-Ford算法的思想,能够处理负权边,通过重新赋权和辅助图的方式来求解多源最短路径。

这些算法在不同的场景下有着不同的适用性和性能表现,其中Dijkstra算法、A*算法、Floyd算法是其中最常用也是最基础,需要优先掌握

1.1、Dijkstra算法

Dijkstra算法是求解单源最短路(一对多)的算法,即确定一个节点(起点)到其他各个节点(终点)的最短路

可观看该视频***【非常直观】***:https://www.bilibili.com/video/BV1zz4y1m7Nq/?share_source=copy_web

也可以直接看我之前的博文***【内容非常的详细、丰富】***

1.2、Floyd算法

Floyd算法是求解多源最短路(多对多)的算法,即确定每个节点(起点)到其他节点(终点)的最短路

算法允许边的权重为负,但是负边构成的回路(环)的权重之和不能为负(负环)

可以直接看我之前的博文***【内容非常的详细、丰富】***

1.3、A*算法

A*算法是一种特殊的单源最短路径算法,通常用于在加权图中确定从一个特定节点(起点 S S S)到一个指定节点(终点 E E E)的最短路。

  • A* 算法通过结合Dijkstra算法的精确性和启发式方法的速度优势优化路径搜索过程;
    • “实际成本”g(n):从 S S S到当前节点的实际路径成本
    • “预估成本”h(n):用于评估从当前节点到 E E E估计成本,常见的启发式方法包括曼哈顿距离、欧几里德距离等;
    • “综合成本”f(n):即 f ( n ) = g ( n ) + h ( n ) f(n)=g(n)+h(n) f(n)=g(n)+h(n),启发式函数估计的成本和实际成本之和,用于决定搜索顺序
  • 启发式函数h(n),通常称为估计成本函数,来估算从任一顶点 n n n到目标顶点的成本,是A*算法与其他单源最短路径算法最主要的区别;
  • 在实际应用中,确保启发式函数是可行的(即它不会高估到达目标的实际成本),这样才能保证算法的效率和结果的正确性。

可以直接看我之前的博文***【一篇对算法原论文的理论解析,分析了算法有效性;一篇是示例及代码】***

四、相关链接

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值