swift--求源点到各顶点的最短距离
程序员文章站
2022-06-08 13:55:07
...
// 给定一个有向邻接图,求从原点出发到任意一点的最短距离
注:
- 采用Dijkstra贪心算法优化版,为了减少建立二维邻接矩阵的空间开销,直接使用顶点的属性。具体关于此算法的解释说明可百度.
- 优化功能:由于正在学习设计模式,所以增加了工厂类,可以在工厂类中指定任一个顶点为原点,求从原点到其它任意一点的最短距离。
- 将各顶点封装成了类,每个实际顶点对应一个point对像,最重要的是类属性中的邻接矩阵。
4.打包代码在此下载:
https://download.csdn.net/download/qq_42439742/10744154
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()
上一篇: 关于互联网营销竞价推广的分析