图的深度、广度优先、最小生成树、最短路径算法(Java版本)

36 篇文章 1 订阅

介绍

本文提供了图的邻接表、邻接矩阵的Java实现,包括深度优先算法、广度优先算法、prim算法和地杰斯特拉算法。

原理分析

输入图:
在这里插入图片描述
深度优先遍历:
在这里插入图片描述

在这里插入图片描述
广度优先遍历:
在这里插入图片描述
在这里插入图片描述
注意:邻接矩阵和邻接表同样的输入遍历的结果可能会不完全一样,原因邻接表的构造并非将小编号的结点连接在靠前的位置,因此导致遍历结果和邻接矩阵不相同,但其在逻辑上仍然满足图的深度优先遍历。另外,最小生成树和最短路径当图中存在两条相同长度的路径时,其结果也不一定唯一。

算法描述

包结构:
在这里插入图片描述

import java.util.List;

public interface Graph {
	public void dfsTraverse();			//深度优先遍历
	public void bfsTraverse();			//广度优先遍历
	public List<String> prim();			//prim算法
	public List<String> dijistra(int v1,int v2);//地杰斯特拉算法
}
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
 * 使用邻接表
 */
public class AGraph implements Graph{
	private static Scanner scanner = new Scanner(System.in);
	private class ArcNode{
		public int adjvex;	//边所指向的结点
		public int w;	//边的权值
		ArcNode next;
	}
	
	private class VNode{
		ArcNode firstArc;
		String data;
	}
	
	int n,e;	//顶点数,边数
	VNode vnodes[];
	
	public static AGraph createGraph(){
		AGraph g = new AGraph();
		System.out.println("输入定点数和边数");
		g.n = scanner.nextInt();
		g.e = scanner.nextInt();
		g.vnodes = new VNode[g.n];
		for(int i = 0;i<g.n;i++){
			g.vnodes[i] = g.new VNode();
		}
		System.out.println("输入定点信息");
		for (int i = 0; i < g.n; i++) {
			g.vnodes[i].data = scanner.next();
		}
		System.out.println();	
		for(int i = 0;i<g.n;i++){
			System.out.println("顶点" + g.vnodes[i].data + "的编号为" + i);
		}
		for (int i = 0; i < g.e; i++) {
			System.out.println("输入边的两个定点编号和它的权值");
			ArcNode arcNode1 = g.new ArcNode();
			int x = scanner.nextInt();
			int y = scanner.nextInt();
			int w = scanner.nextInt();
			arcNode1.adjvex = y;
			arcNode1.w = w;
			arcNode1.next = g.vnodes[x].firstArc;
			g.vnodes[x].firstArc = arcNode1;
			
			ArcNode arcNode2 = g.new ArcNode();
			arcNode2.adjvex = x;
			arcNode2.w = w;
			arcNode2.next = g.vnodes[y].firstArc;
			g.vnodes[y].firstArc = arcNode2;
		}
		return g;
	}
	
	public void dfs(int v,boolean visited[]){
		if(visited[v]){
			return;
		}
		visited[v] = true;
		System.out.println(vnodes[v].data);
		for(ArcNode node = vnodes[v].firstArc;node != null;node = node.next){
			if(!visited[node.adjvex]){
				dfs(node.adjvex,visited);
			}
		}
	}
	
	public void dfsTraverse(){
		boolean visited[] = new boolean[n];
		for(int i = 0;i<n;i++){
			dfs(i,visited);
		}	
	}
	
	public void bfs(int v,boolean visited[]){
		if(visited[v]){
			return;
		}
		int que[] = new int[n];
		int front = 0,rear = 0;
		visited[v] = true;
		System.out.println(vnodes[v].data);
		rear = (rear + 1) % n;
		que[rear] = v;
		while(rear != front){
			front = (front + 1)%n;
			int j = que[front];
			for(ArcNode node = vnodes[j].firstArc;node != null;node=node.next){
				if(!visited[node.adjvex]){
					rear = (rear + 1)%n;
					que[rear] = node.adjvex;
					System.out.println(vnodes[node.adjvex].data);
					visited[node.adjvex] = true;
				}
			}
		}
	}
	
