原版及其加入定制的堆进行改进版本
package Graph
import (
"fmt"
"math"
"testing"
)
/*
Dijkstra
1. Dijkstra 算法必须指定一个源点
2. 生成一个源点到各个点的最小距离表,一开始只有一条记录,即原点到自己的最小距离为0,源点到其他所有点的最小距离都为正无穷大
3. 从距离表中拿出没被拿过记录里的最小记录,通过这个点发出的边,更新源点到各个点的最小距离表,不断重复这一步
4. 原点到所有的点记录如果都被拿过一遍,过程停止,最小距离表得到了
*/
/*
4
A --------> D ---------> E
| \ 6 ^ /`^
| \ | 2 / |
1 | \------>C--------/ |
| 7 ^ 23 | 170
| | 2 |
|---------->B -----------|
A
A 0 A --> A
B 1 A --> B
C 3 A --> B --> C
D 5 A --> B --> C --> D
E 9 A --> B --> C --> D --> E
A
A B C D E
0 +Inf +Inf +Inf +Inf
从原节点出发,到A的距离为0
A B C D E
0 +Inf +Inf +Inf +Inf
以A为桥梁点往外跳
----
A | B C D E
0 | 1 7 6 +Inf
----
锁死A
以B为桥梁点往外跳
---- ----
A | |B | C D E
0 | |1 | 3 6 171
---- ----
锁死B
以C为桥梁点往外跳
---- ---- ----
A | |B | |C | D E
0 | |1 | |3 | 5 26
---- ---- ----
锁死C
以D为桥梁点往外跳
---- ---- --- ----
A | |B | |C | |D | E
0 | |1 | |3 | |5 | 9
---- ---- --- ----
锁死D
以E为桥梁点往外跳
---- ---- ---- ---- ----
A | |B | |C | |D | |E |
0 | |1 | |3 | |5 | |9 |
---- ---- ---- ---- ----
锁死E 结束
实质就是贪心算法
如果过程中有相同的点可以不更新
*/
/*
Tsp问题,商旅问题, 属于动态规划问题,整张图是全连接, 从某个点出发,经过所有点再走
迪杰斯特拉:要求边没有权值为负数的边,有向图和无向图无所谓
中间有相同的可以不更新
深度优先遍历 时间复杂度 会很大
其实就是贪心的思想
*/
func Min(a, b int) int {
if a > b {
return b
}
return a
}
//从from点出发到所有点的最小距离
//key:从from点出发到达的key
//value:从from出发到达key的最小距离
//如果在表中,没有T的记录,含义是从from出发到T这个点的距离为正无穷
func dijkstra1(from *Node) map[*Node]int { //指定出发点,出发点到每个点的最短距离
distanceMap := make(map[*Node]int)
distanceMap[from] = 0
//已经求过距离的点,存在selectedNodes中,以后再也不碰
selectedNodes := make(map[*Node]struct{})
minNode := getMinDistanceAndUnselectedNode(distanceMap, selectedNodes)
for minNode != nil {
distance := distanceMap[minNode]
for k, _ := range minNode.Edges {
toNode := minNode.Edges[k].To
if _, ok := distanceMap[toNode]; !ok {
distanceMap[toNode] = distance + minNode.Edges[k].Weight
} else {
distanceMap[toNode] = Min(distanceMap[toNode], distance+minNode.Edges[k].Weight)
}
}
selectedNodes[minNode] = struct{}{} //锁住
minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes)
}
return distanceMap
}
func getMinDistanceAndUnselectedNode(distanceMap map[*Node]int, touchedNodes map[*Node]struct{}) *Node {
minNode := (*Node)(nil)
minDistance := math.MaxInt64
for node, distance := range distanceMap { // O(N)
if _, ok := touchedNodes[node]; !ok && distance < minDistance {
minNode = node
minDistance = distance
}
}
return minNode
}
func TestDijkstra1(t *testing.T) {
matrix := [][]int{
{1, 1, 2},
{2, 2, 3},
{2, 3, 4},
{7, 1, 3},
{6, 1, 4},
{4, 4, 5},
{23, 3, 5},
{170, 2, 5},
//{1,2,3},
//{2,2,5},
//{3,5,7},
//{5,5,8},
}
g := CreateGraph(matrix)
fmt.Println("起点:", g.Nodes[1].Value, "开始")
for k, v := range dijkstra1(g.Nodes[1]) {
fmt.Println("到", k.Value, "的距离", v)
}
}
//引入小根堆 优化复杂度
type NodeRecord struct {
node *Node
distance int
}
func NewNodeRecord(node *Node, distance int) *NodeRecord {
return &NodeRecord{
node: node,
distance: distance,
}
}
type NodeHeap struct {
nodes []*Node //实际的堆结构
heapIndexMap map[*Node]int //key:某一个node,value:上面堆中的位置
distanceMap map[*Node]int //key:某一个节点,value:从源节点出发到该节点的目前最小距离
size int //堆上有多少个点
}
func NewNodeHeap(size int) *NodeHeap {
return &NodeHeap{
nodes: make([]*Node, size),
heapIndexMap: make(map[*Node]int),
distanceMap: make(map[*Node]int),
size: 0,
}
}
func (this *NodeHeap) IsEmpty() bool {
return this.size == 0
}
func (this *NodeHeap) InHeap(node *Node) bool { // 在堆上
return this.IsEntered(node) && this.heapIndexMap[node] != -1
}
func (this *NodeHeap) IsEntered(node *Node) bool { // 节点是否进来过
_, ok := this.heapIndexMap[node]
return ok
}
func (this *NodeHeap) Swap(index1, index2 int) {
this.heapIndexMap[this.nodes[index1]] = index2
this.heapIndexMap[this.nodes[index2]] = index1
tmp := this.nodes[index1]
this.nodes[index1] = this.nodes[index2]
this.nodes[index2] = tmp
}
func (this *NodeHeap) Pop() *NodeRecord {
nodeRecord := NewNodeRecord(this.nodes[0], this.distanceMap[this.nodes[0]])
this.Swap(0, this.size - 1) //拿最后一个位置的值顶0位置
this.heapIndexMap[this.nodes[this.size-1]] = -1
delete(this.distanceMap, this.nodes[this.size-1])
this.nodes[this.size-1] = nil
this.size--
this.heapify(0, this.size) // 下行
return nodeRecord
}
func (this *NodeHeap) InsertHeapify(index int) {
for this.distanceMap[this.nodes[index]] < this.distanceMap[this.nodes[(index-1)/2]] {
this.Swap(index, (index-1)/2)
index = (index - 1) / 2
}
}
// 有一个点叫node,现在发现了一个从源点出发到达node的距离为distance
// 判断要不要更新,如果需要的话,就更新,否则就忽略
func (this *NodeHeap) addOrUpdateOrIgnore(node *Node, distance int) {
if this.InHeap(node) { // 在堆上
this.distanceMap[node] = Min(this.distanceMap[node], distance)
this.InsertHeapify(this.heapIndexMap[node]) // 上行
}
if !this.IsEntered(node) { // 未进过
this.nodes[this.size] = node
this.heapIndexMap[node] = this.size
this.distanceMap[node] = distance
this.InsertHeapify(this.size) //上行
this.size++
}
// 既不在堆上,也没进来过 什么都不做
}
func (this *NodeHeap) heapify(index, size int) { // O(logN)
left := index*2 + 1
for left < size {
//smallest:= func() int {
// if left+1<this.size && this.distanceMap[this.nodes[left+1]]<this.distanceMap[this.nodes[left]] {
// return left+1
// }else{
// return left
// }
//}()
//smallest= func() int{
// if this.distanceMap[this.nodes[smallest]]<this.distanceMap[this.nodes[index]]{
// return smallest
// }else {
// return index
// }
//}()
smallest := map[bool]int{
left+1 < this.size && this.distanceMap[this.nodes[left+1]] < this.distanceMap[this.nodes[left]]: left + 1,
!(left+1 < this.size && this.distanceMap[this.nodes[left+1]] < this.distanceMap[this.nodes[left]]): left,
}[true]
smallest = map[bool]int{
this.distanceMap[this.nodes[smallest]] < this.distanceMap[this.nodes[index]]: smallest,
!(this.distanceMap[this.nodes[smallest]] < this.distanceMap[this.nodes[index]]): index,
}[true]
if smallest == index {
break
}
this.Swap(smallest, index)
index = smallest
left = index*2 + 1
}
}
func Dijkstra2(head *Node, size int) map[*Node]int {
nodeHeap := NewNodeHeap(size)
nodeHeap.addOrUpdateOrIgnore(head, 0) // 如果不存在,则添加,否则,更新,不更新(值未变),就忽略
result := make(map[*Node]int)
for !nodeHeap.IsEmpty() {
record := nodeHeap.Pop()
cur := record.node
distance := record.distance
for k, _ := range cur.Edges {
nodeHeap.addOrUpdateOrIgnore(cur.Edges[k].To, cur.Edges[k].Weight + distance)
}
result[cur] = distance
}
return result
}
// 改进后的dijkstra算法
// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
func TestDijkstra2(t *testing.T) {
matrix := [][]int{
{1, 1, 2},
{2, 2, 3},
{2, 3, 4},
{7, 1, 3},
{6, 1, 4},
{4, 4, 5},
{23, 3, 5},
{170, 2, 5},
//{1,2,3},
//{2,2,5},
//{3,5,7},
//{5,5,8},
}
g := CreateGraph(matrix)
fmt.Println("起点:", g.Nodes[1].Value, "开始")
for k, v := range Dijkstra2(g.Nodes[1], 8) {
fmt.Println("到", k.Value, "的距离", v)
}
}