地铁两站之间最短路径查询(python实现)

基于图结构实现北京地铁乘坐线路查询( python)

问题描述

编写一个程序实现北京地铁最短乘坐(站)线路查询,输入为起始站名目的站名,输出为从起始站到目的站的最短乘坐站换乘线路。

  1. 采用Dijkstra算法实现;
  2. 如果两站间存在多条最短路径,找出其中的一条就行

在这里插入图片描述

数据输入形式

文件bgstations.txt为数据文件,包含了北京地铁的线路及车站信息。其格式如下:

<地铁线路总条数>

<线路1> <线路1站数>

<站名1> <换乘状态>

<站名2> <换乘状态>

...


<线路2> <线路2站数>

<站名1> <换乘状态>

<站名2> <换乘状态>

...

例如演示代码输入的数据形式:

12

1 23

苹果园 0

古城 0

八角游乐园 0

八宝山 0

玉泉路 0

五棵松 0

万寿路 0

公主坟 1

军事博物馆 1

木樨地 0

南礼士路 0

复兴门 1

西单 1

...

2 19

西直门 1

积水潭 0

鼓楼大街 1

...

西直门 1

...

该文件表明当前北京地铁共有12条线路(不含郊区线路),接着为每条线路信息。

打开当前目录下文件bgstations.txt,读入地铁线路信息,并从标准输入中读入起始站和目的站名(均为字符串,各占一行)。

数据输出形式

输出从起始站到目的站的乘坐信息,要求乘坐站数最少。换乘信息格式如下:

SSN-n1(m1)-S1-n2(m2)-...-ESN

其中:SSN和ESN分别为起始站名和目的站名;n为乘坐的地铁线路号,m为乘坐站数。

代码结构以及重难点分析

  1. 站名是中文字符,对于不同的编译器或者不同的操作系统,可能会导致乱码的形式出现在这里插入图片描述

# try except部分代码用于修改python runtime的标准输入输出的编码格式
try:
    import io
    import sys
    sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding='utf-8')
    sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8')
except:
    pass


# 读取地铁线路信息的函数
def read_file(filename):
    # 打开文件时指定编码为"utf-8"
    f = open(filename, 'r', encoding="utf-8")
    # 读取总线路数目
    total = int(f.readline())
    for _ in range(total):
        # 读取线路id和线路站数
        id, n = (int(x) for x in f.readline().split())
        for j in range(n):
            # 读取站名和换乘站信息
            name, is_transfer = f.readline().split()
    
    return subway_info
    
  1. 环线、直线、换乘
    从bgstations.txt中可以看见如下数据输入:从bgstations.txt中可以看见如下数据输入:

2 19
西直门 1
积水潭 0
鼓楼大街 1

车公庄 1
西直门 1

这说明2号线是环路,所以要在建立地铁站连线图结构的时候对环线的最短路径加以考虑。我们来看图想一下在这里插入图片描述
譬如,从鼓楼大街复兴门,沿安定门方向和沿积水潭方向都可以到达,比较两者路径长度显然是后者是更优解。当然这样一想一脸懵逼 ,要怎么做呢?

我们就要来看看图结构的表达形式

简单介绍一下图

(Graph)是一种比线性表和树更为复杂的数据结构。

图结构:是研究数据元素之间的多对多的关系。在这种结构中,任意两个元素之间可能存在关系。即结点之间的关系可以是任意的,图中任意元素之间都可能相关。 图的应用极为广泛,已渗入到诸如语言学、逻辑学、物理、化学、电讯、计算机科学以及数学的其它分支。

图的基本术语

1.弧(Arc) :表示两个顶点v和w之间存在一个关系,用顶点偶对<v,w>表示。通常根据图的顶点偶对将图分为有向图和无向图。

2.有向图(Digraph): 若图G的关系集合E(G)中,顶点偶对<v,w>的v和w之间是有序的,称图G是有向图。 在有向图中,若 <v,w>∈E(G) ,表示从顶点v到顶点w有一条弧。 其中:v称为弧尾(tail)或始点(initial node),w称为弧头(head)或终点(terminal node) 。