	@Override
	public void bfsTraverse() {
		boolean visited[] = new boolean[n];
		for(int i = 0;i<n;i++){
			bfs(i,visited);
		}	
	}
	
	public List<String> prim(){
		List<String> list = new ArrayList<String>();
		int lowcost[] = new int[n];
		boolean vset[] = new boolean[n];
		for (int i = 0; i < lowcost.length; i++) {
			lowcost[i] = Integer.MAX_VALUE;
		}
		int v = 0;
		for(ArcNode node = vnodes[v].firstArc;node != null; node = node.next){
			lowcost[node.adjvex] = node.w;
		}
		vset[v] = true;
		for (int i = 0; i < n-1; i++) {
			int min = Integer.MAX_VALUE;
			int k = 0;
			for (int j = 0; j < lowcost.length; j++) {
				if(!vset[j] && lowcost[j]<min){
					min = lowcost[j];
					k = j;
				}
			}
			list.add("(" + v + "," + k + ")");
			vset[k] = true;
			v = k;
			for(ArcNode node = vnodes[k].firstArc;node != null; node = node.next){
				if(!vset[node.adjvex] && node.w < lowcost[node.adjvex]){
					lowcost[node.adjvex] = node.w;
				}
			}
		}
		return list;
	}
	
	public List<String> dijistra(int v1,int v2){
		List<String> list = new ArrayList<String>();
		boolean vset[] = new boolean[n];
		int path[] = new int[n];
		int dist[] = new int[n];
		for (int i = 0; i < path.length; i++) {
			dist[i] = Integer.MAX_VALUE;
			path[i] = -1;
		}
		for(ArcNode node = vnodes[v1].firstArc;node != null;node = node.next){
			dist[node.adjvex] = node.w;
			path[node.adjvex] = v1;
		}
		
		vset[v1] = true;
		path[v1] = -1;	
		for (int i = 0; i < n; i++) {
			int min = Integer.MAX_VALUE;
			int k = 0;
			for (int j = 0; j < dist.length; j++) {
				if(!vset[j] && dist[j] < min){
					min = dist[j];
					k = j;
				}
			}
			vset[k] = true;
			for(ArcNode node = vnodes[k].firstArc;node != null;node = node.next){
				if(!vset[node.adjvex] && dist[node.adjvex] + dist[k] < dist[node.adjvex]){
					dist[node.adjvex] = dist[node.adjvex] + dist[k];
					path[node.adjvex] = k;
				}
			}
		}	
		int p = v2;
		while(path[p] != -1){
			list.add(0, "(" + path[p] + "," + p +")");
			p = path[p];
		}
		
		return list;
	}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
 * 邻接矩阵-
 */
public class MGraph implements Graph{
	private class Node{
		public String data;
	}
	protected static Scanner scanner = new Scanner(System.in);
	protected int edges[][];
	protected Node nodes[];
	protected int n,e;
	//创建图
	public static MGraph createGraph(){
		MGraph g = new MGraph();
		System.out.println("分别输入顶点数量和边数量");
		g.n = scanner.nextInt();
		g.e = scanner.nextInt();
		g.nodes = new Node[g.n];
		for(int i = 0;i<g.n;i++){
			g.nodes[i] = g.new Node();
		}
		g.edges = new int[g.n][g.n];
		System.out.println("输入每个顶点的信息");
		for(int i = 0;i<g.n;i++){
			g.nodes[i].data = scanner.next();
		}
		System.out.println();	
		for(int i = 0;i<g.n;i++){
			System.out.println("顶点" + g.nodes[i].data + "的编号为" + i);
		}
		for(int i=0;i<g.n;i++){
			for(int j = 0;j<g.n;j++){
				g.edges[i][j] = Integer.MAX_VALUE;
			}
		}
		for(int i = 0;i<g.e;i++){
			System.out.println("输入边的两个定点编号和它的权值");
			int x = scanner.nextInt();
			int y = scanner.nextInt();
			g.edges[x][y] = scanner.nextInt();
			g.edges[y][x] = g.edges[x][y];
		}
		return g;
	}
	//打印矩阵
	public void show(){
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				if(edges[i][j] == Integer.MAX_VALUE){
					System.out.print( "N\t");		
				}else{
					System.out.print(edges[i][j] + "\t");		
				}		
			}
			System.out.println();		
		}
	}
	//深度优先遍历
	public void dfsTraverse(){
		boolean visited[] = new boolean[n];
		for(int i = 0;i<n;i++){
			dfs(i,visited);
		}
	}
	
	private void dfs(int v,boolean visited[]){
		if(visited[v])return;
		visited[v] = true;
		System.out.print(nodes[v].data+ "\t");
		for(int i=0;i<n;i++){
			if(edges[v][i] != Integer.MAX_VALUE && !visited[i]){
				dfs(i,visited);
			}
		}
	}

	private void bfs(int v,boolean visited[]){
		if(visited[v]){
			return;
		}
		int que[] = new int[n];
		int front = 0,rear = 0;
		rear = (rear+1)%n;
		que[rear] = v;
		visited[v] = true;
		System.out.print(nodes[v].data + "\t");
		while(front != rear){
			front = (front + 1)%n;
			int flag = que[front];
			for(int i = 0;i<n;i++){
				for(int j=0;j<n;j++){
					if(edges[flag][i] != Integer.MAX_VALUE && !visited[i]){
						System.out.print(nodes[i].data+ "\t");		
						rear = (rear + 1)%n;
						que[rear] = i;
						visited[i] = true;
					}
				}
			}
		}
	}
	//广度优先遍历
	public void bfsTraverse(){
		boolean visited[] = new boolean[n];
		for(int i = 0;i<n;i++){
			bfs(i,visited);
		}
	}
	//prim算法
	public List<String> prim(){
		List<String> list = new ArrayList<String>();	//用来保存最小生成树的边集
		int lowcost[] = new int[n];		//记录所有点到最小生成的距离
		boolean vset[] = new boolean[n];//记录已经加入到最小生成树的节点
		int v = 0;
		//从编号0开遍历
		for(int i = 0;i<n;i++){
			lowcost[i] = edges[0][i];
		}
		vset[v] = true;
		for (int i = 0; i < n-1; i++) {
			int min = Integer.MAX_VALUE;
			int k = 0;
			for (int j = 0; j < n; j++) {
				if (!vset[j] && lowcost[j] < min) {
					min = lowcost[j];
					k = j;
				}
			}
			list.add("(" + v + "," + k + ")");
			vset[k] = true;
			v = k;
			for (int j = 0; j < n; j++) {
				if(!vset[j] && edges[k][j] < lowcost[j]){
					lowcost[j] = edges[k][j];
				}
			}
		}
		return list;
	}
	//地杰斯特拉算法
	public List<String> dijistra(int v1,int v2){
		List<String> list = new ArrayList<String>();
		boolean vset[] = new boolean[n];
		int path[] = new int[n];
		int dist[] = new int[n];
		for (int i = 0; i < n; i++) {	//对个数组进行初始化
			dist[i] = edges[v1][i];
			if(edges[v1][i] < Integer.MAX_VALUE){
				path[i] = v1;
			}else{
				path[i] = -1;
			}
		}
		vset[v1] = true;
		path[v1] = -1;
		for (int i = 0; i < n-1; i++) {
			int min = Integer.MAX_VALUE;
			int v = 0;
			for (int j = 0; j < n; j++) {
				if(!vset[j] && dist[j] < min){
					v = j;
					min = dist[j];
				}
			}
			vset[v] = true;
			for (int j = 0; j < n; j++) {
				if(!vset[j] && dist[v] + edges[v][j] < dist[j]){
					dist[j] = dist[v] + edges[v][j];
					path[j] = v;
				}
			}
		}
		int p = v2;
		while(path[p] != -1){
			list.add(0, "(" + path[p] + "," + p +")");
			p = path[p];
		}
		return list;
	}
}
import java.util.List;
import java.util.Scanner;

