无向无权图见另一篇文章《python无向无权图结构》,这篇讲无向带权图,并且给出一个地铁线路例子。

# -*- coding: UTF-8 -*-
#!/usr/bin/python

#-----------------------------------------------------------------------
# python3.6
# wgraph.py  
# 图的数据结构,以邻接表的方式存储结点
#-----------------------------------------------------------------------

import sys
import stdio
from instream import InStream
from outstream import OutStream

MAXL=99999

#-----------------------------------------------------------------------
#无向带权图
class WGraph:

    #构造函数,从文件或命令行读取边
    def __init__(self, filename=None, delimiter=None):
        self._e = 0
        self._adj = dict()
        self.lpath = []
        if filename is not None:
            instream = InStream(filename)
            while instream.hasNextLine():
                line = instream.readLine()
                names = line.split(delimiter)
                if(len(names)>0):   #防止无效的行
                    if(names[-1].isdigit()):
                        for i in range(1, len(names)-1):#最后一个元素表示边的权值
                            self.addEdge(names[0], names[i], names[-1])
                    else:
                        for i in range(1, len(names)):#最后一个元素不是数值表示无权图,默认权为1
                            self.addEdge(names[0], names[i], 1)
    
    # 
    def addEdge(self, v, w, weight):
        if not self.hasVertex(v): self._adj[v] = {}
        if not self.hasVertex(w): self._adj[w] = {}
        if not self.hasEdge(v, w):
            self._e += 1
            self._adj[v][w]=int(weight)
            self._adj[w][v]=int(weight)
            
    # 
    def adjacentTo(self, v):
        return iter(self._adj[v])
    
    #
    def vertices(self):
        return iter(self._adj)

    # 
    def hasVertex(self, v):
        return v in self._adj

    # 
    def hasEdge(self, v, w):
        return w in self._adj[v]
    
    # 
    def countV(self):
        return len(self._adj)
    
    # 
    def countE(self):
        return self._e
    
    # 
    def degree(self, v):
        return len(self._adj[v])
    
    def print_path(self, l, o):
        for x in l:
            #print("%s -> "%x, end='')
            o.write("%s -> "%x)
        #print("\n", end='')
        o.writeln()
    
    def allpath(self, s, t, o):
        self.lpath += [s]
        if(s==t):
            self.print_path(self.lpath, o)
        else:
            for v in self.adjacentTo(str(s)):
                if(v not in self.lpath):             
                    self.allpath(v, t, o)
        self.lpath.pop()
        
    def __findShorestNode(self, cost, visited):
        minDist=MAXL
        node=None
        for i in self.vertices():
            if (cost[i]<minDist) and (i not in visited):
                minDist=cost[i]
                node=i
        return node
    
    #返回从源结点s到所有每个结点的最短路径代价字典cost,和路径指向字典parents
    #cost[i]就是从s到达i的最小代价值,parents[i]存储从s到达i的最短路径上, i的前一个结点
    def dijkstra(self, s):
        cost={}
        visited=[s]
        parents={s:None}
        #初始化cost字典
        for v in self.vertices():
            if self.hasEdge(s, v):
                cost[v]=self._adj[s][v]
            else:
                cost[v]=MAXL
        cost[s]=0
        #初始化parents字典
        for i in self.adjacentTo(s):
            parents[i]=s
        
        node=self.__findShorestNode(cost, visited)
        while node:
            for i in self.adjacentTo(node): #所有node结点的邻居结点
                newcost = cost[node] + self._adj[node][i]
                if newcost<cost[i]:
                    parents[i]=node #最短路径到达i的路径上,i的上一个结点是node
                    cost[i]=newcost
            visited.append(node)
            node=self.__findShorestNode(cost, visited)

        return cost, parents
        
    # 
    def __str__(self):
        s = ''
        for v in self.vertices():
            s += v + '  '
            for w in self.adjacentTo(v):
                s += w + ' '
            s += '\n'
        return s

#-----------------------------------------------------------------------
#返回从图graph中源结点source到目的结点destination的最短路径
#返回空字典cost和列表spath,表示graph不是一个连通图
def shortestPath(graph, source, destination):
        s=source
        t=destination
        cost={}
        spath=[]
        if graph.hasVertex(s) and graph.hasVertex(t):
            cost, parents=graph.dijkstra(s)
            if MAXL in cost.values():   #graph不是一个连通图(只有一个连通分量)
                return {}, []
            path=[]
            p=parents[t]
            while(p):
                path.append(p)
                p=parents[p]
            spath = list(reversed(path))+[t]    
        return cost, spath  
