最短路徑 - Floyd算法

算法思想

  • Floyd算法是一種動(dòng)態(tài)規(guī)劃算法勒奇,查找i到j(luò)之間的最短距離,我們可以找一個(gè)中間點(diǎn)k,然后變成子問題,i到k的最短路徑和k到j(luò)的最短路徑尖坤。也就是說,我們可以枚舉中間點(diǎn)k,找到最小的d[i][k]+d[k][j],作為d[i][j]的最小值。

  • Floyd構(gòu)造的結(jié)構(gòu)非常巧妙:找i和j之間通過編號(hào)不超過k(k從1到n)的節(jié)點(diǎn)的最短路徑(一定要注意焊唬,這里是當(dāng)前最短路徑媒惕,當(dāng)k=n時(shí)達(dá)到最終最短路徑)拼苍。為了便于說明贬芥,我們可以弄一個(gè)三維數(shù)組f[k][i][j]表示i和j之間可以通過編號(hào)不超過k的節(jié)點(diǎn)的“最短路徑”吐辙。對(duì)于k-1到k,只有兩種可能蘸劈,經(jīng)過編號(hào)為k的點(diǎn)昏苏,要么不能找到一條從i到j(luò)的更短路,此時(shí)有f[k][i][j] = f[k-1][i][j] 威沫;要么能找到贤惯,那這個(gè)最短路徑一定是d[i][k]+d[k][j],那么就用這個(gè)較小的距離去更新d[i][j]棒掠。綜合以上兩種情況孵构,f[k][i][j] = min(f[k-1][i][j] , f[k-1][i][k]+f[k-1][k][j])

動(dòng)態(tài)轉(zhuǎn)移推導(dǎo)(重要)

  • 動(dòng)態(tài)轉(zhuǎn)移思想可以認(rèn)為是在建立一種當(dāng)前狀態(tài)向之前狀態(tài)的轉(zhuǎn)移的方法。
  • d[k][i][j]表示使用1號(hào)到K號(hào)的中間點(diǎn)來計(jì)算i到j(luò)直接按的最短距離烟很。
  • i和j之間的最短距離有兩種情況颈墅,情況一:最短距離經(jīng)過K點(diǎn);情況二:最短距離不經(jīng)過k點(diǎn)雾袱。
  • 如果i和j之間的最短距離經(jīng)過k點(diǎn)精盅,則D[k,i,j] = D[K,i,j],否則D[k,i,j] = D[k-1,i,j]谜酒;那么最終的結(jié)果為D[k,i,j] = min(D[k,i,j],D[k-1,i,k]+D[k-1,k,j])。
  • 因此可知妻枕,如果i到j(luò)之間的最短距離如果不經(jīng)過k點(diǎn)僻族,則可轉(zhuǎn)移到k-1、k-2... 一直到1屡谐,總是可以將k的狀態(tài)向前轉(zhuǎn)移到上一個(gè)狀態(tài)述么。
  • 最后通過k從1到n的遍歷,可最終找到i到j(luò)之間的最短距離愕掏。
  • 兩點(diǎn)之間的最短距離如果需要經(jīng)過第三點(diǎn)的話 這個(gè)第三點(diǎn)一定不是起點(diǎn)或終點(diǎn)(這樣的話就是兩點(diǎn)之間的距離是最短距離了)度秘。
  • 如果點(diǎn)i和點(diǎn)j之間經(jīng)過k點(diǎn),則i點(diǎn)和k點(diǎn)之間一定不會(huì)經(jīng)過j 即路徑之間不會(huì)出現(xiàn)相互引用到的情況烙样。

通俗理解

  • Floyd算法通過依次修改中間節(jié)點(diǎn)耍共,計(jì)算任意兩點(diǎn)的經(jīng)過中間節(jié)點(diǎn)后的距離,計(jì)算出任意兩個(gè)節(jié)點(diǎn)之間的最短距離粟耻;每次計(jì)算完成的結(jié)果都是當(dāng)前的最優(yōu)解垢乙,當(dāng)所有的中間節(jié)點(diǎn)被遍歷結(jié)束后獲得的結(jié)果就是整個(gè)算法的最優(yōu)解锨咙。

