目录
一、问题描述
设计一个校园导游程序,为来访的客人提供各种信息查询服务
基本要求
-
设计你所在学校的校园平面图,所含景点不少于10个
-
以图中顶点表示校内各景点,存放景点名称、代号、简介 等信息;以边表示路径,存放路径长度等相关信息
-
为来访客人提供图中任意景点相关信息的查询
-
为来访客人提供图中任意景点的问路查询,即查询任意两个景点之间的一条最短的简单路径
二、问题分析
本项目用于呈现某区域的景点、各景点的距离等信息,并为用户提供信息查询功能以及根据当前位置获得到目的地的最短路径
实现思路
- 利用对象流实现读取和存储平面图的邻接矩阵以及景点信息,实现查询不同区域的景点信息
- 根据用户提供数据生成当前平面图的邻接矩阵
- 通过Dijkstra算法实现查询两景点的最短路径功能
三、逻辑设计
系统通过读取用户提供的邻接矩阵,提供景点和路径查询服务
1.数据结构
目前考虑用带权图来表示学校平面图,并用邻接矩阵进行存储,声明Sights类,用于存放景点的具体信息,在对JavaScript进行学习以后会对系统界面进行设计,将景点位置信息进行可视化,从而更好地与用户进行交互
2.具体功能
- 查询功能:可通过用户提供的景点名称得到对应景点的详细信息
- 路径查询:根据用户提供的当前位置与目的地信息,为用户提供抵达目的地的最短路径
3.概要设计
用户打开系统后可选择读取或生成新的地图信息,通过声明GenerateMap类的对象newMap调用本类的generate方法来生成并初始化新的平面图和相关景点信息,初始化工作完成后系统将提供查询功能,可通过输入景点的编号来得到对应景点的所有信息,并能通过用户输入的当前景点编号和目的地编号来得到两景点最短路径的距离以及具体路径,此外,可根据用户需要选择是否存储当前平面图的相关信息
四、物理设计
1.Main类
通过I/O对象流来实现邻接矩阵和景点信息的读取和存储,以及显示平面图
2.CampusGuidingSystem类
系统的主要功能在本类中实现
1.成员变量
Sights[] vertex | 存放景点信息 |
int[][] map | 存放平面图的邻接矩阵 |
int[] dist | 记录当前位置到其余结点的最短距离 |
int[] visited | 标记已收录到最短路径集合的结点 |
int[] path | 记录目标结点在最短路径中的上一结点 |
2.方法
CampusGuidingSystem(){...} | 完成对象初始化 |
functions(){...} | 提供查询功能 |
findTarget(){...} | 由编号得到目标景点 |
querySightIf(){...} | 查询景点信息 |
findPath(){...} | 由Dijkstra算法得到的path得到具体路径 |
dijkstra(){..} | 求最短路径 |
3.Dijkstra算法
用于查询最短路径,其主要思想如下
- 每次从未标记的结点中找到离起始结点最短的结点并将其收入最短路径集合
- 计算新加入结点A的临近结点B(未被标记)的距离,即代码中的minDist + map[temp][i]
- 若计算得到的距离小于原来从起始结点到结点B的距离,则更新B结点的dist和path
Dijkstra算法流程图
具体代码
public void dijkstra(int begin) { //dijkstra算法求最短路径
int length = map.length;
dist = new int[length];
visited = new int[length];
path = new int[length];
for (int i = 0; i < length; i++) {
dist[i] = map[begin][i];
}
for (int i = 0; i < length; i++) {
if (dist[i] != Integer.MAX_VALUE) path[i] = begin;
else path[i] = -1;
}
visited[begin] = 1;
int count = 1;
while (count != length) {
int temp = 0; //记录当前dist中最短距离的编号
int minDist = Integer.MAX_VALUE; //记录当前最小值
for (int i = 0; i < length; i++) { //从未标记的结点中选取离出发点最近的结点
if (visited[i] != 1 && dist[i] < minDist) {
temp = i;
minDist = dist[i];
}
}
visited[temp] = 1;
for (int i = 0; i < length; i++) { //更新dist和path
if (visited[i] != 1 && map[temp][i] != Integer.MAX_VALUE) {
if (dist[i] > (minDist + map[temp][i])) {
dist[i] = minDist + map[temp][i];
path[i] = temp;
}
}
}
count++;
}
}
3.GenerateMap类
用于生成所需的邻接矩阵以及初始化景点信息
1.成员变量
Sights[] vertex | 存放景点 |
int[][] map | 存放生成的邻接矩阵 |
2.方法
generate(){...} | 初始化景点信息和邻接矩阵 |
getName(){...} | 由景点编号得到景点名称 |
4.Sights类
存放每个景点的详细信息
成员变量
int number | 景点编号 |
String name | 景点名称 |
String detailedInformation | 景点的详细信息 |
五、源代码
1.Main类
import java.io.*;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception { //系统入口
Scanner in = new Scanner(System.in);
System.out.println("是否要导入校园平面图\ny:是\nn:否");
char w=in.next().charAt(0);
if(w=='y'){
System.out.println("请输入文件路径以及图片的高度和宽度");
String file=in.next();
int height=in.nextInt();
int width=in.nextInt();
MainWnd mainWnd=new MainWnd(file,height,width);
}
System.out.println("========欢迎使用校园导航资讯系统========");
System.out.println("请选择:\n1:查询已存储的地图\n2:导入新地图");
while (true) {
int choice = in.nextInt();
if (choice == 1) {
System.out.println("请输入文件路径");
String filePath = in.next();
ObjectInputStream read = new ObjectInputStream(new FileInputStream(filePath));
Object a = read.readObject();
int[][] map = (int[][]) a;
Object b = read.readObject();
Sights[] vertex = (Sights[]) b;
CampusGuidingSystem start = new CampusGuidingSystem(vertex, map);
start.functions();
System.out.println("查询结束");
break;
} else if (choice == 2) {
GenerateMap newMap = new GenerateMap();
newMap.generate();
int[][] map = newMap.map;
Sights[] vertex = newMap.vertex;
CampusGuidingSystem start = new CampusGuidingSystem(vertex, map);
start.functions();
System.out.println("查询结束");
System.out.println("是否保存当前地图信息?\ny:是\nn:否");
char c = in.next().charAt(0);
if (c == 'y') {
String filePath = "";
System.out.println("请输入文件保存路径");
filePath = in.next();
ObjectOutputStream preserve = new ObjectOutputStream(new FileOutputStream(filePath));
preserve.writeObject(map);
preserve.writeObject(vertex);
System.out.println("数据保存完毕");
break;
} else break;
} else {
System.out.println("输入有误,请重新选择");
}
}
System.out.println("程序运行完毕");
}
}
2.MainWnd类
import javax.swing.*;
import java.awt.*;
public class MainWnd extends JFrame {
public MainWnd(String filePath, int height, int width) {
super("校园平面图");
ImageIcon imageIcon = new ImageIcon(filePath);
this.setVisible(true);
this.setLayout(null);
this.setBounds(300, 80, 1000, 700);
Image image = imageIcon.getImage();
image = image.getScaledInstance(height, width, Image.SCALE_DEFAULT);
imageIcon.setImage(image);
JLabel show = new JLabel(imageIcon);
this.add(show);
show.setSize(height, width);
}
}
3.CampusGuidingSystem类
import java.util.Scanner;
import java.util.Stack;
public class CampusGuidingSystem {
public Sights[] vertex; //存放景点信息
public int[][] map; //校园图的邻接矩阵
int[] dist; //记录当前位置到其余景点的最短距离
int[] visited; //标记已收录到最短路径集合的结点
int[] path; //记录目标景点在最短路径中的上一个结点
public CampusGuidingSystem(Sights[] vertex, int[][] map) {
this.vertex = vertex;
this.map = map;
}
public void functions() {
Scanner in = new Scanner(System.in);
System.out.println("========请选择需要查找的信息========");
while (true) {
int choice = 0;
while (true) {
System.out.println("1:查询景点信息\n2:查询到目的地的最短路径");
choice = in.nextInt();
if (choice == 1) {
System.out.println("请输入要查询景点的编号");
int number = in.nextInt();
querySightIf(findTarget(number - 1));
break;
}
if (choice == 2) {
System.out.println("请输入当前景点的编号");
int now = in.nextInt();
System.out.println("请输入目的地的编号");
int destination = in.nextInt();
findPath(now - 1, destination - 1);
break;
} else System.out.println("输入有误,请重新选择");
}
System.out.println("是否要重新查询?\ny:是\nn:否");
char judge = in.next().charAt(0);
if (judge == 'n') break;
}
}
public Sights findTarget(int number) { //根据编号得到目标景点
for (Sights i : vertex) {
if (i.number == number) return i;
}
return null;
}
public void querySightIf(Sights target) { //查询景点信息
if (target == null) {
System.out.println("未查询到该景点,请检查输入编号是否有误");
return;
}
System.out.println("编号:" + (target.number + 1));
System.out.println("名称:" + target.name);
System.out.println("详细信息:" + target.detailedInformation);
}
public void findPath(int now, int destination) { //查询两景点间的最短路径
dijkstra(now);
Stack<String> stack = new Stack<>();
int temp = destination;
while (temp != now) {
stack.push(findTarget(temp).name);
temp = path[temp];
}
String path = findTarget(now).name;
while (!stack.empty()) {
path += "->";
path += stack.pop();
}
System.out.println("从当前位置到目的地最短路径的长度为:" + dist[destination]);
System.out.println("具体路径为:" + path);
}
public void dijkstra(int begin) { //dijkstra算法求最短路径
int length = map.length;
dist = new int[length];
visited = new int[length];
path = new int[length];
for (int i = 0; i < length; i++) {
dist[i] = map[begin][i];
}
for (int i = 0; i < length; i++) {
if (dist[i] != Integer.MAX_VALUE) path[i] = begin;
else path[i] = -1;
}
visited[begin] = 1;
int count = 1;
while (count != length) {
int temp = 0; //记录当前dist中最短距离的编号
int minDist = Integer.MAX_VALUE; //记录当前最小值
for (int i = 0; i < length; i++) { //从未标记的结点中选取离出发点最近的结点
if (visited[i] != 1 && dist[i] < minDist) {
temp = i;
minDist = dist[i];
}
}
visited[temp] = 1;
for (int i = 0; i < length; i++) { //更新dist和path
if (visited[i] != 1 && map[temp][i] != Integer.MAX_VALUE) {
if (dist[i] > (minDist + map[temp][i])) {
dist[i] = minDist + map[temp][i];
path[i] = temp;
}
}
}
count++;
}
}
}
4.GenerateMap类
import java.util.Scanner;
/*
用于生成邻接矩阵和初始化景点相关信息
*/
public class GenerateMap {
public Sights[] vertex;
public int[][] map;
public void generate() {
Scanner in = new Scanner(System.in);
System.out.println("请输入景点个数");
int num = in.nextInt();
vertex = new Sights[num];
map = new int[num][num];
System.out.println("========请输入景点信息========");
for (int i = 0; i < num; i++) {
System.out.println("请输入编号为" + (i + 1) + "的景点信息");
System.out.println("请输入景点名称和景点的详细信息");
String name = in.next();
String detailedInformation = in.next();
Sights temp = new Sights(i, name, detailedInformation);
vertex[i] = temp;
}
System.out.println("========请输入各景点间的距离========");
System.out.println("若两景点间没有直达的路径,输入距离为0");
for (int i = 0; i < num; i++) {
for (int j = 0; j < i + 1; j++) {
if (i == j) {
map[i][j] = Integer.MAX_VALUE;
map[j][i] = Integer.MAX_VALUE;
continue;
}
System.out.println("请输入" + getName(i) + "和" + getName(j) + "间的距离");
int length = in.nextInt();
if (length == 0) {
map[i][j] = Integer.MAX_VALUE;
map[j][i] = Integer.MAX_VALUE;
} else {
map[i][j] = length;
map[j][i] = length;
}
}
}
}
public String getName(int number) { //由景点编号得到景点名称
for (Sights i : vertex) if (i.number == number) return i.name;
return null;
}
}
5.Sights类
import java.io.Serializable;
public class Sights implements Serializable {
int number; //景点编号
String name; //景点名称
String detailedInformation; //景点详细信息
public Sights(int number, String name, String detailedInformation) {
this.number = number;
this.name = name;
this.detailedInformation = detailedInformation;
}
}