以下是一些常见的图的算法:
一、深度优先搜索(Depth-First Search,DFS)
-
基本思想:
- 从图中的某个顶点出发,沿着一条路径尽可能深地探索,直到无法继续前进时,回溯到上一个顶点,继续探索另一条未访问过的路径。
-
应用场景:
- 寻找图中的连通分量。
- 检测图中是否存在环。
- 求解迷宫问题等。
-
实现步骤:
- 初始化一个标记数组,用于记录每个顶点是否被访问过。
- 从某个顶点开始,递归地访问其未被访问过的邻接顶点。
- 当没有未被访问过的邻接顶点时,回溯到上一个顶点。
以下是深度优先搜索(DFS)、广度优先搜索(BFS)和迪杰斯特拉算法(Dijkstra's algorithm)分别用 C++、Java 和 Go 语言实现的示例代码。
一、深度优先搜索(DFS)
C++ 代码:
#include <iostream>
#include <vector>
using namespace std;
void dfs(vector<vector<int>>& graph, vector<bool>& visited, int vertex) {
visited[vertex] = true;
cout << vertex << " ";
for (int i = 0; i < graph[vertex].size(); i++) {
if (!visited[graph[vertex][i]]) {
dfs(graph, visited, graph[vertex][i]);
}
}
}
Java 代码:
import java.util.ArrayList;
import java.util.List;
public class DFS {
public static void dfs(List<List<Integer>> graph, boolean[] visited, int vertex) {
visited[vertex] = true;
System.out.print(vertex + " ");
for (int neighbor : graph.get(vertex)) {
if (!visited[neighbor]) {
dfs(graph, visited, neighbor);
}
}
}
}
Go 代码:
package main
import "fmt"
func dfs(graph [][]int, visited []bool, vertex int) {
visited[vertex] = true
fmt.Print(vertex, " ")
for _, neighbor := range graph[vertex] {
if!visited[neighbor] {
dfs(graph, visited, neighbor)
}
}
}
二、广度优先搜索(Breadth-First Search,BFS)
-
基本思想:
- 从图中的某个顶点出发,依次访问其所有的邻接顶点,然后再访问这些邻接顶点的邻接顶点,如此逐层向外扩展,直到访问完所有顶点。
-
应用场景:
- 计算图中两个顶点之间的最短路径。
- 构建图的广度优先树。
-
实现步骤:
- 初始化一个队列和一个标记数组。
- 将起始顶点加入队列,并标记为已访问。
- 从队列中取出一个顶点,访问其所有未被访问过的邻接顶点,并将这些邻接顶点加入队列,标记为已访问。
- 重复上述步骤,直到队列为空。
二、广度优先搜索(BFS)
C++ 代码:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
void bfs(vector<vector<int>>& graph, int start) {
vector<bool> visited(graph.size(), false);
queue<int> q;
q.push(start);
visited[start] = true;
while (!q.empty()) {
int vertex = q.front();
q.pop();
cout << vertex << " ";
for (int i = 0; i < graph[vertex].size(); i++) {
if (!visited[graph[vertex][i]]) {
visited[graph[vertex][i]] = true;
q.push(graph[vertex][i]);
}
}
}
}
Java 代码:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class BFS {
public static void bfs(List<List<Integer>> graph, int start) {
boolean[] visited = new boolean[graph.size()];
Queue<Integer> q = new LinkedList<>();
q.add(start);
visited[start] = true;
while (!q.isEmpty()) {
int vertex = q.poll();
System.out.print(vertex + " ");
for (int neighbor : graph.get(vertex)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.add(neighbor);
}
}
}
}
}
Go 代码:
package main
import "fmt"
func bfs(graph [][]int, start int) {
visited := make([]bool, len(graph))
q := []int{start}
visited[start] = true
for len(q) > 0 {
vertex := q[0]
q = q[1:]
fmt.Print(vertex, " ")
for _, neighbor := range graph[vertex] {
if!visited[neighbor] {
visited[neighbor] = true
q = append(q, neighbor)
}
}
}
}
三、迪杰斯特拉算法(Dijkstra's algorithm)
-
基本思想:
- 用于求解图中某一特定顶点到其他各顶点的最短路径。
- 算法维护一个距离源点距离最短的顶点集合,每次从集合外选择距离源点最近的顶点加入集合,并更新其他顶点到源点的距离。
-
应用场景:
- 网络路由中寻找最短路径。
- 地理信息系统中计算两点之间的最短距离。
-
实现步骤:
- 初始化一个距离数组,记录源点到每个顶点的距离,初始值为无穷大(除源点外)。
- 初始化一个标记数组,记录每个顶点是否已加入最短路径集合。
- 从距离源点最近的未标记顶点开始,更新其邻接顶点到源点的距离。
- 重复上述步骤,直到所有顶点都被加入最短路径集合。
C++ 代码:
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
typedef pair<int, int> pii;
vector<int> dijkstra(vector<vector<pii>>& graph, int start) {
int n = graph.size();
vector<int> dist(n, INT_MAX);
dist[start] = 0;
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({0, start});
while (!pq.empty()) {
int u = pq.top().second;
int d = pq.top().first;
pq.pop();
if (d > dist[u]) continue;
for (auto& edge : graph[u]) {
int v = edge.first;
int w = edge.second;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
return dist;
}
Java 代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
class Pair implements Comparable<Pair> {
int vertex;
int distance;
public Pair(int vertex, int distance) {
this.vertex = vertex;
this.distance = distance;
}
@Override
public int compareTo(Pair other) {
return Integer.compare(this.distance, other.distance);
}
}
public class Dijkstra {
public static int[] dijkstra(List<List<Pair>> graph, int start) {
int n = graph.size();
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
PriorityQueue<Pair> pq = new PriorityQueue<>();
pq.add(new Pair(start, 0));
while (!pq.isEmpty()) {
Pair pair = pq.poll();
int u = pair.vertex;
int d = pair.distance;
if (d > dist[u]) continue;
for (Pair edge : graph.get(u)) {
int v = edge.vertex;
int w = edge.distance;
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.add(new Pair(v, dist[v]));
}
}
}
return dist;
}
}
Go 代码:
package main
import (
"container/heap"
"fmt"
)
type Pair struct {
vertex int
distance int
}
type PQ []Pair
func (pq PQ) Len() int { return len(pq) }
func (pq PQ) Less(i, j int) bool {
return pq[i].distance < pq[j].distance
}
func (pq PQ) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}
func (pq *PQ) Push(x interface{}) {
*pq = append(*pq, x.(Pair))
}
func (pq *PQ) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func dijkstra(graph [][]Pair, start int) []int {
n := len(graph)
dist := make([]int, n)
for i := range dist {
dist[i] = 1<<63 - 1
}
dist[start] = 0
pq := &PQ{{start, 0}}
heap.Init(pq)
for pq.Len() > 0 {
pair := heap.Pop(pq).(Pair)
u := pair.vertex
d := pair.distance
if d > dist[u] {
continue
}
for _, edge := range graph[u] {
v := edge.vertex
w := edge.distance
if dist[u]+w < dist[v] {
dist[v] = dist[u] + w
heap.Push(pq, Pair{v, dist[v]})
}
}
}
return dist
四、弗洛伊德算法(Floyd-Warshall algorithm)
-
基本思想:
- 用于求解图中任意两个顶点之间的最短路径。
- 算法通过动态规划的思想,逐步更新任意两个顶点之间经过中间顶点的最短距离。
-
应用场景:
- 交通网络中计算任意两点之间的最短路径。
- 多源最短路径问题。
-
实现步骤:
- 初始化一个二维距离矩阵,记录图中任意两个顶点之间的直接距离。
- 依次以每个顶点作为中间顶点,更新任意两个顶点之间的距离。
- 重复上述步骤,直到所有顶点都作为过中间顶点。
二、弗洛伊德算法(Floyd-Warshall algorithm)
C++ 代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
void floydWarshall(vector<vector<int>>& graph) {
int n = graph.size();
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][k]!= INT_MAX && graph[k][j]!= INT_MAX && graph[i][k] + graph[k][j] < graph[i][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
}
Java 代码:
import java.util.Arrays;
public class FloydWarshall {
public static void floydWarshall(int[][] graph) {
int n = graph.length;
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][k]!= Integer.MAX_VALUE && graph[k][j]!= Integer.MAX_VALUE && graph[i][k] + graph[k][j] < graph[i][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
}
}
Go 代码:
package main
import "fmt"
func floydWarshall(graph [][]int) {
n := len(graph)
for k := 0; k < n; k++ {
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if graph[i][k]!= 1<<31-1 && graph[k][j]!= 1<<31-1 && graph[i][k]+graph[k][j] < graph[i][j] {
graph[i][j] = graph[i][k] + graph[k][j]
}
}
}
}
}
五、拓扑排序(Topological Sort)
-
基本思想:
- 对于一个有向无环图(DAG),将图中的顶点按照一定的顺序排列,使得对于任意一条有向边 (u, v),顶点 u 在排序中都位于顶点 v 之前。
-
应用场景:
- 任务调度问题,确保任务按照依赖关系执行。
- 课程安排问题,确定课程的学习顺序。
-
实现步骤:
- 统计图中每个顶点的入度。
- 将入度为 0 的顶点加入一个队列。
- 从队列中取出一个顶点,将其输出,并将其邻接顶点的入度减 1。如果某个邻接顶点的入度变为 0,则将其加入队列。
- 重复上述步骤,直到队列为空。如果此时图中还有未输出的顶点,则说明图中存在环,无法进行拓扑排序。
C++ 代码:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<int> topologicalSort(vector<vector<int>>& graph) {
int n = graph.size();
vector<int> inDegree(n, 0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < graph[i].size(); j++) {
inDegree[graph[i][j]]++;
}
}
queue<int> q;
for (int i = 0; i < n; i++) {
if (inDegree[i] == 0) {
q.push(i);
}
}
vector<int> result;
while (!q.empty()) {
int vertex = q.front();
q.pop();
result.push_back(vertex);
for (int i = 0; i < graph[vertex].size(); i++) {
inDegree[graph[vertex][i]]--;
if (inDegree[graph[vertex][i]] == 0) {
q.push(graph[vertex][i]);
}
}
}
return result;
}
Java 代码:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class TopologicalSort {
public static List<Integer> topologicalSort(List<List<Integer>> graph) {
int n = graph.size();
int[] inDegree = new int[n];
for (int i = 0; i < n; i++) {
for (int neighbor : graph.get(i)) {
inDegree[neighbor]++;
}
}
Queue<Integer> q = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (inDegree[i] == 0) {
q.add(i);
}
}
List<Integer> result = new ArrayList<>();
while (!q.isEmpty()) {
int vertex = q.poll();
result.add(vertex);
for (int neighbor : graph.get(vertex)) {
inDegree[neighbor]--;
if (inDegree[neighbor] == 0) {
q.add(neighbor);
}
}
}
return result;
}
}
Go 代码:
package main
import "fmt"
func topologicalSort(graph [][]int) []int {
n := len(graph)
inDegree := make([]int, n)
for i := range graph {
for _, v := range graph[i] {
inDegree[v]++
}
}
q := []int{}
for i := range inDegree {
if inDegree[i] == 0 {
q = append(q, i)
}
}
result := []int{}
for len(q) > 0 {
vertex := q[0]
q = q[1:]
result = append(result, vertex)
for _, neighbor := range graph[vertex] {
inDegree[neighbor]--
if inDegree[neighbor] == 0 {
q = append(q, neighbor)
}
}
}
return result
}