狄克斯特拉算法

狄克斯特拉算法(Dijkstra’s algorithm)是一种用于在带权图中找到从单一源点到所有其他顶点的最短路径的算法。它适用于处理带有非负权值的图。

下面将详细解释算法的工作原理、时间复杂度以及如何通过优化数据结构来改进其性能。

狄克斯特拉算法的工作原理

  1. 初始化:算法开始时,将所有顶点标记为未访问。源点到自身的距离设为0,其他所有顶点到源点的距离设为无穷大(表示尚未找到路径)。

  2. 选择最小距离顶点:在未访问的顶点中,选择一个具有最小距离的顶点,称为当前顶点。

  3. 松弛操作:对于当前顶点的每一个邻接顶点,执行松弛操作。如果通过当前顶点到邻接顶点的路径比已知的路径更短,则更新该邻接顶点的距离。

  4. 标记访问:将当前顶点标记为已访问,然后从未访问顶点集合中移除。

  5. 重复迭代:重复步骤2到4,直到所有顶点都被访问或者找到目标顶点。

时间复杂度分析

  • 原始算法:在每次迭代中,算法需要从所有未访问的顶点中选择一个最小距离的顶点,这需要 O(n) 的时间。由于有 n 个顶点,因此总的时间复杂度是 O(n^2)。

  • 优化后的算法:通过使用优先队列(如二叉堆或斐波那契堆),算法可以在 O(log n) 的时间内找到最小距离的顶点。更新邻接顶点的距离并将其重新插入优先队列也需要 O(log n) 的时间。因此,对于每个顶点的松弛操作,总时间复杂度是 O(n log n)。由于有 m 条边,每个边可能需要进行一次松弛操作,所以边的松弛操作总时间复杂度是 O(m log n)。综合考虑,优化后的算法时间复杂度是 O((n + m) log n)。

数据结构优化

  • 优先队列:使用优先队列可以快速访问最小元素,并且可以在对数时间内插入和删除元素。这是优化狄克斯特拉算法的关键。

  • 二叉堆:一种常见的实现优先队列的数据结构,但在最坏情况下,插入和删除操作的时间复杂度为 O(log n)。

  • 斐波那契堆:另一种实现优先队列的数据结构,它在平均情况下可以提供更好的性能,特别是在删除最小元素时,平均时间复杂度接近 O(1)。

实际应用中的注意事项

  • 图的表示:图可以以邻接矩阵或邻接表的形式表示。邻接矩阵适用于稠密图,而邻接表适用于稀疏图。

  • 负权边:狄克斯特拉算法不适用于包含负权边的图。对于这种情况,可以使用贝尔曼-福特算法。

  • 算法变体:存在狄克斯特拉算法的变体,如 A* 搜索算法,它使用启发式信息来进一步优化搜索过程。

代码实现

使用数组实现的Dijkstra算法

package main

import "math"

type Graph struct {
	vertices int     // 图中顶点的数量
	edges    [][]int // 存储边权重的邻接表
}

// NewGraph 创建一个新的图实例
func NewGraph(v int) *Graph {
	return &Graph{
		vertices: v,
		edges:    make([][]int, v),
	}
}

// DijkstraArray 使用数组实现的Dijkstra算法来计算从源顶点到所有其他顶点的最短路径
func (g *Graph) DijkstraArray(src int) []int {
	dist := make([]int, g.vertices) // 存储从源顶点到每个顶点的最短距离
	visited := make([]bool, g.vertices) // 记录顶点是否已经被访问过

	// 初始化距离数组,所有顶点距离设为无穷大
	for i := range dist {
		dist[i] = math.MaxInt32
	}
	dist[src] = 0 // 源顶点到自身的距离设为0

	// 找到最短距离的顶点进行迭代更新
	for count := 0; count < g.vertices-1; count++ {
		u := g.minDistance(dist, visited) // 从未访问过的顶点中找到距离最小的顶点
		visited[u] = true // 标记顶点u为已访问

		// 更新顶点u的邻接顶点的最短距离
		for v := 0; v < g.vertices; v++ {
			if !visited[v] && g.edges[u][v] != 0 && dist[u]+g.edges[u][v] < dist[v] {
				dist[v] = dist[u] + g.edges[u][v]
			}
		}
	}

	return dist // 返回从源顶点到所有其他顶点的最短距离数组
}

// minDistance 辅助函数,找到当前距离数组中距离最小的顶点
func (g *Graph) minDistance(dist []int, visited []bool) int {
	min := math.MaxInt32
	minIndex := -1

	for v := range dist {
		if !visited[v] && dist[v] <= min {
			min = dist[v]
			minIndex = v
		}
	}

	return minIndex
}

func main() {
	g := NewGraph(9)
	g.edges = [][]int{
		{0, 4, 0, 0, 0, 0, 0, 8, 0},
		{4, 0, 8, 0, 0, 0, 0, 11, 0},
		{0, 8, 0, 7, 0, 4, 0, 0, 2},
		{0, 0, 7, 0, 9, 14, 0, 0, 0},
		{0, 0, 0, 9, 0, 10, 0, 0, 0},
		{0, 0, 4, 14, 10, 0, 2, 0, 0},
		{0, 0, 0, 0, 0, 2, 0, 1, 6},
		{8, 11, 0, 0, 0, 0, 1, 0, 7},
		{0, 0, 2, 0, 0, 0, 6, 7, 0},
	}
	distances := g.DijkstraArray(0)
	println("Shortest distances from source vertex 0:")
	for i, dist := range distances {
		println(i, ":", dist)
	}
}