算法步驟分析

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


image

算法代碼實(shí)現(xiàn)

  • 算法中點(diǎn)的定義
--[[
--      Floyd算法中的點(diǎn)的數(shù)據(jù)結(jié)構(gòu)
 ]]
local max_distance = 9999999999999999999999999999
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



local FloydPointObj = ns.class("FloydPointObj")

function FloydPointObj:ctor(pointIdx)
    ns.utils.registerVariableAndSetGetFunc(self,"pointIdx", pointIdx or 0)  -- 當(dāng)前點(diǎn)的ID
    ns.utils.registerVariableAndSetGetFunc(self,"links",    {}) -- 當(dāng)前點(diǎn)的所有的相鄰點(diǎn)
end

--[[
--      添加相鄰點(diǎn)
 ]]
function FloydPointObj:addLink(pointIdx,distance)
    local link = LinkObj.new(pointIdx,distance)

    table.insert(self._links,link)
    return self
end

--[[
--      獲取兩個(gè)點(diǎn)是否是相鄰
 ]]
function FloydPointObj:isLink(pointIdx)
    for i = 1,#self._links do
        local link = self._links[i]
        if link and link:getPointIdx() == pointIdx then
            return true
        end
    end

    return false
end

--[[
--      獲取兩個(gè)點(diǎn)之間的距離,如果兩個(gè)點(diǎn)不相鄰追逮,返回?zé)o窮大
 ]]
function FloydPointObj:getLinkDistance(pointIdx)
    for i = 1,#self._links do
        local link = self._links[i]
        if link and link:getPointIdx() == pointIdx then
            return link:getDistance()
        end
    end

    return max_distance
end


return FloydPointObj


  • 算法的實(shí)現(xiàn)
local FloydCommand = ns.class("FloydCommand")

function FloydCommand:ctor()
    self._D = {}        -- 存放最短距離的二維數(shù)組
    self._P = {}        -- 存放最短路徑經(jīng)過的點(diǎn)的二維數(shù)據(jù)
end

--[[
--      計(jì)算出所有的點(diǎn)之間的最短距離的數(shù)組
 ]]
function FloydCommand:execute(floydPointObjs)
    local points    = floydPointObjs or {}
    local pointNums = #points

    local D = self._D
    local P = self._P
    -- 初始化距離數(shù)組和最短路徑經(jīng)過的點(diǎn)的數(shù)組
    for i =1,pointNums do
        local pointObj = points[i]
        local subDisTab     = {}
        local subPointTab   = {}
        for j = 1,pointNums do
            local subPointObj = points[j]
            local distance = pointObj:getLinkDistance(subPointObj:getPointIdx())
            table.insert(subDisTab,distance)
            table.insert(subPointTab,{pointObj:getPointIdx()})
        end

        table.insert(D,subDisTab)
        table.insert(P,subPointTab)
    end

    -- 開始執(zhí)行算法核心循環(huán)
    for k = 1,pointNums do  -- 該層循環(huán)用于計(jì)算所有的點(diǎn)之間經(jīng)過該點(diǎn)之后的距離是否是最短距離
        for i = 1,pointNums do
            for j = 1,pointNums do
                local distance = D[i][k] + D[k][j]
                if D[i][j] > distance then
                    D[i][j] = distance
                    local points = ns.clone(P[i][k])
                    table.insertto(points,ns.clone(P[k][j]))
                    P[i][j] = points
                end
            end
        end
    end

    return self
end

function FloydCommand:find(beginPoint,endPoint)
    local distance = self._D[beginPoint][endPoint] or 0
    local points   = ns.clone(self._P[beginPoint][endPoint] or {})
    -- 注意P中的路徑不包括終點(diǎn)酪刀,需要手動(dòng)添加
    table.insert(points,endPoint)
    return points,distance
end

return FloydCommand

參考文章

探求Floyd算法的動(dòng)態(tài)規(guī)劃本質(zhì)

floyd算法:我們真的明白floyd嗎?

最后編輯于
?著作權(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