public class Main {
	public static void main(String args[]){
		Scanner scanner = new Scanner(System.in);
		System.out.println("选择需要操作的序号");
		System.out.println("1、使用邻接表的方法遍历");
		System.out.println("2、使用邻接矩阵的方法遍历");
		
		int flag = scanner.nextInt();
		Graph g = null;
		switch(flag){
		case 1:
			g = AGraph.createGraph();
			break;
		case 2:
			g = MGraph.createGraph();
			break;
		}
		traverse(g);
		scanner.close();
	}
	
	public static void traverse(Graph g){
		System.out.println("深度优先结果为:");
		g.dfsTraverse();
		System.out.println("广度优先结果为:");
		g.bfsTraverse();
	
		System.out.println("prim算法结果为:");
		List<String> eset = g.prim();
		for (String string : eset) {
			System.out.println(string);
		}
		
		System.out.println("地杰斯特算法结果为:");
		List<String> eset1 = g.dijistra(0, 2);
		for (String string : eset1) {
			System.out.println(string);
		}
	}
}

测试结果

选择需要操作的序号
1、使用邻接表的方法遍历
2、使用邻接矩阵的方法遍历
2
分别输入顶点数量和边数量
6 8
输入每个顶点的信息
1 2 3 4 5 6

顶点1的编号为0
顶点2的编号为1
顶点3的编号为2
顶点4的编号为3
顶点5的编号为4
顶点6的编号为5
输入边的两个定点编号和它的权值
0 1 3
输入边的两个定点编号和它的权值
0 2 4
输入边的两个定点编号和它的权值
1 2 2
输入边的两个定点编号和它的权值
1 3 7
输入边的两个定点编号和它的权值
3 5 3
输入边的两个定点编号和它的权值
1 4 4
输入边的两个定点编号和它的权值
2 4 1
输入边的两个定点编号和它的权值
4 5 5
深度优先结果为:
1	2	3	5	6	4	
广度优先结果为:
1	2	3	4	5	6	
prim算法结果为:
(0,1)
(1,2)
(2,4)
(4,5)
(5,3)
地杰斯特算法结果为:
(0,1)
(1,5)
(5,4)
(4,2)