# 
def main():
    #file = sys.argv[1]
    file1 = 'E:\\python_code\\GFmetro.txt'   #必须是utf-8编码
    outfile = 'E:\\python_code\\output.txt'
    graph = WGraph(file1)
    #stdio.writeln(graph)
    
    outsream = OutStream(outfile) 
    
    start=u"沥滘"
    end=u"广州东站"
    (cost, path) = shortestPath(graph, start, end)
    #print("path from %s to %s: cost=%d"%(start, end, cost[end]))
    if len(cost)>0 and len(path)>0:
        stdio.writeln("path from %s to %s: cost=%d"%(start, end, cost[end]))
        stdio.writeln(path)
    else:   #非连通图,结束程序
        stdio.writeln("the graph is not a fully connected graph")
        return 
    
    #print("all path")
    outsream.writeln("all path from  %s to %s: "%(start, end))
    graph.allpath(start, end, outsream)
    stdio.writeln("output to %s .... ok" % outfile)


if __name__ == '__main__':
    main()

#-----------------------------------------------------------------------
# PS:
class InStream:
    """
    可以从网络或文件输入
    """
    def __init__(self, fileOrUrl=None):
   
        self._buffer = ''
        self._stream = None
        self._readingWebPage = False

        if fileOrUrl is None:
            import stdio 
            self._stream = sys.stdin  # 从命令行输入
            return

        try:
            if sys.hexversion < 0x03000000:
                self._stream = open(fileOrUrl,'rU')
            else:
                self._stream = open(fileOrUrl, 'r', encoding='utf-8')
        except IOError:
            try:
                self._stream = urllib.urlopen(fileOrUrl)
                self._readingWebPage = True
            except IOError:
                raise IOError('No such file or URL: ' + fileOrUrl)
    def hasNextLine(self):
        """
        下一行非空,返回True
        """
        if self._buffer != '':
            return True
        else:
            self._buffer = self._stream.readline()
            if sys.hexversion < 0x03000000 or self._readingWebPage:
                self._buffer = self._buffer.decode('utf-8')
            if self._buffer == '':
                return False
            return True
    def readLine(self):
        """
        返回读出的一行
        仅对自动分行(非正常换行)的行会报错
        """
        if not self.hasNextLine():
            raise EOFError()
        s = self._buffer
        self._buffer = ''
        return s.rstrip('\n')

    def __del__(self):
        if self._stream is not None:
            self._stream.close()

class OutStream:
    """
    输出到文件或命令行
    """
    def __init__(self, f=None):#f是文件名,默认是命令行
        if f is None:
            self._stream = sys.stdout
        else:
            if sys.hexversion < 0x03000000:
                self._stream = open(f, 'w')#可以添加错误处理
            else:
                self._stream = open(f, 'w', encoding='utf-8')
    def writeln(self, x=''):
        """
        往流中写入串x,末尾加上换行符
        """
        if sys.hexversion < 0x03000000:
            x = unicode(x)
            x = x.encode('utf-8')
        else:
            x = str(x)
        self._stream.write(x)
        self._stream.write('\n')
        self._stream.flush()

    def __del__(self):
        self._stream.close()


程序运行输出:

path from 沥滘 to 广州东站: cost=7
['沥滘', '大塘', '客村', '广州塔', '珠江新城', '体育西路', '体育中心', '广州东站']

output to E:\python_code\output.txt .... ok

 

注意不要以此cost值作为出行的时间依据,因为文件中每两个站点间的时间值有待商榷,cost=1表示1个单位的时间,可能是1分钟,也可能是2分30秒,而且没有考虑换乘的时间因数 :P   当然,没人会按output.txt文件中的线路出行,除非他想在地铁里环游广佛~~

GFmetro.txt为广佛地铁图数据文件,见文末。对应的图如下:

若求 从魁奇路 到 区庄的最短线路,输出如下:

path from 魁奇路 to 区庄: cost=28
['魁奇路', '季华园', '同济路', '祖庙', '普君北路', '朝安', '桂城', '南桂路', '礌岗', '千灯湖', '金融高新区', '龙溪', '菊树', '西朗', '坑口', '花地湾', '芳村', '黄沙', '长寿路', '陈家祠', '西门口', '公园前', '农讲所', '烈士陵园', '东山口', '区庄']

 

GFmetro.txt 广佛地铁图数据文件内容,边角的线路待补充:P

