最短路徑 - Dijkstra算法

算法原理

  • 保存兩個(gè)數(shù)組S和U棒动,S用于存放已經(jīng)找到和起點(diǎn)之間最短距離的點(diǎn),U用于存放尚未找到最短距離的點(diǎn)叨叙,起初S中只有起點(diǎn)锭弊,其余所有的點(diǎn)存放在U中,并根據(jù)和起點(diǎn)是否相鄰擂错,修改了距離起點(diǎn)的距離味滞,不相鄰則默認(rèn)為無窮遠(yuǎn)。
  • 循環(huán)遍歷U中的點(diǎn),找出離起點(diǎn)距離最近的點(diǎn)剑鞍,該點(diǎn)到起點(diǎn)的距離就是該點(diǎn)到起點(diǎn)的最短距離昨凡,將該點(diǎn)從U中移除并放置到S中去,并重新計(jì)算U中的點(diǎn)距離起點(diǎn)的距離蚁署。這樣每次選取的點(diǎn)都是剩余點(diǎn)中距離起點(diǎn)最近的點(diǎn)便脊。
  • 循環(huán)上述操作,直到找到終點(diǎn)到起點(diǎn)的最短路徑或則循環(huán)結(jié)束光戈。

通俗解釋

算法每次都查找距離起始點(diǎn)最近的點(diǎn)哪痰,那么剩下的點(diǎn)距離起始點(diǎn)的距離一定比當(dāng)前點(diǎn)大。

圖解

1.選定A節(jié)點(diǎn)并初始化久妆,如上述步驟3所示

image

2.執(zhí)行上述 4晌杰、5兩步驟,找出U集合中路徑最短的節(jié)點(diǎn)D 加入S集合筷弦,并根據(jù)條件 if ( 'D 到 B,C,E 的距離' + 'AD 距離' < 'A 到 B,C,E 的距離' ) 來更新U集合

image

3.這時(shí)候 A->B, A->C 都為3肋演,沒關(guān)系。其實(shí)這時(shí)候他倆都是最短距離烂琴,如果從算法邏輯來講的話惋啃,會(huì)先取到B點(diǎn)。而這個(gè)時(shí)候 if 條件變成了 if ( 'B 到 C,E 的距離' + 'AB 距離' < 'A 到 C,E 的距離' ) 监右,如圖所示這時(shí)候A->B距離 其實(shí)為 A->D->B

image

思路就是這樣,往后就是大同小異了
算法結(jié)束

Dijkstra算法的探測(cè)過程

image

(圖片來源于網(wǎng)絡(luò))

Dijkstra算法保證能找到一條從初始點(diǎn)到目標(biāo)點(diǎn)的最短路徑异希,只要所有的邊都有一個(gè)非負(fù)的代價(jià)值健盒。在上圖中,粉紅色的結(jié)點(diǎn)是初始結(jié)點(diǎn)称簿,藍(lán)色的是目標(biāo)點(diǎn)扣癣,而類菱形的有色區(qū)域則是Dijkstra算法掃描過的區(qū)域。顏色最淡的區(qū)域是那些離初始點(diǎn)最遠(yuǎn)的憨降,因而形成探測(cè)過程(exploration)的邊境(frontier)父虑。因而Dijkstra算法可以找到一條最短的路徑,但是效率上并不高授药。

lua代碼實(shí)現(xiàn)

  • 點(diǎn)的數(shù)據(jù)結(jié)構(gòu)的定義
--[[
--     Dijkstra算法中需要的點(diǎn)的信息結(jié)構(gòu)
 ]]

local MAX_DISTANCE = 999999999999999999
---------------------------------------------------------------------------------------------
--------------------------------        相鄰的點(diǎn)的結(jié)構(gòu)     -----------------------------------
---------------------------------------------------------------------------------------------
local LinkObj = ns.class("LinkObj")
function LinkObj:ctor(pointIdx, distance)
    ns.utils.registerVariableAndSetGetFunc(self, "pointIdx", pointIdx or 0)
    ns.utils.registerVariableAndSetGetFunc(self, "distance", distance or 0)
end

---------------------------------------------------------------------------------------------
-------------------------------         點(diǎn)的結(jié)構(gòu)        --------------------------------------
local DijkstraPointObj = ns.class("DijkstraPointObj")

function DijkstraPointObj:ctor(pointIdx,links)
    ns.utils.registerVariableAndSetGetFunc(self,"pointIdx", pointIdx or 0)  -- 點(diǎn)的索引
    ns.utils.registerVariableAndSetGetFunc(self, "links",   links or {}) -- 相鄰的點(diǎn)的集合



    ns.utils.registerVariableAndSetGetFunc(self,"distance",  MAX_DISTANCE) -- 用于輔助算法記錄距離起點(diǎn)的距離的
    ns.utils.registerVariableAndSetGetFunc(self,"routes",    {})   -- 記錄距離起點(diǎn)的過程中經(jīng)過的點(diǎn)
