【数据结构】从BFS到迪杰斯特拉

本文从BFS遍历的角度出发,探讨如何利用广度优先搜索思想解决有权图的最短路径问题。通过引入优先队列,详细阐述了迪杰斯特拉算法的原理和实现,适用于无负权图,无论是有向还是无向,有环或无环。

从BFS到迪杰斯特拉

回顾图的BFS遍历

我们知道,图的BFS遍历有一个“附加功能”,就是可以求得无权图两点间的最短距离。
一个无权有向图
对图进行BFS遍历的过程很简单:
借助一个队列,首先起始节点入队,当队列不空的时候,弹出队首元素,再将其后继节点入队,不断重复这个过程。

下面,以0为起始点对上图进行广度优先遍历:

操作 队列
第一步:0入队 0
第二步:访问0,1、2、3入队 1 2 3
第三步:访问1,4入队 2 3 4
第四步:访问2,5入队 3 4 5
第五步:访问3 4 5
第六步:访问4 5
第七步:访问5 null

分析广度优先遍历能够找到无权图最短路的原因

一个假设:假设无权图也是有权图,权值相等且均为1
那么,我们可以把刚才那个有向无权图转化成如下有向有权图:
规定无权图权值均为1
我们来观察一下广度优先遍历时队列中的元素
0 1 2 3 4 5
他们距离起始点0的距离是:0 1 1 1 2 3
可以发现,这是一个递增的序列,先入队的距离起始点更近,后入的距离起始点更远。

由广度优先遍历转向迪杰斯特拉算法

显然,刚才发现的规律是由于无权图“众边平等”的特点造成的;更加显然的是,有权图不具备这个特点。
如下有权图中,距0的距离为:0 1 5 4 3 6
有权图

那么,能不能通过某种方式,借助广度优先遍历的思想去寻找有权图的最短路呢?(没有我就不问了(ε=ε=ε=┏(゜ロ゜;)┛))

想象一下,如果有一个附带排序功能的队列,每次节点入队时,都将最小的节点置于队首,那这样不就模拟出广度优先遍历时,“距离短的先入队,距离长的后入队”的规律了吗?

这种队列被称作优先队列

下面给出优先队列类的实现:
(C++ STL和其他语言的库里有封装好的优先队列,可以自己写一写STL里面的东西,挺有意思的)

template<class T>class PriorityQueue//优先队列
{
   
   
private:
    int size = 1;//队列的容量,初始值为1
    int length = 0;//队列已经占用的长度
    int head = 0;//队首下标
    int tail = 0;//队尾下标
    T** queue = new T * [size];
public:
    PriorityQueue()//构造方法
    {
   
   
        memset(queue, 0, size * sizeof(T*));
    }
    bool empty()//队列是否为空
    {
   
   
        return head == tail;
    }
    void push(const T& a)//入队
    {
   
   
        for (int i = head; i <= tail; i++)//将元素插入对应位置
        {
   
   
            if (i == tail ||*queue[i] < a) {
   
   
                for (int j = tail; j >= i; j--) {
   
   
                    queue[j] = queue[j - 1];
                }
                queue[i] = new T(a);
                tail++;
                length++;
                break;
            }
        }
        if (length == size) //如果队列大小不够就自动扩容,每次扩容大小为原来的二倍
        {
   
   
            T** temp = queue;
            size <<= 1;
            queue = new T * [size];
            memmove(queue, temp + head, (tail - head) * sizeof(T*));
            delete[] temp;
            length = tail - head;
            tail -=</
### 迪杰斯特拉算法 迪杰斯特拉算法是一种用于计算加权图中单源最短路径的经典贪心算法[^1]。该算法通过逐步扩展已找到最短路径的顶点集合来工作,每次从未处理的节点中选取距离最小的一个作为当前节点,并更新其邻居的距离。 #### Python 实现示例 ```python import heapq def dijkstra(graph, start): queue = [] distances = {node: float('inf') for node in graph} distances[start] = 0 heapq.heappush(queue, (distances[start], start)) while queue: current_distance, current_node = heapq.heappop(queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(queue, (distance, neighbor)) return distances ``` 此版本利用优先队列优化了原始O(n²)的时间复杂度至接近线性的性能,在稀疏图上表现更佳[^2]。 --- ### 堆栈(Stack) 堆栈是一个遵循后进先出(LIFO)原则的数据结构。它支持两种基本操作:`push()` 和 `pop()`. 当元素被压入(stack push),它们会被放置在顶部;当执行弹出(stack pop), 则移除并返回位于顶端的那个元素。 Python中的列表可以很方便地模拟这种行为: ```python stack = [] # Push operation stack.append(1) stack.append(2) print(f"After pushes: {stack}") # Pop operation last_item = stack.pop() print(f"Popped item: {last_item}") print(f"Stack after popping: {stack}") ``` --- ### 队列(Queue) 与堆栈相反的是队列,这是一种先进先出(FIFO)的数据结构。新加入的成员总是排在队伍最后面等待服务,而最先到达者则会最早离开。 同样可以用Python内置模块collections.deque高效实现: ```python from collections import deque queue = deque() # Enqueue operations queue.appendleft('a') queue.appendleft('b') # Dequeue operation first_in_line = queue.pop() print(f"First out was '{first_in_line}', remaining elements are now {list(queue)}") ``` --- ### 广度优先搜索(BFS) 广度优先搜索是从根结点开始逐层遍历树形或其他图形结构的一种方法。对于每一个访问到的新位置都会记录下来以便后续探索相邻未访问过的节点直到整个空间都被覆盖为止。 下面给出一个简单的无向图BFS例子: ```python graph = { 'A': ['B', 'C'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F'], 'D': ['B'], 'E': ['B', 'F'], 'F': ['C', 'E'] } visited = set() # Set to keep track of visited nodes. def bfs(node): queue = [node] while queue: vertex = queue.pop(0) if vertex not in visited: print(vertex, end=" ") visited.add(vertex) neighbors = graph[vertex] for neighbour in neighbors: if neighbour not in visited: queue.append(neighbour) bfs('A') # Output should be A B C D E F assuming alphabetical order when multiple choices exist. ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值