魁奇路 季华园 1
同济路 季华园 1
同济路 祖庙 1
祖庙 普君北路 1
朝安 普君北路 1
桂城 朝安 1
南桂路 桂城 1
礌岗 南桂路 1
礌岗 千灯湖 1
千灯湖 金融高新区 1
金融高新区 龙溪 2
龙溪 菊树 2
西朗 菊树 2
西朗 鹤洞 1
鹤洞 沙涌 1
沙园 沙涌 凤凰新村 宝岗大道 燕岗 1
燕岗 石溪 1
石溪 南洲 1
沥滘 南洲 3

宝岗大道 昌岗 1
昌岗 晓港 1
晓港 中大 1
中大 鹭江 1
客村 鹭江 广州塔 赤岗 大塘 1
赤岗 磨碟沙 1
磨碟沙 新港东 1
新港东 琶洲 1
琶洲 万胜围 1

东涌 低涌 1
低涌 海傍 1
海傍 石碁 1
石碁 新造 1
新造 大学城南 1
大学城南 大学城北 1
大学城北 官洲 1
万胜围 车陂南 官洲 1
车陂南 车陂 1
车陂 黄村 1

鱼珠 三溪 1
三溪 东圃 1
东圃 车陂南 1
车陂南 科韵路 1
科韵路 员村 1
潭村 员村 1
猎德 潭村 1
猎德 珠江新城 1
珠江新城 五羊邨 1
五羊邨 杨箕 1
杨箕 东山口 体育西路 动物园 1
动物园 区庄 1
区庄 淘金 东山口 黄花岗 1
淘金 小北 1
小北 广州火车站 1
广州火车站 西村 1
西村 西场 1
西场 中山八 1
中山八 坦尾 1
坦尾 窖口 1

西朗 坑口 1
坑口 花地湾 1
花地湾 芳村 1
芳村 黄沙 1
黄沙 长寿路 1
长寿路 陈家祠 1
陈家祠 西门口 1
西门口 公园前 1
公园前 农讲所 1
农讲所 烈士陵园 1
烈士陵园 东山口 1
体育西路 石牌桥 1
石牌桥 岗顶 1
岗顶 华师 1
华师 五山 1
五山 天河客运站 1
体育西路 体育中心 1
体育中心 广州东站 1

萝岗 香雪 1
苏元 萝岗 1
暹岗 苏元 1
金峰 暹岗 1
黄陂 金峰 1
高塘石 黄陂 1
柯木塱 高塘石 1
龙洞 柯木塱 1
植物园 龙洞 2
植物园 长湴 2
天河客运站 长湴 2
天河客运站 燕塘 2
燕塘 天平架 1
天平架 沙河顶 1
沙河顶 黄花岗 1
黄花岗 区庄 1
区庄 东山口 1
东山口 东湖 1
东湖 团一大广场 1
团一大广场 北京路 1
北京路 海珠广场 1
海珠广场 一德路 1
一德路 文化公园 1
文化公园 黄沙 1
黄沙 如意坊 2
如意坊 坦尾 1

机场北 机场南 1
机场南 高增 1
高增 人和 1
人和 龙归 1
嘉禾望岗 龙归 黄边 白云大道北 1 
黄边 江夏 1
江夏 萧岗 1
萧岗 白云文化广场 1
白云文化广场 白云公园 1
飞翔公园 白云公园 1
三元里 飞翔公园 1
三元里 广州火车站 1
广州火车站 越秀公园 1
越秀公园 纪念堂 1
纪念堂 公园前 1
公园前 海珠广场 1
海珠广场 市二宫 1
市二宫 江南西 1
江南西 昌岗 1
昌岗 江泰路 1
江泰路 东晓南 2
东晓南 南洲 2
南洲 洛溪 2
洛溪 南浦 2
南浦 会江 2
会江 石壁 1
石壁 广州南站 1
石壁 谢村 2
谢村 钟村 2
钟村 汉溪长隆 2
汉溪长隆 南村万博 2
南村万博 员岗 2
员岗 板桥 1
板桥 大学城南 1

番禺广场 市桥 1
市桥 汉溪长隆 1
汉溪长隆 大石 1
大石 厦滘 1
厦滘 沥滘 1
沥滘 大塘 1
大塘 客村 1
客村 广州塔 1
广州塔 珠江新城 1
珠江新城 体育西路 1
体育西路 林和西 1
林和西 广州东站 1
广州东站 燕塘 1
燕塘 梅花园 1
梅花园 京溪南方医院 1
京溪南方医院 同和 1
同和 永泰 1
永泰 白云大道北 1


嘉禾望岗 白云东平 2
白云东平 夏良 2

 

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