使用最小堆实现优先级队列的Dijkstra算法

package main  
  
import (  
	"container/heap"  
	"fmt"  
	"math"  
)  
  
// Item 是优先级队列中的元素,包含顶点和其距离  
type Item struct {  
	vertex int // 顶点  
	dist   int // 顶点的当前距离  
	index  int // 顶点在dist数组中的索引(可选,用于更新)  
}  
  
// PriorityQueue 是优先级队列,基于Item的dist字段排序  
type PriorityQueue []*Item  
  
func (pq PriorityQueue) Len() int { return len(pq) }  
  
func (pq PriorityQueue) Less(i, j int) bool {  
	return pq[i].dist < pq[j].dist  
}  
  
func (pq PriorityQueue) Swap(i, j int) {  
	pq[i], pq[j] = pq[j], pq[i]  
	pq[i].index = i  
	pq[j].index = j  
}  
  
func (pq *PriorityQueue) Push(x interface{}) {  
	n := len(*pq)  
	item := x.(*Item)  
	item.index = n  
	*pq = append(*pq, item)  
}  
  
func (pq *PriorityQueue) Pop() interface{} {  
	old := *pq  
	n := len(old)  
	item := old[n-1]  
	item.index = -1 // for safety  
	*pq = old[0 : n-1]  
	return item  
}  
  
func (pq *PriorityQueue) update(item *Item, dist int) {  
	item.dist = dist  
	heap.Fix(pq, item.index)  
}  
  
// Edge 表示图中的一条边  
type Edge struct {  
	to     int // 目标顶点  
	weight int // 边的权重  
}  
  
// Graph 表示整个图结构  
type Graph struct {  
	vertices int       // 图中顶点的数量  
	edges    [][]*Edge // 存储边的邻接表,使用指针避免复制  
}  
  
// DijkstraMinHeap 使用最小堆优先级队列的Dijkstra算法  
func (g *Graph) DijkstraMinHeap(src int) []int {  
	dist := make([]int, g.vertices)  
	for i := range dist {  
		dist[i] = math.MaxInt32  
	}  
	dist[src] = 0  
  
	pq := make(PriorityQueue, 0)  
	heap.Init(&pq)  
	heap.Push(&pq, &Item{vertex: src, dist: 0})  
  
	for pq.Len() > 0 {  
		item := heap.Pop(&pq).(*Item)  
		u := item.vertex  
  
		for _, edge := range g.edges[u] {  
			v := edge.to  
			alt := dist[u] + edge.weight  
			if alt < dist[v] {  
				dist[v] = alt  
				heap.Push(&pq, &Item{vertex: v, dist: alt})  
			}  
		}  
	}  
  
	return dist  
}  
  
func main() {  
	// 初始化图的边的连接关系  
	graph := Graph{  
		vertices: 9,  
		edges: [][]*Edge{  
			{{to: 1, weight: 4}, {to: 7, weight: 8}},  
			{{to: 0, weight: 4}, {to: 2, weight: 8}, {to: 7, weight: 11}},  
			{{to: 1, weight: 8}, {to: 3, weight: 7}, {to: 5, weight: 4}, {to: 8, weight: 2}},  
			{{to: 2, weight: 7}, {to: 4, weight: 9}, {to: 5, weight: 14}},  
			{{to: 3, weight: 9}, {to: 5, weight: 10}},  
			{{to: 2, weight: 4}, {to: 3, weight: 14}, {to: 4, weight: 10}, {to: 6, weight: 2}}, 
            {{to: 5, weight: 2}, {to: 7, weight: 1}, {to: 8, weight: 6}},  
			{{to: 0, weight: 8}, {to: 1, weight: 11}, {to: 6, weight: 1}, {to: 8, weight: 7}},  
			{{to: 2, weight: 2}, {to: 6, weight: 6}, {to: 7, weight: 7}}, 
		},  
	}  
  
	distances := graph.DijkstraMinHeap(0)
	fmt.Println("Shortest distances from source vertex 0:")
	for i, dist := range distances {
		fmt.Printf("%d: %d\n", i, dist)
	}
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
狄克斯特拉算法(Dijkstra's algorithm)是一种用于解决单源最短路径问题的经典算法。它可以找到从一个顶点到其他所有顶点的最短路径。 以下是狄克斯特拉算法的Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int INF = Integer.MAX_VALUE; public static void dijkstra(int[][] graph, int start) { int n = graph.length; int[] dist = new int[n]; boolean[] visited = new boolean[n]; Arrays.fill(dist, INF); dist[start] = 0; for (int i = 0; i < n - 1; i++) { int minDist = INF; int minIndex = -1; for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; minIndex = j; } } visited[minIndex] = true; for (int j = 0; j < n; j++) { if (!visited[j] && graph[minIndex][j] != 0 && dist[minIndex] != INF && dist[minIndex] + graph[minIndex][j] < dist[j]) { dist[j] = dist[minIndex] + graph[minIndex][j]; } } } // 打印最短路径 System.out.println("顶点\t最短距离"); for (int i = 0; i < n; i++) { System.out.println(i + "\t" + dist[i]); } } public static void main(String[] args) { int[][] graph = { {0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0} }; dijkstra(graph, 0); } } ``` 这段代码实现了狄克斯特拉算法,通过传入一个邻接矩阵表示的图和起始顶点,计算出从起始顶点到其他所有顶点的最短路径,并打印出最短距离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值