3.无向图(Undigraph): 若图G的关系集合E(G)中,顶点偶对<v,w>的v和w之间是无序的,称图G是无向图。

4.完全无向图:对于无向图,若图中顶点数为n ,用e表示边的数目,则e ∈[0,n(n-1)/2] 。具有n(n-1)/2条边的无向图称为完全无向图。

5.完全有向图:对于有向图,若图中顶点数为n ,用e表示弧的数

目,则e∈[0,n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图。
6.权(Weight):与图的边和弧相关的数。权可以表示从一个顶点到另一个顶点的距离或耗费

7.子图和生成子图:设有图G=(V,E)和G’=(V’,E’),若V’=V且E’∈E ,则称图G’是G的子图;若V’=V且E’∈E,则称图G’是G的一个生成子图。顶点的邻接(Adjacent):对于无向图G=(V,E),若边(v,w)∈E,则称顶点v和w 互为邻接点,即v和w相邻接。边(v,w)依附(incident)与顶点v和w 。

图的存储结构

  1. 邻接矩阵

    ∞ 6 2 ∞ ∞
    6 ∞ 3 4 3
    2 3 ∞ 1 ∞
    ∞ 4 3 ∞ 5
    ∞ 3 ∞ 5 ∞


# 定义邻接矩阵图类
class Graph:
    def __init__(self,mat,unconn=0):
        vnum=len(mat)
        for x in mat:
            if len(x)!=vnum:#检查是否是方阵
                raise ValueError("Argument for 'Graph'.")
        self._mat=[mat[i][:] for i in range(vnum)]#赋值mat到self._mat
        self._unconn=unconn
        self._vnum=vnum
    def vertex_num(self):
        return self._vnum
    def _invalid(self,v):
        return 0>v or v>=self._vnum
    def add_vertex(self):#并未计划支持增加顶点,所以直接定义为错误,要增加顶点需要增加一行矩阵一列
        raise GraphError("Adj-Matrix does not support 'add_vertex'.")
    def add_edge(self,vi,vj,val=1):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi)+' or '+str(vj)+" is not a valid vertex.")
        self._mat[vi][vj]=val
    def get_edge(self,vi,vj):
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi)+' or '+str(vj)+" is not a valid vertex.")
        return self._mat[vi][vj]
   #记录已经构造的表
    #用静态方法构造结点表
    def out_edges(self,vi):
        if self._invalid(vi):
            raise GraphError(str(vi)+" is not a valid vertex.")
        return self._out_edges(self._mat[vi],self._unconn)
    @staticmethod
    def _out_edges(row,unconn):
        edges=[]
        for i in range(len(row)):
            if row[i]!=unconn:
                edges.append((i,row[i]))
            return edges
    

邻接矩阵 对于大型数据的处理很鸡肋!!
(对于本次讨论的地铁图结构来说,你会发现二百多个点,一个200*200数量极的大二维列表,每一行就几个有数,其他都是inf,对于数据处理没有卵用!!)

所有的线索都指向==》当当当当

  1. 邻接表(可以理解为字典形式,每一个顶点可以指到连接它的所有点)
# 基于邻接表定义图,继承图类,也可以直接写
class GraphAL(Graph):
    def __init__(self,mat=[],unconn=0):
        vnum=len(mat)
        for x in mat:
            if len(x)!=vnum:
                raise ValueError("Argument for 'GraphAL'.")
        self._mat=[Graph._out_edges(mat[i],unconn) for i in range(vnum)]
        self._vnum=vnum
        self._unconn=unconn
    def add_vertex(self):#增加新节点时安排一个新编号
        self._mat.append([])
        self._vnum+=1
        return self._vnum-1
    def add_edge(self,vi,vj,val=1):
        if self._vnum==0:
            raise GraphError("cannot add edge to empty graph")
        if self._invalid(vi) or self._invalid(vj):
            raise GraphError(str(vi) + ' or ' + str(vj) + " is not a valid vertex.")
        row=self._mat[vi]
        i=0
        while i<len(row):
            if row[i][0]==vj:#更新mat[vi][vj]的值
                self._mat
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值