图——Dijkstra

原版及其加入定制的堆进行改进版本

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)
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值