end

function DijkstraPointObj:initWithParams(pointIdx, links)
    self._pointIdx = pointIdx
    self._links    = links or {}

    return self
end

--[[
--      添加一個(gè)相鄰的點(diǎn)
 ]]
function DijkstraPointObj:addLink(pointIdx,distance)
    local linkObj = LinkObj.new(pointIdx,distance)

    table.insert(self._links,linkObj)
end


--[[
--      判斷是否相鄰
 ]]
function DijkstraPointObj:isLink(pointIdx)
    local links = self._links or {}
    for i = 1,#links do
        local link = links[i]
        if link and link:getPointIdx() == pointIdx then
            return true
        end
    end

    return false
end

--[[
--      獲取兩個(gè)頂點(diǎn)之間的距離
--          如果相鄰士嚎,返回之間的實(shí)際距離,否則返回?zé)o窮大
 ]]
function DijkstraPointObj:getLinkDistance(pointIdx)
    local links = self._links or {}
    for i = 1,#links do
        local link = links[i]
        if link and link:getPointIdx() == pointIdx then
            return link:getDistance()
        end
    end

    return MAX_DISTANCE
end

--[[
--      判斷距離是否時(shí)無窮遠(yuǎn)
 ]]
function DijkstraPointObj:isValidDistance()
    local distance = self:getDistance()

    return distance < MAX_DISTANCE
end


return DijkstraPointObj
  • 算法代碼的實(shí)現(xiàn)
--[[
--       最短路徑算法
 ]]

local DijkstraCommand = ns.class("DijkstraCommand")

function DijkstraCommand:ctor(points)
    self._points    = points or {}
    self._recordMap = {}        -- 將所有計(jì)算出最短路徑信息全部記錄下來悔叽,便于后續(xù)使用
end

--[[
--      將最近路徑全部記錄下來
 ]]
function DijkstraCommand:recordResult(beginIdx,endIdx,routes,distance)
    local key = string.format("%d_%d",beginIdx,endIdx)

    local info = {
        beginIdx = beginIdx,
        endIdx   = endIdx,
        routes   = routes,
        distance = distance,
    }

    self._recordMap[key] = info
end

--[[
--      查找是否已經(jīng)有記錄了
 ]]

function DijkstraCommand:findRecord(beginIdx,endIdx)
    local firstKey = string.format("%d_%d",beginIdx,endIdx)

    local info = self._recordMap[firstKey]
    if info then
        return info.routes,info.distance
    end

    local secondKey = string.format("%d_%d",endIdx,beginIdx)
    local info = self._recordMap[secondKey]
    if info then
        return table.reverse(info.routes),info.distance
    end

    return nil
end

--[[
--      清空本地的記錄
 ]]
function DijkstraCommand:cleanRecord()
    self._recordMap = {}
end


--[[
--      開始查找,并將查找到的路徑轉(zhuǎn)換成點(diǎn)的數(shù)組的形式
 ]]
function DijkstraCommand:find(beginIdx,endIdx,points)
    local routes,distance = self:start(beginIdx,endIdx,points)
    routes      = routes or {}
    distance    = distance or 0

    local points    = {}
    for i = 1,#routes do
        table.insert(points,routes[i]:getPointIdx())
    end

    return points,distance
end



--[[
--      開始尋找動(dòng)作
--          @beginIdx:起點(diǎn)
--          @endIdx: 終點(diǎn)
--          @points:所有的點(diǎn)的列表莱衩,DijkstraPointObj對(duì)象的列表
 ]]
