swift--求源點到各頂點的最短距離
阿新 • • 發佈:2018-12-17
// 給定一個有向鄰接圖,求從原點出發到任意一點的最短距離 注:
- 採用Dijkstra貪心演算法優化版,為了減少建立二維鄰接矩陣的空間開銷,直接使用頂點的屬性。具體關於此演算法的解釋說明可百度.
- 優化功能:由於正在學習設計模式,所以增加了工廠類,可以在工廠類中指定任一個頂點為原點,求從原點到其它任意一點的最短距離。
import Foundation /// 定義頂點的結構 struct Point { var id:Int //編號 var isYuanDian : Bool //是否原點 var isV:Bool=false //是否屬於V陣營。true:屬於V陣營; false:屬於V-S陣營 var juZhen:[(nextId:Int,juli:Int)] = [] //臨接矩陣的陣列,規則為[(指向的頂點編號,距離)] init(id:Int,isYuanDian:Bool=false,juZhen:[(nextId:Int,juli:Int)]) { self.id=id self.isYuanDian = isYuanDian self.juZhen=juZhen //如果原點,則自動加入到V陣營。其它頂點在V-S陣營 if isYuanDian == true { isV = true } } } /// 頂點工廠類,在此生成所有頂點 class PointFactory { func createPoints() -> [Point] { var points:[Point]=[] //最終返回的點陣列 var tmpJuZhen:[(nextId:Int,juli:Int)] = [] //臨時的矩陣陣列,用於生成某個點 //1號頂點,預設為原點 tmpJuZhen.append((nextId: 2,juli:2)) tmpJuZhen.append((nextId: 3, juli: 5)) points.append( Point(id: 1, isYuanDian: true, juZhen: tmpJuZhen )) //2號頂點 tmpJuZhen.removeAll() tmpJuZhen.append((nextId: 4,juli:6)) tmpJuZhen.append((nextId: 3, juli: 2)) points.append( Point(id: 2, isYuanDian: false, juZhen: tmpJuZhen )) //3號頂點 tmpJuZhen.removeAll() tmpJuZhen.append((nextId: 4,juli:7)) tmpJuZhen.append((nextId: 5, juli: 1)) points.append( Point(id: 3, isYuanDian: false, juZhen: tmpJuZhen )) //4號頂點 tmpJuZhen.removeAll() tmpJuZhen.append((nextId: 3,juli:2)) tmpJuZhen.append((nextId: 5, juli: 4)) points.append( Point(id: 4, isYuanDian: false, juZhen: tmpJuZhen )) //5號頂點 tmpJuZhen.removeAll() points.append( Point(id: 5, isYuanDian: false, juZhen: tmpJuZhen )) //判斷有幾個原點,只允許有一個,如果有多個,則保留第一個原點 var i=0 for point in points { if point.isYuanDian { i += 1 } } //沒有原點則預設第一個為原點 if i==0 { points[0].isYuanDian = true print("沒有原點,預設第一個頂點為原點") } //如果有多個原點,則保留第一個 else if i>1{ var j=0 //記錄第一個為原點的下標 for i in 0..<points.count{ if points[i].isYuanDian { j = i break } } for i in (j+1)..<points.count { if points[i].isYuanDian { points[i].isYuanDian = false } } print("有多個原點,預設設定第一個為原點") } return points } } /// 計算最短路徑的類 class ZuiDuan { private var points : [Point] //頂點陣列 private let number :Int //頂點個數,包含原點 private let wuQiongDa = 10000 //定義無窮大 private var yuanDian = 0//原點的下標編號 private var zuiDuan:[Int] = [] //各個頂點的最短路徑陣列 private var p:[Int] = [] //頂點的前驅陣列 init() { points = PointFactory().createPoints() //生成頂點集合 number = points.count //頂點個數 //先求哪個是頂點 for i in 0..<number { if points[i].isYuanDian { self.yuanDian = i break } } //初始化各個頂點的最短路徑陣列zuiduan[] for _ in 0..<number { zuiDuan.append(wuQiongDa) //對陣列全部用無窮大填充 } //再用原點的下級鄰接矩陣填充各頂點的最短路徑陣列zuiduan[] zuiDuan[yuanDian]=0 for tmpJuZhen in points[yuanDian].juZhen{ //讀取原點的鄰接矩陣 zuiDuan[tmpJuZhen.nextId-1] = tmpJuZhen.juli } //初始化各頂點的前驅陣列 for i in 0..<number{ //如果【0】的值為無窮大,則為-1,表示沒有前驅點 if zuiDuan[i] == 0 || zuiDuan[i] == wuQiongDa { p.append(-1) } //如果值不為無窮大,則為1,表示和頂點有邊相連,前驅點為頂點 else{ p.append(yuanDian+1) } } } /// 列印鄰接矩陣,預設最短路徑陣列,前驅陣列 func prinfJuZhen(){ print("各頂點到原點的最短路徑陣列:") for juli in zuiDuan { print("\(juli) ") } print("各頂的前驅陣列為:") for x in p{ print("\(x)") } } /// 求V-S中到頂點距離最短的點和距離 /// /// - Returns: 返回最短的ID和距離,其中iD為下標,當ID為-1時說明已經沒有下級節點 func getVS_ZuiDuan() ->(id:Int,juli:Int) { //再確定離頂點最短的距離和頂點 var min=wuQiongDa //最小的距離,預設為無窮大 var t = -1 //最小的距離對應的頂點下標,如果=-1,說明沒有下級節點 //統計V-S中的頂點 --新建VS陣列,存放還在V-S中的頂點 var vs:[Point] = [] for i in 0..<points.count{ if points[i].isV == false { vs.append(points[i]) } } //如果V-S為空,代表所有頂點都在V陣營,返回t=-1 if vs.isEmpty { return (t,min) } //當還有頂點在V-S陣營的時侯,求V-S陣營中離原點的最短距離 for i in 0..<vs.count { if min >= zuiDuan[vs[i].id-1] { min = zuiDuan[vs[i].id-1] } } // 求對短距離對應的點 for i in 0..<number { //必須為VS陣營中的頂點 if min == zuiDuan[i] && points[i].isV == false{ t=i break } } return (t,min) } /// 列印結果 func echoResult() { //顯示第i+1個頂點的資訊 //print("原點為頂點\(yuanDian+1)。。。") for i in 0..<number { var str = "" //最短的路徑 if points[i].isYuanDian != true { var x = i //定義最開始的起始點 str = String(i+1) //最後顯示的字串 while p[x] >= 0 { str = str + "->" + String(p[x]) x=p[x]-1 } } if zuiDuan[i] == wuQiongDa { str = "頂點\(i+1)無法到達" } else if zuiDuan[i] == 0{ str = "頂點\(i+1)為原點" } else{ str = "頂點\(i+1)的最短距離為:\(zuiDuan[i]),最短路徑為:"+str } print(str) } } /// 計算各個點到頂點的最短路徑 /// /// - Returns: func zuiDuanLuJin() { //開始按貪心演算法求解。 //1.令V={1},V-S={2,3,4,5},找V-S中離頂點最近的點 let zuiJin_VS=getVS_ZuiDuan() //如果VS陣營中已經沒有頂點了,則顯示資料 if zuiJin_VS.id == -1 { //顯示結果 // prinfJuZhen() echoResult() return } //更新最短距離陣列 let i = zuiJin_VS.id zuiDuan[i] = zuiJin_VS.juli //2.走捷徑。與i鄰接的點,分別判斷最短 //a.通過上一級節點的最短距離。 //b.最短距離陣列p[]中已有的值。 //a和b對比,小的就是新的最短距離,重新更新到陣列zuiduan[]中,同時更新前驅陣列p[] var minJuli=0 //定義一個最短距離的變數 // print("頂點=\(i+1)") for juZhen_item in points[i].juZhen { // print("頂點矩陣為:\(juZhen_item.nextId):\(juZhen_item.juli)") //a.通過上級節點的最短距離 minJuli = zuiDuan[i] + juZhen_item.juli //b.和P【】中的值對比,將比較小的更新到P[]中 if minJuli <= zuiDuan[juZhen_item.nextId-1] { zuiDuan[juZhen_item.nextId-1] = minJuli p[juZhen_item.nextId-1] = i+1 } } //3.將i劃入V陣營,重新在V-S陣容中找到頂點最短的 points[i].isV = true //使用遞迴進行迴圈 zuiDuanLuJin() } } let zuiduan = ZuiDuan() zuiduan.zuiDuanLuJin() //zuiduan.prinfJuZhen()