图搜索算法是用于在图数据结构中查找特定节点或路径
的算法。常见的图搜索算法包括如下:
1. 深度优先搜索(DFS)
从起始节点开始,沿着一条路径一直向下探索,直到无法再继续为止,然后回溯到上一个节点继续探索。DFS通常使用栈或递归实现。
代码如下:
import java.util.*;
public class DFSSearch {
private int V; // 节点数
private LinkedList<Integer> adj[]; // 邻接表
DFSSearch(int v) {
V = v;
adj = new LinkedList[v];
for (int i = 0; i < v; ++i)
adj[i] = new LinkedList();
}
void addEdge(int v, int w) {
adj[v].add(w);
}
void DFSUtil(int v, boolean visited[]) {
visited[v] = true;
System.out.print(v + " ");
Iterator<Integer> i = adj[v].listIterator();
while (i.hasNext()) {
int n = i.next();
if (!visited[n])
DFSUtil(n, visited);
}
}
void DFS(int v) {
boolean visited[] = new boolean[V];
DFSUtil(v, visited);
}
public static void main(String args[]) {
DFSSearch g = new DFSSearch(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
System.out.println("深度优先搜索遍历结果(从顶点2开始):");
g.DFS(2);
}
}
2. 广度优先搜索(BFS)
从起始节点开始,首先探索所有相邻节点,然后逐层向外扩展。BFS通常使用队列实现。
代码如下:
import java.util.*;
public class BFSSearch {
private int V; // 节点数
private LinkedList<Integer> adj[]; // 邻接表
BFSSearch(int v) {
V = v;
adj = new LinkedList[v];
for (int i = 0; i < v; ++i)
adj[i] = new LinkedList();
}
void addEdge(int v, int w) {
adj[v].add(w);
}
void BFS(int start) {
boolean visited[] = new boolean[V];
LinkedList<Integer> queue = new LinkedList<Integer>();
visited[start] = true;
queue.add(start);
while (queue.size() != 0) {
start = queue.poll();
System.out.print(start + " ");
Iterator<Integer> i = adj[start].listIterator();
while (i.hasNext()) {
int n = i.next();
if (!visited[n]) {
visited[n] = true;
queue.add(n);
}
}
}
}
public static void main(String args[]) {
BFSSearch g = new BFSSearch(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
System.out.println("广度优先搜索遍历结果(从顶点2开始):");
g.BFS(2);
}
}
3. Dijkstra算法
用于在带权重图中找到最短路径的算法。它通过维护一个距离数组来确定最短路径。
代码如下:
import java.util.*;
public class DijkstraAlgorithm {
private static final int V = 6; // 节点数
private static int graph[][] = new int[][] { { 0, 4, 1, 0, 0, 0 },
{ 4, 0, 2, 5, 0, 0 },
{ 1, 2, 0, 4, 1, 0 },
{ 0, 5, 4, 0, 3, 0 },
{ 0, 0, 1, 3, 0, 2 },
{ 0, 0, 0, 0, 2, 0 } };
int minDistance(int dist[], Boolean sptSet[]) {
int min = Integer.MAX_VALUE, minIndex = -1;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && dist[v] <= min) {
min = dist[v];
minIndex = v;
}
}
return minIndex;
}
void printSolution(int dist[]) {
System.out.println("顶点到源点的最短距离:");
for (int i = 0; i < V; i++) {
System.out.println(i + " ---> " + dist[i]);
}
}
void dijkstra(int src) {
int dist[] = new int[V];
Boolean sptSet[] = new Boolean[V];
for (int i = 0; i < V; i++) {
dist[i] = Integer.MAX_VALUE;
sptSet[i] = false;
}
dist[src] = 0;
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet);
sptSet[u] = true;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] != 0 && dist[u] != Integer.MAX_VALUE && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
printSolution(dist);
}
public static void main(String args[]) {
DijkstraAlgorithm dijkstra = new DijkstraAlgorithm();
dijkstra.dijkstra(0);
}
}
上面代码中,使用一个6x6的邻接矩阵来表示图的边权重。算法通过不断更新最短路径来计算源节点到其他节点的最短距离。最终输出各节点到源节点的最短距离。
4. A*算法
一种启发式搜索算法,结合了最佳优先搜索和Dijkstra算法的特点,用于在图中找到最短路径。
代码如下:
import java.util.PriorityQueue;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
class Node {
int x, y;
int f, g, h;
Node(int x, int y) {
this.x = x;
this.y = y;
}
}
class AStarComparator implements Comparator<Node> {
public int compare(Node a, Node b) {
return a.f - b.f;
}
}
public class AStarAlgorithm {
private static final int[][] grid = {
{0, 0, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 0, 0}
};
private static final int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
private static boolean isValid(int x, int y) {
return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length;
}
private static int heuristic(int x, int y, int targetX, int targetY) {
return Math.abs(x - targetX) + Math.abs(y - targetY);
}
public static void aStarSearch(int startX, int startY, int targetX, int targetY) {
PriorityQueue<Node> openList = new PriorityQueue<>(new AStarComparator());
Set<Node> closedList = new HashSet<>();
Node startNode = new Node(startX, startY);
startNode.g = 0;
startNode.h = heuristic(startX, startY, targetX, targetY);
startNode.f = startNode.g + startNode.h;
openList.offer(startNode);
while (!openList.isEmpty()) {
Node current = openList.poll();
if (current.x == targetX && current.y == targetY) {
System.out.println("路径找到!");
return;
}
closedList.add(current);
for (int[] dir : dirs) {
int newX = current.x + dir[0];
int newY = current.y + dir[1];
if (isValid(newX, newY) && grid[newX][newY] == 0) {
Node neighbor = new Node(newX, newY);
int newG = current.g + 1;
int newH = heuristic(newX, newY, targetX, targetY);
int newF = newG + newH;
neighbor.g = newG;
neighbor.h = newH;
neighbor.f = newF;
if (closedList.contains(neighbor)) {
continue;
}
if (!openList.contains(neighbor) || newF < neighbor.f) {
openList.offer(neighbor);
}
}
}
}
System.out.println("路径未找到!");
}
public static void main(String[] args) {
aStarSearch(0, 0, 4, 4);
}
}
上面代码中,用于在给定的二维网格中寻找从起点到终点的最短路径。算法通过估算当前节点到目标节点的距离(启发式函数)来选择下一个节点进行探索。
5. Bellman-Ford算法
用于解决带负权边的图中的单源最短路径问题。
代码如下:
import java.util.*;
class Edge {
int source, destination, weight;
Edge() {
source = 0;
destination = 0;
weight = 0;
}
}
class BellmanFordAlgorithm {
private int V, E;
private Edge edge[];
private static final int INF = Integer.MAX_VALUE;
BellmanFordAlgorithm(int v, int e) {
V = v;
E = e;
edge = new Edge[e];
for (int i = 0; i < e; i++) {
edge[i] = new Edge();
}
}
void bellmanFord(int source) {
int dist[] = new int[V];
Arrays.fill(dist, INF);
dist[source] = 0;
for (int i = 1; i < V; i++) {
for (int j = 0; j < E; j++) {
int u = edge[j].source;
int v = edge[j].destination;
int weight = edge[j].weight;
if (dist[u] != INF && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
for (int i = 0; i < E; i++) {
int u = edge[i].source;
int v = edge[i].destination;
int weight = edge[i].weight;
if (dist[u] != INF && dist[u] + weight < dist[v]) {
System.out.println("图中存在负权回路");
return;
}
}
printSolution(dist);
}
void printSolution(int dist[]) {
System.out.println("顶点到源点的最短距离:");
for (int i = 0; i < V; i++) {
System.out.println(i + " ---> " + dist[i]);
}
}
public static void main(String[] args) {
int V = 5; // 节点数
int E = 8; // 边数
BellmanFordAlgorithm graph = new BellmanFordAlgorithm(V, E);
graph.edge[0].source = 0;
graph.edge[0].destination = 1;
graph.edge[0].weight = -1;
graph.edge[1].source = 0;
graph.edge[1].destination = 2;
graph.edge[1].weight = 4;
graph.edge[2].source = 1;
graph.edge[2].destination = 2;
graph.edge[2].weight = 3;
graph.edge[3].source = 1;
graph.edge[3].destination = 3;
graph.edge[3].weight = 2;
graph.edge[4].source = 1;
graph.edge[4].destination = 4;
graph.edge[4].weight = 2;
graph.edge[5].source = 3;
graph.edge[5].destination = 2;
graph.edge[5].weight = 5;
graph.edge[6].source = 3;
graph.edge[6].destination = 1;
graph.edge[6].weight = 1;
graph.edge[7].source = 4;
graph.edge[7].destination = 3;
graph.edge[7].weight = -3;
graph.bellmanFord(0);
}
}
上面代码中,用于解决带负权边的图中的单源最短路径问题。算法通过在每一轮松弛所有边的权重来计算最短路径。如果在V-1轮松弛后仍然存在边的权重可以被减小,则图中存在负权回路。
6. Prim算法
用于在加权图中找到最小生成树的算法。
代码如下:
import java.util.*;
public class PrimAlgorithm {
private static final int V = 5; // 节点数
private static final int INF = Integer.MAX_VALUE;
int minKey(int key[], boolean mstSet[]) {
int min = INF, minIndex = -1;
for (int v = 0; v < V; v++) {
if (!mstSet[v] && key[v] < min) {
min = key[v];
minIndex = v;
}
}
return minIndex;
}
void printMST(int parent[], int graph[][]) {
System.out.println("最小生成树的边:");
for (int i = 1; i < V; i++) {
System.out.println(parent[i] + " - " + i + " 权重:" + graph[i][parent[i]]);
}
}
void primMST(int graph[][]) {
int parent[] = new int[V];
int key[] = new int[V];
boolean mstSet[] = new boolean[V];
for (int i = 0; i < V; i++) {
key[i] = INF;
mstSet[i] = false;
}
key[0] = 0;
parent[0] = -1;
for (int count = 0; count < V - 1; count++) {
int u = minKey(key, mstSet);
mstSet[u] = true;
for (int v = 0; v < V; v++) {
if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}
printMST(parent, graph);
}
public static void main(String[] args) {
PrimAlgorithm prim = new PrimAlgorithm();
int graph[][] = { { 0, 2, 0, 6, 0 },
{ 2, 0, 3, 8, 5 },
{ 0, 3, 0, 0, 7 },
{ 6, 8, 0, 0, 9 },
{ 0, 5, 7, 9, 0 } };
prim.primMST(graph);
}
}
7. Kruskal算法
另一种用于在加权图中找到最小生成树的算法,基于边的权重进行排序。
代码如下:
import java.util.*;
class Edge implements Comparable<Edge> {
int source, destination, weight;
public int compareTo(Edge compareEdge) {
return this.weight - compareEdge.weight;
}
}
public class KruskalAlgorithm {
private static final int V = 5; // 节点数
private static final int E = 7; // 边数
private Edge edge[];
KruskalAlgorithm() {
edge = new Edge[E];
for (int i = 0; i < E; i++) {
edge[i] = new Edge();
}
}
int find(int parent[], int i) {
if (parent[i] == i) {
return i;
}
return find(parent, parent[i]);
}
void union(int parent[], int rank[], int x, int y) {
int xRoot = find(parent, x);
int yRoot = find(parent, y);
if (rank[xRoot] < rank[yRoot]) {
parent[xRoot] = yRoot;
} else if (rank[xRoot] > rank[yRoot]) {
parent[yRoot] = xRoot;
} else {
parent[yRoot] = xRoot;
rank[xRoot]++;
}
}
void kruskalMST() {
Edge result[] = new Edge[V - 1];
int e = 0;
int i = 0;
Arrays.sort(edge);
int parent[] = new int[V];
int rank[] = new int[V];
for (int v = 0; v < V; v++) {
parent[v] = v;
rank[v] = 0;
}
while (e < V - 1) {
Edge nextEdge = edge[i++];
int x = find(parent, nextEdge.source);
int y = find(parent, nextEdge.destination);
if (x != y) {
result[e++] = nextEdge;
union(parent, rank, x, y);
}
}
System.out.println("最小生成树的边:");
for (i = 0; i < V - 1; i++) {
System.out.println(result[i].source + " - " + result[i].destination + " 权重:" + result[i].weight);
}
}
public static void main(String[] args) {
KruskalAlgorithm kruskal = new KruskalAlgorithm();
kruskal.edge[0].source = 0;
kruskal.edge[0].destination = 1;
kruskal.edge[0].weight = 2;
kruskal.edge[1].source = 0;
kruskal.edge[1].destination = 3;
kruskal.edge[1].weight = 6;
kruskal.edge[2].source = 1;
kruskal.edge[2].destination = 3;
kruskal.edge[2].weight = 3;
kruskal.edge[3].source = 1;
kruskal.edge[3].destination = 2;
kruskal.edge[3].weight = 5;
kruskal.edge[4].source = 1;
kruskal.edge[4].destination = 4;
kruskal.edge[4].weight = 2;
kruskal.edge[5].source = 2;
kruskal.edge[5].destination = 4;
kruskal.edge[5].weight = 7;
kruskal.edge[6].source = 3;
kruskal.edge[6].destination = 4;
kruskal.edge[6].weight = 9;
kruskal.kruskalMST();
}
}