知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
文章目录
- 图的顶点类型定义
- 图的类型定义
- 从文件中读取图的顶点和路径信息创建邻接表
- 图的深度优先遍历DFS
- 图的广度优先遍历BFS
- 最短路径求解
- 深度优先遍历、广度优先遍历,最短路径代码测试
这篇文章主要输出完整的有向图的相关代码,关于无向图和有向图的理论,很多数据结构的书籍都有涉及,网上也有很多的资料,这里就不再赘述了,关于图的主要基本数据结构主要有三种:邻接矩阵、邻接表、十字链表。
这篇文章的代码使用Java实现的,一个基于邻接表结构的不带权值有向图的深度优先遍历、广度优先遍历和最短路径求解。
代码实现思想是从文件中读取图的顶点和路径信息,文件名称是citys.txt,文件的内容如下:
(1)西安
3, 5
(2)洛阳
4, 6
(3)安徽
1 ,4
(4)北京
2, 7, 8
(5)福建
3, 6, 9
(6)杭州
1, 4, 8
(7)深圳
2, 3, 6
(8)苏州
3, 6, 1
(9)中南海
2, 5
上面每两行分别显示了图的顶点信息(城市名称)和路径信息。
图的顶点类型定义
/**
* 定义图的顶点类型
*/
static class Vertex{
public Vertex(String data, LinkedList<Integer> adjList) {
this.data = data;
this.adjList = adjList;
}
String data; // 邻接表数组的数据
LinkedList<Integer> adjList; // 邻接表中的链表
}
图的类型定义
public class Digraph {
// 定义邻接表
private ArrayList<Vertex> adj;
/**
* 初始化邻接表集合
*/
public Digraph(){
adj = new ArrayList<>();
}
}
从文件中读取图的顶点和路径信息创建邻接表
/**
* 从指定的流里面读取邻接表的数据
* @param reader
*/
public void read(BufferedReader reader) throws IOException {
// 给邻接表的第0项添加一个vertex,因为代码上顶点是从1开始编号的
adj.add(new Vertex("", null));
String city = null;
String[] vertex = null;
for(;;){
city = reader.readLine();
if(city == null){
break;
}
LinkedList<Integer> list = new LinkedList<>();
vertex = reader.readLine().split(",");
for (int i = 0; i < vertex.length; i++) {
list.add(Integer.parseInt(vertex[i].trim()));
}
adj.add(new Vertex(city, list));
}
}
图的深度优先遍历DFS
/**
* 从指定的start顶点开始,实现深度优先搜索
*/
public void dfs(int start){
// 因为图的每一个顶点都有可能有多条路径到达,因此需要定义一个数组,记录遍历的情况
boolean[] visited = new boolean[adj.size()];
dfs(start, visited);
}
/**
* 递归实现图的深度遍历
* @param start
* @param visited
*/
private void dfs(int start, boolean[] visited) {
System.out.print(adj.get(start).data + " ");
visited[start] = true;
LinkedList<Integer> list = adj.get(start).adjList;
for (int i = 0; i < list.size(); i++) {
if(!visited[list.get(i)]){
dfs(list.get(i), visited);
}
}
}
图的广度优先遍历BFS
/**
* 从指定的start顶点开始,实现广度优先搜索
* @param start
*/
public void bfs(int start){
boolean[] visited = new boolean[adj.size()];
// 广度优先搜搜需要借助一个队列完成层层向外扩张的方式进行遍历,类似于树的层序遍历
LinkedList<Vertex> queue = new LinkedList<>();
// 把起始的start节点入队列
queue.offer(adj.get(start));
visited[start] = true;
// 出队遍历顶点,并把顶点的邻接节点入队列
while(!queue.isEmpty()){
Vertex vt = queue.poll();
System.out.print(vt.data + " ");
LinkedList<Integer> list = vt.adjList;
for (int i = 0; i < list.size(); i++) {
if(!visited[list.get(i)]){
queue.offer(adj.get(list.get(i)));
visited[list.get(i)] = true;
}
}
}
}
最短路径求解
/**
* 在不带权图中,找一条从start到end的最短路径信息并且打印,借助广度优先搜索,层层向外扩张
* 需要记录节点的邻接路径
* @param start
* @param end
*/
public void findShortestPath(int start, int end){
int[] path = new int[adj.size()];
boolean[] visited = new boolean[adj.size()];
LinkedList<Integer> queue = new LinkedList<>();
boolean flag = false;
queue.offer(start);
visited[start] = true;
while(!queue.isEmpty()){
int v = queue.poll();
if(v == end){
flag = true;
break;
}
LinkedList<Integer> list = adj.get(v).adjList;
for (int i = 0; i < list.size(); i++) {
if(!visited[list.get(i)]){
queue.offer(list.get(i));
path[list.get(i)] = v; // 在这里记录顶点的前驱顶点信息
visited[list.get(i)] = true;
}
}
}
if(!flag){
System.out.println(start + "->" + end + " 不存在路径!");
return;
}
// 从path数组中读取正确的顶点最短路径信息
LinkedList<Integer> stack = new LinkedList<>();
stack.push(end);
for (;;){
int v = path[stack.peek()];
stack.push(v);
if(v == start){
break;
}
}
// 打印最短路径信息
while(!stack.isEmpty()){
System.out.print(adj.get(stack.pop()).data);
if(stack.isEmpty()){
break;
}
System.out.print(" => ");
}
}
深度优先遍历、广度优先遍历,最短路径代码测试
@Test
public void testdfs() throws IOException {
Digraph d = new Digraph();
d.read(new BufferedReader(new FileReader("citys.txt")));
d.dfs(1);
}
@Test
public void testbfs() throws IOException {
Digraph d = new Digraph();
d.read(new BufferedReader(new FileReader("citys.txt")));
d.bfs(1);
}
@Test
public void testshortestpath() throws IOException {
Digraph d = new Digraph();
d.read(new BufferedReader(new FileReader("citys.txt")));
d.findShortestPath(1, 8);
}
上面三个测试用例的打印如下:
(1)西安 (3)安徽 (4)北京 (2)洛阳 (6)杭州 (8)苏州 (7)深圳 (5)福建 (9)中南海
(1)西安 (3)安徽 (5)福建 (4)北京 (6)杭州 (9)中南海 (2)洛阳 (7)深圳 (8)苏州
(1)西安 => (3)安徽 => (4)北京 => (8)苏州