总结

本文提供了图的几个重要算法的Java实现,最后给出了实验结果的原理分析。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中的深度优先搜索(Depth-First Search, DFS)和广度优先搜索(Breadth-First Search, BFS)是两种常用的遍历算法,它们主要用于探索或树结构中节点间的连接。 **深度优先搜索(DFS)**: - DFS通过递归或栈实现,从根节点开始,尽可能深地探索分支,直到到达叶子节点,然后回溯到上一个未访问的节点。 - 它的主要特点是能够找到一条从起点到终点的路径,如果存在这样的路径。 - 实现时,通常会使用递归方法标记已访问节点,避免重复访问。 **广度优先搜索(BFS)**: - BFS采用队列数据结构,从根节点开始,逐层地访问节点,先访问同一层的所有节点,然后再访问下一层。 - BFS的特点是保证最先被访问的是距离起点最近的节点,用于寻找最短路径或最小代价路径。 - 在实现时,通常用一个队列来存储当前层级的所有节点。 **算法分析**: 1. **时间复杂度**:DFS在最坏情况下可能需要遍历所有节点,时间复杂度为O(V+E)(V为顶点数,E为边数)。BFS同样如此,因为它们都需要访问所有节点。 2. **空间复杂度**:DFS通常占用较少的栈空间,因为只需要保存当前路径;而BFS由于需要存储所有同一层级的节点,所以可能需要较大的队列空间,为O(W),其中W为宽度(最大层级数)。 3. **适用场景**:DFS适用于寻找连通分量、是否存在环等问题,而BFS则适用于求解最短路径最小生成树等场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值