地铁最短路线个人项目

地铁个人项目

主要功能


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

数据输入格式

文件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.站名是中文字符(文本data.txt打成拼音了),对于不同的编译器或者不同的操作系统,可能会导致乱码的形式出现


# 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
    

2.环线、直线、换乘
从bgstations.txt中可以看见如下数据输入:从bgstations.txt中可以看见如下数据输入:
1 23
ping.guo.yuan 0
gu.cheng 0
ba.jiao.you.le.yuan 0
ba.bao.shan 0
yu.quan.lu 0
wu.ke.song 0
wan.shou.lu 0
后面的数字为0表示没有换乘

简单介绍一下图

图(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
    

2.邻接表(可以理解为字典形式,每一个顶点可以指到连接它的所有点)

# 基于邻接表定义图,继承图类,也可以直接写
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[vi][i]=(vj,val)
                return
            if row[i][0]>vj:#原来如果没有到vj的边,退出循环,加入边
                break
            i+=1
        self._mat[vi].insert(i,(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.")
        for i,val in self._mat[vi]:
            if i==vj:
                return val
        return self._unconn
    def out_edges(self,vi):
        if self._invalid(vi):
            raise GraphError(str(vi)+" is not a valid vertex.")
        return self._mat[vi]

代码实现

#这里我可以直接读取并显示中文,大家不行的自己修改

LINEDATA=['1','2','4','5','6','7','8','9','10','13','14','15']
STATION_NUM={}#字典,站名到数字编号的对应
data={}
datanum={}  #邻接表,啊哈就先这么叫他吧
STATIO={}#字典,对应数字到站名的对应
with open("routedata.txt","r") as f:
    TOTAL=f.readline()
    for line in f.readlines():
        if line!='\n':
            line = line.rstrip('\n')
            line=line.split(' ')
            if line[0] in LINEDATA:
                linei=line[0]
                continue
            line[1]=linei
            line0=line[0]
            intline=int(line[1])
            if intline not in data.keys():
                data[intline]=[line0]
            else:
                data[intline].append(line0)
            if line0 not in datanum.keys():
                datanum[line0]=[intline]
            else:
                datanum[line0].append(intline)

for datai in datanum.keys():
    STATION_NUM[datai]=i
    STATIO[i]=datai
    i+=1

I=i # 顶点个数

#判断是否为环线
def iscircle(mlist):
    if mlist[0]==mlist[-1]:
        return True
    return False
 data:{1: ['苹果园', '古城', '八角游乐园', '八宝山', '玉泉路', '五棵松', '万寿路', '公主坟'...
datanum:{'苹果园': [1], '古城': [1], '八角游乐园': [1], '八宝山': [1], '玉泉路': [1], '五棵松': [1], '万寿路': [1], '公主坟': [1, 10]...
STATION-NUM:{'苹果园': 0, '古城': 1, '八角游乐园': 2, '八宝山': 3, '玉泉路': 4, '五棵松': 5, '万寿路': 6, '公主坟': 7...

最短路径搜索

如题,采用Dijkstra算法实现

#基于优先队列的dijkstra算法
def dijkstra_shortest_pathS(graph,v0,endpos):
    vnum=0
    for i in pathss.keys():
        vnum+=1
    # print(vnum)
    # vnum=graph.vertex_num()
    assert 0<=v0<vnum
    paths=[None]*vnum#长为vnum的表记录路径
    count=0
    cands=PrioQueue([(0,v0,v0)])#求解最短路径的候选边集记录在优先队列cands中(p,v,v')v0经过v到v'的最短路径长度为p,根据p的大小排序,保证选到最近的未知距离顶点
    while count<vnum and not cands.is_empty():
        plen,u,vmin=cands.dequeue()#取路径最短顶点
        # print(u,vmin)
        if paths[vmin]:#如果这个点的最短路径已知,则跳过
            continue
        paths[vmin]=(u,plen)#新确定最短路径并记录
        for v in graph[vmin]:#遍历经过新顶点组的路径
            if not paths[v]:#如果还不知道最短路径的顶点的路径,则记录
                cands.enqueue((plen+1,vmin,v))
        count+=1
        # print(paths)
    return paths

输入的graph参数如下定义:

pathss={}
for i in range(I):
    for j in range(I):
        if RouteGraph.get_edge(i,j)==1:
            start=STATIO[i]
            end=STATIO[j]
            if i not in pathss.keys():
                pathss[i]=[j]
            else:
                pathss[i].append(j) 

测试用例

1.孙河到石门

sun.he
shi.men

输出

sun.he-15(5)-shi.men

2.公主坟到石门

gong.zhu.fen
shi.men

输出

gong.zhu.fen-1(1)-shi.men
jun.shi.bo.wu.guan-9(2)-shi.men
bai.shi.qiao.nan-6(2)-shi.men
che.gong.zhuang-2(5)-shi.men
yong.he.gong-5(3)-shi.men
hui.xin.xi.jie.nan.kou-10(1)-shi.men
shao.yao.ju-13(1)-shi.men
wang.jing.xi-15(9)-shi.men

3.安定门到石门

an.ding.men
shi.men

输出

an.ding.men-2(1)-shi.men
yong.he.gong-5(3)-shi.men
hui.xin.xi.jie.nan.kou-10(1)-shi.men
shao.yao.ju-13(1)-shi.men
wang.jing.xi-15(9)-shi.men

4.异常输入

bb
bb
Traceback (most recent call last):
  File "C:/Users/admin/PycharmProjects/untitled6/ditie.py", line 305, in <module>
    s1=STATION_NUM[startpos]
KeyError: 'bb'

输出文件保存

同目录下的result.txt文件里

github

https://github.com/lincolnforever/subway

原文地址:https://www.cnblogs.com/lincolnfoever/p/11674764.html

时间: 2024-08-07 07:54:04

地铁最短路线个人项目的相关文章

结对项目—地铁出行路线规划

结对项目—地铁出行路线规划 我的搭档:陈鸿超 14061216 https://github.com/ChengFR/PairProgramming_SubwayRoute- 会在十一期间发布新版本 结对编程体会: 结对编程的优点: 站在软件开发的角度,两个人共同面对同一台电脑进行开发,无论是效率还是软件质量都要超过一个人进行开发的情况. 对于还处于学习阶段的年轻软件开发者来说,结对编程是一个很好的互相学习的机会 结对编程时动力.责任感更强 结对编程的缺点: 对于我们来说,寻找两个人共同的时间进

个人项目-地铁出行路线

---恢复内容开始--- 地铁出行路线 https://github.com/ChengFR/PersonalProgram_SubwayRoute 时间预期 PSP 2.1 Personal Software Process Stages Time Planning 计划 · Estimate · 估计这个任务需要多少时间 Development 开发 · Analysis · 需求分析 (包括学习新技术) 3h · Design Spec · 生成设计文档 1h · Design Revie

北京地铁最短线路规划

一.项目需求 设计一个能进行北京地铁最短线路规划的程序. 二.文件存储 用一个名为data.txt的文件来存储所有北京地铁线路及站点信息,如下所示. 三.算法代码 本次项目的设计用到的语言是java语言,主要的规划最短路径的算法采用dijkstra算法 public class PathControl { private static List<Station> analysisList = new ArrayList<>(); private static HashMap<

天津地铁出行线路规划项目需求分析与设计思路分析

天津地铁出行线路规划项目需求分析与设计思路分析 项目概要 以下是天津地铁线路总图,本项目的受众可以通过本软件,获得天津市地铁出行最便捷,最快速的线路推荐. 需求分析 实现一个帮助进行地铁出行路线规划的命令行程序. 支持地铁线路的更改,站点更改.取消与添加,以及线路的局部封闭. 支持查询线路的所有站点. 支持查询到某终止站点的途径最少站点的路线. 数据存储结构分析 由于单一的线路表与站点表是无法表示如此复杂的地铁线路情况的. 有多个前驱的站点如:,以及有多个后继的站点如:,这种情况无法只通过这两个

天津地铁出行路线规划

需求 1.采用合适的方式将地铁线路信息(线路名称.站点名称.车站换乘信息)保存,并能通过应用程序读取信息: 2.应用程序能够查询相关信息:如某线路从起始站到终点站的距离.站点数量 3.能够查询任意两个站点之间的最短路线,输出经过站点个数和路径 4.写出至少10个测试用例 5.进行正常情况测试和错误情况测试 设计思路 1.采用.txt形式存储地铁线路信息,假定每个站点之间的距离为1,格式如下: 1号线:刘园,西横堤,果酒厂,本溪路,勤俭道,洪湖里,西站#6... '#'表示可以在此处换乘到另一条线

地铁出行路线规划分析与设计

需求分析:1.实现一个支持显示地铁线路与计算换乘的程序2.实现基础的查询操作(查询指定地铁线经过的站点等)3.计算从出发到目的站点之间的最短路线(经过的站点数最少)设计思路:将地铁线路信息用一个文本文件(.txt)的形式保存起来 1号线:站点1 站点2 ... 2号线:站点1 站点2 ... 3号线:站点1 站点2 ......... 将最短路线信息用一个文本文件(.txt)的形式输出 3 洪湖里 西站 6号线 复兴路 整体代码使用java编写. 将地铁线路图看作一张无向图,图中的每一个节点即为

天津地铁线路最短路径计算项目规划

天津地铁线路路径查询项目规划 一.项目介绍 实现一个帮助进行地铁出行路线规划的命令行程序. 二.项目完成预估表 PSP 2.1 Personal Software Process Stages Time Time Planning 计划 · Estimate · 估计这个任务需要多少时间 1day Development 开发 · Analysis · 需求分析 (包括学习新技术) 3day · Design Spec · 生成设计文档 1day · Design Review · 设计复审 (

北京地铁出行路线规划设计

一.需求分析 根据题意,需要做到以下几点: 1.可以判断命令行输入的参数并执行相应的操作 2.可以读入地图信息 3.用户可以读取某一地铁线路从起始站到终点站的全部信息 4.用户写入起始站和终点站,规划出正确的路线并告知用户路线 5.能对于用户的不正确输入加以判断并提示 二.设计思路 1.考虑到每个站点会有很多属性,故采用java类的形式进行编程 2.要寻找最短路径问题,则采用dijkstra算法 3.给每条线路id方便运算 三.预计的具体实现(可能后续会作修改)   1.地图导入格式:用文本文件

地铁出行路线规划程序

工程:实现一个帮助进行地铁出行路线规划的命令行程序. 使用PSP表格记录预估将在程序的各个模块的开发上耗费的时间. PSP 2.1 Personal Software Process Stages Time Planning 计划 · Estimate · 估计这个任务需要多少时间 10h Development 开发 · Analysis · 需求分析 (包括学习新技术) 30min · Design Spec · 生成设计文档 无 · Design Review · 设计复审 (和同事审核设