【数据结构与算法python】最短路径算法-Dijkstra算法

1、引入

当我们通过网络浏览网页、 发送电子邮件、 QQ消息传输的时候, 数据会在联网设备之间流动, 如图, 当PC上的浏览器向服务器请求一个网页时, 请求信息需要:先通过本地局域网,由路由器A发送到Internet,请求信息沿着Internet中的众多路由器传播,最后到达服务器本地局域网所属的路由器B,从而传给服务器。
在这里插入图片描述
我们可以将互联网路由器体系表示为一个带权边的图,路由器作为顶点,路由器之间网络连接作为边,权重可以包括网络连接的速度、网络负载程度、分时段优先级等影响因素,作为一个抽象,我们把所有影响因素合成为单一的权重,如下图所示
在这里插入图片描述

2、功能分析

(1)算法简介

解决信息在路由器网络中选择传播速度最快路径的问题, 就转变为在带权图上最短路径的问题。这个问题与广度优先搜索BFS算法解决的词梯问题相似, 只是在边上增加了权重,如果所有权重相等,还是还原到词梯问题,解决该问题的算法为Dijkstra算法。

(2)算法实现

❖顶点的访问次序由一个优先队列来控制,队列中作为优先级的是顶点的dist属性
❖最初, 只有开始顶点dist设为0, 而其他所有顶点dist设为sys.maxsize(最大整数) , 全部加入优先队列。
❖随着队列中每个最低dist顶点率先出队
❖并计算它与邻接顶点的权重, 会引起其它顶点dist的减小和修改, 引起堆重排
❖并据更新后的dist优先级再依次出队

3、代码实现

class Graph:
    # 图结构初始化
    def __init__(self):
        self.vertices = {}
        self.numVertices = 0

    # 增加节点
    def addVertex(self, key):
        self.numVertices = self.numVertices + 1
        newVertex = Vertex(key)
        self.vertices[key] = newVertex
        return newVertex

    # 获取节点
    def getVertex(self, n):
        if n in self.vertices:
            return self.vertices[n]
        else:
            return None

    # 特殊方法 in
    def __contains__(self, n):
        return n in self.vertices

    # 添加边
    def addEdge(self, f, t, cost=0):
        if f not in self.vertices:
            nv = self.addVertex(f)
        if t not in self.vertices:
            nv = self.addVertex(t)
        self.vertices[f].addNeighbor(self.vertices[t], cost)

    # 获取所有顶点的key值
    def getVertices(self):
        return list(self.vertices.keys())

    # 特殊方法 迭代器
    def __iter__(self):
        return iter(self.vertices.values())


class Vertex:
    # 顶点初始化
    def __init__(self, num):
        self.id = num
        self.connectedTo = {}
        self.color = 'white'  # 颜色
        self.dist = sys.maxsize  # 距离
        self.pred = None  # 前驱节点
        self.disc = 0  # 开始时间
        self.fin = 0  # 结束时间

    # 比较大小
    # def __lt__(self,o):
    #     return self.id < o.id
    # 添加邻边节点
    def addNeighbor(self, nbr, weight=0):
        self.connectedTo[nbr] = weight

    # 设置颜色
    def setColor(self, color):
        self.color = color

    # 设置距离
    def setDistance(self, d):
        self.dist = d

    # 设置前驱
    def setPred(self, p):
        self.pred = p

    # 设置发现时间
    def setDiscovery(self, dtime):
        self.disc = dtime

    # 设置结束时间
    def setFinish(self, ftime):
        self.fin = ftime

    # 获取结束时间
    def getFinish(self):
        return self.fin

    # 获取发现时间
    def getDiscovery(self):
        return self.disc

    # 获取前驱
    def getPred(self):
        return self.pred

    # 获取距离
    def getDistance(self):
        return self.dist

    # 获取颜色
    def getColor(self):
        return self.color

    # 获取连接边
    def getConnections(self):
        return self.connectedTo.keys()

    # 获取与某个节点间的权重值
    def getWeight(self, nbr):
        return self.connectedTo[nbr]

    # 特殊方法 print
    def __str__(self):
        return str(self.id) + ":color " + self.color + ":disc " + str(self.disc) + ":fin " + str(
            self.fin) + ":dist " + str(self.dist) + ":pred \n\t[" + str(self.pred) + "]\n"

    # 获取Id
    def getId(self):
        return self.id