function DijkstraCommand:start(beginIdx,endIdx,points)
    ns.logW(string.format("DijkstraCommand尋路:起點(diǎn) = %d, 終點(diǎn) = %d",beginIdx,endIdx))
    if beginIdx == endIdx then
        return {},0
    end

    points = points or self._points

    -- 檢查記錄中是否已經(jīng)查到該點(diǎn)
    local routes,distance = self:findRecord(beginIdx,endIdx)
    if routes then
        return routes,distance
    end


    local S = {}    -- 已經(jīng)遍歷過的節(jié)點(diǎn)
    local U = {}    -- 尚未遍歷的節(jié)點(diǎn)

    -- 將起始點(diǎn)放置到S列表中去
    local beginPointObj = nil
    for i = 1,#points do
        local pointObj = points[i]
        local pointIdx = pointObj:getPointIdx()
        if pointIdx == beginIdx then
            table.insert(S,pointObj)
            beginPointObj = pointObj
            break
        end
    end

    if not beginPointObj then
        return {},0
    end

    -- 將剩余的點(diǎn)全部防止到U列表中去
    for i = 1,#points do
        local pointObj = points[i]
        local pointIdx = pointObj:getPointIdx()
        if pointIdx ~= beginIdx then
            local distance = beginPointObj:getLinkDistance(pointIdx)
            pointObj:setDistance(distance)
            pointObj:setRoutes({beginPointObj,pointObj})
            table.insert(U,pointObj)
        end
    end

    while #U > 0 do
        -- 將U中的點(diǎn)按照到beginPointObj的距離從近到遠(yuǎn)排列
        table.sort(U,function(a,b)
            local distanceA = a:getDistance()
            local distanceB = b:getDistance()
            if distanceA ~= distanceB then
                return distanceA < distanceB
            end

            return a:getPointIdx() < b:getPointIdx()
        end)


        -- 選擇距離最近的(距離不是無窮遠(yuǎn)的點(diǎn)),添加到S中去娇澎,并從U中移除
        local pointObj = U[1]
        if pointObj and pointObj:isValidDistance() then
            table.insert(S,pointObj)
            table.remove(U,1)
            -- 判斷是是否時(shí)終點(diǎn)
            if pointObj:getPointIdx() == endIdx then
                ns.logW("DijkstraCommand找到結(jié)果")
                return pointObj:getRoutes(), pointObj:getDistance()
            end

            -- 更新U中其他的距離S點(diǎn)的距離(相對(duì)于pointObj的)
            for i = 1,#U do
                local nextPointObj = U[i]

                if pointObj:isLink(nextPointObj:getPointIdx()) then
                    local distance = pointObj:getLinkDistance(nextPointObj:getPointIdx())
                        + pointObj:getDistance()

                    if distance < nextPointObj:getDistance() then
                        nextPointObj:setDistance(distance)

                        local routes = ns.clone(pointObj:getRoutes())
                        table.insert(routes,nextPointObj)
                        nextPointObj:setRoutes(routes)
                    end
                end
            end
        else
            ns.logW(string.format("DijkstraCommand未找到結(jié)果  pointId = ",pointObj:getPointIdx()))
            return {},0
        end
    end

    return {},0
end


return DijkstraCommand


參考文章

數(shù)據(jù)結(jié)構(gòu)--Dijkstra算法最清楚的講解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末笨蚁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌括细,老刑警劉巖伪很,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奋单,居然都是意外死亡锉试,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門辱匿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來键痛,“玉大人,你說我怎么就攤上這事匾七⌒醵蹋” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵昨忆,是天一觀的道長(zhǎng)丁频。 經(jīng)常有香客問我,道長(zhǎng)邑贴,這世上最難降的妖魔是什么席里? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮拢驾,結(jié)果婚禮上奖磁,老公的妹妹穿的比我還像新娘。我一直安慰自己繁疤,他們只是感情好咖为,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稠腊,像睡著了一般躁染。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上架忌,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天吞彤,我揣著相機(jī)與錄音,去河邊找鬼叹放。 笑死饰恕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的井仰。 我是一名探鬼主播懂盐,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼糕档!你這毒婦竟也來了莉恼?” 一聲冷哼從身側(cè)響起拌喉,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俐银,沒想到半個(gè)月后尿背,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捶惜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年田藐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吱七。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汽久,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出踊餐,到底是詐尸還是另有隱情景醇,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布吝岭,位于F島的核電站啊易,受9級(jí)特大地震影響槽惫,放射性物質(zhì)發(fā)生泄漏隅津。R本人自食惡果不足惜韧涨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望幕帆。 院中可真熱鬧获搏,春花似錦、人聲如沸失乾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仗扬。三九已至,卻和暖如春蕾额,著一層夾襖步出監(jiān)牢的瞬間早芭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工诅蝶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留退个,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓调炬,卻偏偏與公主長(zhǎng)得像语盈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缰泡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 專業(yè)考題類型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項(xiàng)A選項(xiàng)B選項(xiàng)C選項(xiàng)D選項(xiàng)E選項(xiàng)F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 8,981評(píng)論 0 13
  • 1. 關(guān)于診斷X線機(jī)準(zhǔn)直器的作用刀荒,錯(cuò)誤的是()。 (6.0 分) A. 顯示照射野 B. 顯示中心線 C. 屏蔽多...
    我們村我最帥閱讀 10,340評(píng)論 0 5
  • 所謂的最短路徑,顧名思義就是帶權(quán)值的圖中缠借,求一個(gè)結(jié)點(diǎn)到另一個(gè)結(jié)點(diǎn)的路徑最小干毅。 Dijkstra算法 1.介紹 迪杰...
    放開那個(gè)BUG閱讀 1,365評(píng)論 1 0
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些閱讀 2,028評(píng)論 0 2
  • 01. 顱腦CT掃描采用的聽眶線是()。 (1.0 分) A. 外耳孔與外眼眥的連線 B. 外耳孔上緣與眶下緣的連...
    我們村我最帥閱讀 3,175評(píng)論 0 6