Dijkstra算法和BFS(广度优先搜索)算法

2 阅读4分钟

Dijkstra算法和广度优先搜索(BFS)算法是图论中常用的两种算法,它们用于解决不同类型的问题。本文将详细介绍这两种算法的原理、执行步骤、区别及应用场景,并提供代码实现。

Dijkstra算法

原理

Dijkstra算法是一种用于求解带权图中单源最短路径问题的算法。它基于贪心思想,即每一步选择当前最短的路径,并逐步扩展到其他节点。Dijkstra算法的核心思想是:每次选择未访问的顶点中距离起点最近的一个,然后用这个顶点更新其他顶点的距离。

执行步骤
  1. 初始化:
  • 从起始顶点到自身的距离设为0,其他顶点的距离设为无穷大。
  • 将所有顶点标记为未访问。
  1. 选择最小距离顶点:
  • 从未访问的顶点中选择距离起始顶点最近的顶点,并标记为已访问。
  1. 更新距离:
  • 对于该顶点的每一个邻接顶点,如果通过该顶点的路径比之前记录的距离更短,则更新该邻接顶点的距离。
  1. 重复步骤2和3,直到所有顶点都被访问。
代码实现

Dijkstra算法的Kotlin实现:

import java.util.PriorityQueue

data class Edge(val target: String, val weight: Int)
data class Vertex(val name: String, val distance: Int) : Comparable<Vertex> {
    override fun compareTo(other: Vertex): Int = distance.compareTo(other.distance)
}

fun dijkstra(graph: Map<String, List<Edge>>, start: String): Map<String, Int> {
    //distances用于存储从起始节点到其他节点的最短距离,
    //初始时将所有距离设为Int.MAX_VALUE,起始节点的距离为0
    val distances = mutableMapOf<String, Int>().apply {
        graph.keys.forEach { put(it, Int.MAX_VALUE) }
        put(start, 0)
    }
    
    //使用PriorityQueue来选择当前距离最小的顶点。
    //PriorityQueue通过自定义Comparable接口的实现来进行优先级排序。
    val priorityQueue = PriorityQueue<Vertex>().apply { add(Vertex(start, 0)) }

    //当队列不为空时,取出队列中的顶点
    while (priorityQueue.isNotEmpty()) {
        val (currentVertex, currentDistance) = priorityQueue.poll()

        if (currentDistance > distances[currentVertex]!!) continue

        //更新其邻接点的距离
        //如果有更短的路径则更新distances
        for (edge in graph[currentVertex] ?: emptyList()) {
            val newDistance = currentDistance + edge.weight
            if (newDistance < distances[edge.target]!!) {
                distances[edge.target] = newDistance
                priorityQueue.add(Vertex(edge.target, newDistance))
            }
        }
    }

    return distances
}

BFS(广度优先搜索)算法

原理

广度优先搜索是一种遍历图的算法,用于找到从起始顶点到其他顶点的最短路径(在无权图中)。BFS使用队列数据结构来按层次遍历图的节点,即首先访问起始节点的所有邻接节点,然后依次访问这些邻接节点的邻接节点,依此类推。

执行步骤
  1. 初始化:
  • 创建一个空队列,将起始节点加入队列。
  • 标记起始节点为已访问。
  1. 出队列:
  • 从队列中取出一个节点,记为当前节点。
  1. 访问邻接节点:

对于当前节点的每一个邻接节点,如果它们尚未被访问,则将其加入队列并标记为已访问。 4. 重复步骤2和3,直到队列为空。

代码实现

BFS算法的Kotlin实现:

import java.util.ArrayDeque

fun bfs(graph: Map<String, List<String>>, start: String): List<String> {
    //visited集合用于跟踪已访问的节点,避免重复访问
    val visited = mutableSetOf<String>()
    //使用ArrayDeque来实现队列功能
    val queue = ArrayDeque<String>()
    //order列表记录访问节点的顺序
    val order = mutableListOf<String>()

    visited.add(start)
    queue.add(start)

    while (queue.isNotEmpty()) {
        val vertex = queue.removeFirst()
        order.add(vertex)

        for (neighbor in graph[vertex] ?: emptyList()) {
            if (neighbor !in visited) {
                visited.add(neighbor)
                queue.add(neighbor)
            }
        }
    }

    return order
}

区别及应用场景

区别

  1. 适用图类型:
  • Dijkstra算法适用于带权图,要求所有边的权值非负。
  • BFS适用于无权图或权值相同的图。
  1. 复杂度:
  • Dijkstra算法的时间复杂度为O(V^2)(使用矩阵表示图)或O(E + V log V)(使用优先队列),其中V是顶点数,E是边数。
  • BFS的时间复杂度为O(V + E),其中V是顶点数,E是边数。
  1. 结果:
  • Dijkstra算法返回起始节点到其他所有节点的最短路径。
  • BFS返回从起始节点按层次遍历的顺序。

应用场景

  1. Dijkstra算法:
  • 用于解决带权图中的最短路径问题,例如交通网络中最短路线规划、网络路由等。
  1. BFS算法:
  • 用于无权图中最短路径的寻找,如社交网络中找到最短关系链、迷宫最短路径等。

这两种算法在各自的应用领域都有广泛的应用。Dijkstra算法适用于有权图的情况,而BFS则是解决无权图问题的常用方法。