class PriorityQueue:
    def __init__(self):
        self.heapArray = [(0, 0)]
        self.currentSize = 0

    def buildHeap(self, alist):
        self.currentSize = len(alist)
        self.heapArray = [(0, 0)]
        for i in alist:
            self.heapArray.append(i)
        i = len(alist) // 2
        while (i > 0):
            self.percDown(i)
            i = i - 1

    def percDown(self, i):
        while (i * 2) <= self.currentSize:
            mc = self.minChild(i)
            if self.heapArray[i][0] > self.heapArray[mc][0]:
                tmp = self.heapArray[i]
                self.heapArray[i] = self.heapArray[mc]
                self.heapArray[mc] = tmp
            i = mc

    def minChild(self, i):
        if i * 2 > self.currentSize:
            return -1
        else:
            if i * 2 + 1 > self.currentSize:
                return i * 2
            else:
                if self.heapArray[i * 2][0] < self.heapArray[i * 2 + 1][0]:
                    return i * 2
                else:
                    return i * 2 + 1

    def percUp(self, i):
        while i // 2 > 0:
            if self.heapArray[i][0] < self.heapArray[i // 2][0]:
                tmp = self.heapArray[i // 2]
                self.heapArray[i // 2] = self.heapArray[i]
                self.heapArray[i] = tmp
            i = i // 2

    def add(self, k):
        self.heapArray.append(k)
        self.currentSize = self.currentSize + 1
        self.percUp(self.currentSize)

    def delMin(self):
        retval = self.heapArray[1][1]
        self.heapArray[1] = self.heapArray[self.currentSize]
        self.currentSize = self.currentSize - 1
        self.heapArray.pop()
        self.percDown(1)
        return retval

    def isEmpty(self):
        if self.currentSize == 0:
            return True
        else:
            return False

    def decreaseKey(self, val, amt):
        # this is a little wierd, but we need to find the heap thing to decrease by
        # looking at its value
        done = False
        i = 1
        myKey = 0
        while not done and i <= self.currentSize:
            if self.heapArray[i][1] == val:
                done = True
                myKey = i
            else:
                i = i + 1
        if myKey > 0:
            self.heapArray[myKey] = (amt, self.heapArray[myKey][1])
            self.percUp(myKey)

    def __contains__(self, vtx):
        for pair in self.heapArray:
            if pair[1] == vtx:
                return True
        return False

def dijkstra(aGraph,start):
    pq = PriorityQueue()
    start.setDistance(0)
    pq.buildHeap([(v.getDistance(),v) for v in aGraph])
    while not pq.isEmpty():
        currentVert = pq.delMin()
        for nextVert in currentVert.getConnections():
            newDist = currentVert.getDistance()+currentVert.getWeight(nextVert)
            if newDist<nextVert.getDistance():
                nextVert.setDistance(newDist)
                nextVert.setPred(currentVert)
                pq.decreaseKey(nextVert,newDist)

4、算法分析

  • Dijkstra算法只能处理大于0的权重,如果图中出现负数权重,则算法会陷入无限循环
  • 首先, 将所有顶点加入优先队列并建堆,时间复杂度为O(|V|)
  • 其次, 每个顶点仅出队1次, 每次delMin花费O(log|V|), 一共就是O(|V|log|V|)
  • 另外, 每个边关联到的顶点会做一次decreaseKey操作(O(log|V|)), 一共是O(|E|log|V|)
  • 上 面 三 个 加 在 一 起 , 数 量 级 就 是O((|V|+|E|)log|V|)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值