1、项目概述
1.1项目目标和主要内容
- 学习图形界面的设计,利用 MFC 应用程序(Java swing 或 QT 框架,或 C#)创建基于对话框的应用程序,添加按钮、编辑框等控件;
- 能通过设计的按钮控件输入并实现简单算术运算,要求表达式在编辑框中显示,能将运算结果,输出在编辑框内显示;并保存历史的表达式运算记录。
- 也能够实现混合运算的算术表达式求解,算术表达式中包括加、减、乘、除、括号等运算符;并且能够识别括号,优先级正确。
1.2项目的主要功能
- 计算器能够实现混合运算的算术表达式求解;
- 输入时有足够的容错机制,来尽量避免用户输入不合规的算术表达式。
2、项目设计
一个类实现将txt文件加载到idea变成字节流,再将字节流转换成地图,实现通路和墙体:
1·导入文件迷宫转化成地图
3·绘制地图并计数的函数
package maze_solution;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class MazeSolution {
public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
System.out.println("Please enter the size(不能大于20) and name of your maze(数据的文件名)");
int n = in.nextInt();
// String filename = in.next();
// File file=new File(filename);
FileReader file = null;
try {
file = new FileReader(".//src//map.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 读取文件里面的数据,将其存入map二维数组中
Scanner input = new Scanner(file);
int[][] map = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = input.nextInt();
}
}
// 这是开始时间,用于记录搜索耗费的时间
long startTime = System.currentTimeMillis();
// 定义地图的基本数据,长宽高,大小
MapStructure QuestionMap = new MapStructure(map, map.length, new Point(0, 0),
new Point(map.length-1, map.length-1));
new Algorithm().Go(QuestionMap);
// 打印最后的map
printAnswer(map);
// 记录结束时间
long endTime=System.currentTimeMillis();
System.out.println("This program takes " + (endTime - startTime) + "ms.");
}
一个类实现地图绘制
public static void printAnswer(int[][] maps)
{
for (int i = 0; i < maps.length; i++)
{
for (int j = 0; j < maps[i].length; j++)
{
if (maps[i][j] == 1) {
// 这个代表墙体
System.out.print("🟦");
}
if (maps[i][j] == 0) {
// 这个代表路
System.out.print("0️⃣");
}
if (maps[i][j] == 2) {
// 这个代表最终的路径
System.out.print("*️⃣");
}
// System.out.print(maps[i][j] + " ");
}
System.out.println();
}
}
}
一个类判断是否找到终点
class Coord{
int x;
int y;
Coord(int x, int y){
this.x = x;
this.y = y;
}
//判断是否已经到达了终点
boolean isEquals(Coord goal){
if(goal.x == x && goal.y == y)
return true;
return false;
}
}
一个类表示节点(当前点,墙体,持有点)坐标
class Point{
Coord coord;
Point last;
// A*算法的两个参数
int Gx; //移动距离
int Hx; //离终点的距离
int air;
//节点坐标
Point(int x, int y){
this.coord = new Coord(x,y);
}
Point(Coord coord, Point parent, int Gx, int Hx){
this.coord = coord;
this.last = parent;
this.Gx = Gx;
this.Hx = Hx;
}
Point(int air){
this.air = air;
}
}
一个类初始化地图
//MapStructure定义结构
class MapStructure{
int[][] map;
int n; //地图的宽和高
Point start;
Point end;
MapStructure(int [][]map, int n, Point start,Point end){
this.map = map;
this.n = n;
this.start = start;
this.end = end;
}
void setMapStructure(Point start,Point end){
this.start = start;
this.end = end;
}
}
一个类完成A*算法核心查找终点功能
//完成A*算法,并求出路径
class Algorithm{
// 1代表墙
final static int bar = 1;
// 2代表选中,走的路径
final static int sign = 2;
final static int value = 1; //只能上下左右移动
ArrayList<Point> openList = new ArrayList<Point>(); //放入可移动的路径
ArrayList<Point> closeList = new ArrayList<Point>(); //放入走过的路径
boolean isEndCoord(Coord coord, Coord end){
if(coord != null && end.isEquals(coord))
// 已经到达终点
return true;
return false;
}
//是否能加入移动路径
boolean canAddopenList(MapStructure map, int x, int y){
if(x<0 || x>=map.n || y<0 || y>=map.n)
// 不能越界
return false;
if(map.map[y][x] == bar)
// 不能等于墙
return false;
if(isIncloseList(x, y))
return false;
if(isInopenList(x, y))
return false;
// 以上判断判断都不符合,可以加入
return true;
}
// 是否在放入可移动的路径
boolean isInopenList(int x, int y){
for(Point point : openList){
if(x == point.coord.x && y == point.coord.y)
return true;
}
return false;
}
// 是否在放入走过的路径
boolean isIncloseList(int x, int y){
if(closeList.isEmpty())
return false;
for(Point point : closeList){
if(x == point.coord.x && y == point.coord.y)
return true;
}
return false;
}
int calcHx(Coord goal, Coord end){
// 欧式距离
return Math.abs(goal.x - end.x) + Math.abs(goal.y - end.y);
}
// 增加可移动的路径列表
void addPointInopenList(MapStructure map, Point current, int x, int y){
if(canAddopenList(map, x, y)){
Point end = map.end;
Point goal = new Point(x, y);
// 当前的距离加1
int Gx = current.Gx + value;
// 这是与目标的距离
int Hx = calcHx(current.coord, end.coord);
// 判断是否在终点
if(isEndCoord(goal.coord, end.coord)){
goal = end;
goal.last = current;
goal.Gx = Gx;
goal.Hx = calcHx(goal.coord, end.coord);
}
else
// 如果不在,继续寻找
goal = new Point(goal.coord, current, Gx, Hx);
// 加入放入可移动的路径
openList.add(goal);
}
}
void addPointInopenList(MapStructure map, Point current){
int x = current.coord.x;
int y = current.coord.y;
addPointInopenList(map, current, x-1, y); //左
addPointInopenList(map, current, x+1, y); //右
addPointInopenList(map, current, x, y-1); //上
addPointInopenList(map, current, x, y+1); //下
}
void drawPath(int[][] map, Point end){
while(end != null){
map[end.coord.y][end.coord.x] = sign;
end = end.last;
}
}
//获得路径信息
void getPathInformation(int[][] map, Point end){
int path = 1;
System.out.println("The shortest path needs " + end.Gx + " steps.");
while(end != null){
// 可移动路径进行遍历
for(Point point : openList){
if(point.Hx == end.Hx && point.Gx == end.Gx){
path++;
}
}
map[end.coord.y][end.coord.x] = sign;
end = end.last;
}
System.out.println("The shortest path number is " + path);
}
//从地图左上角为所有的0赋予
void addBreathbyLeft(MapStructure map){
int i = 0;
int j = 0;
Point zero = new Point(j, i);
for (i = 1; i < map.n-1; i++) {
for (j = 1; j < map.n-1; j++) {
zero.air = 0;
if(map.map[i][j] == 0){
if(map.map[i][j+1] == 0)
zero.air++;
if(j >= 1 && map.map[i][j-1] == 0)
zero.air++;
if(map.map[i+1][j] == 0)
zero.air++;
if(i >= 1 && map.map[i-1][j] == 0)
zero.air++;
}
if(zero.air <= 1){ //判断迷宫中只有一格气的点,说明它是死胡同,将它改为1
map.map[i][j] = bar;
// System.out.println("0➡1");
}
}
}
}
//从地图右下角为所有的0赋予气
void addBreathbyRight(MapStructure map){
int i = 0;
int j = 0;
Point zero = new Point(j, i);
for (i = map.n-2; i > 0; i--) {
for (j = map.n-2; j > 0; j--) {
zero.air = 0;
if(map.map[i][j] == 0){
if(map.map[i][j+1] == 0)
zero.air++;
if(j >= 1 && map.map[i][j-1] == 0)
zero.air++;
if(map.map[i+1][j] == 0)
zero.air++;
if(i >= 1 && map.map[i-1][j] == 0)
zero.air++;
}
if(zero.air <= 1){ //判断迷宫中只有一格气的点,说明它是死胡同,将它改为1
//System.out.println("0➡1");
map.map[i][j] = bar;
}
}
}
}
//从左上和右下两次遍历,将所有死胡同堵死
void changeBreath(MapStructure map){
for (int i = 0; i < map.map.length-1; i++) {
addBreathbyLeft(map);
addBreathbyRight(map);
}
System.out.println("Get rid of all dead paths (they don't have extra breath)");
}
一个类进行执行,完成路径数,查找时间,和最佳路线的输出
void Go(MapStructure map){
if(map == null){
// 初始化失败
System.out.println("The map structure is wrong!");
return;
}
openList.clear();
closeList.clear();
changeBreath(map);
openList.add(map.start);
movePoint(map);
getPathInformation(map.map, map.end);
System.out.println("This is all maze paths (The sign of the pathway is '2'):");
}
void Back(MapStructure map){
openList.clear();
closeList.clear();
openList.add(map.start);
movePoint(map);
}
Point findMinInopenList(){
Point goal = openList.get(0);
for(Point point : openList){
if(point.Gx + point.Hx < goal.Gx + goal.Hx)
goal = point;
}
return goal;
}
void movePoint(MapStructure map){
while(!openList.isEmpty()){
Point goal = findMinInopenList();
openList.remove(goal);
closeList.add(goal);
addPointInopenList(map, goal);
if(isIncloseList(map.end.coord.x, map.end.coord.y)){
drawPath(map.map, map.end);
break;
}
}
}
}
2.2关键算法分析
1·深度遍历的算法描述:
更新访问点数组信息,在邻接矩阵当中找到该访问点的最近的邻接点,访问该邻接点,输出遍历顶点信息,重复此步骤。
当无法继续深度遍历的时候,在访问点数组中往后依次退1,直到能有一个顶点能继续深度遍历。
当起点都无法继续深度遍历的时候,对图的深度遍历已完成
实际就是从第n个顶点开始、标记该顶点已被访问,然后查找该顶点第一个未访问的邻接点第i个顶点,再去第i个顶点 深度遍历。就是一个递归的过程。
2·广度优先搜索:
又被称为宽度优先搜索,是最常见的图搜索方法之一。
广度优先搜索指从某个节点(源点)出发,一次性访问所有未被访问的邻接点,再依次从这些已访问过的邻接点出发,一层一层地访问。
BFS以起点A为圆心,先搜索A周围的所有点,形成一个类似圆的搜索区域,再扩大搜索半径,进一步搜索其它没搜索到的区域,直到终点B进入搜索区域内被找到。
再看一下深度优先搜索,这里的深度优先搜索不是所有路径都搜索而是沿着B点方向搜索。(图片来源网上)。DFS则是让搜索的区域离A尽量远,离B尽量近,比如现在你在一个陌生的大学校园里,你知道校门口在你的北方,虽然你不知道你和校门口之间的路况如何,地形如何,但是你会尽可能的往北方走,总能找到校门口。
比起BFS,DFS因为尽量靠近终点的原则,其实是用终点相对与当前点的方向为导向,所以有一个大致的方向,就不用盲目地去找了,这样,就能比BFS能快地找出来最短路径,但是这种快速寻找默认起点A终点B之间没有任何障碍物,地形的权值也都差不多。如果起点终点之间有障碍物,那么DFS就会出现绕弯的情况。
图中DFS算法使电脑一路往更右下方的区域探索,可以看出,在DFS遇到障碍物时,其实没有办法找到一条最优的路径,只能保证DFS会提供其中的一条路径(如果有的话)。
BFS保证的是从起点到达路线上的任意点花费的代价最小(但是不考虑这个过程是否要搜索很多格子);DFS保证的是通过不断矫正行走方向和终点的方向的关系,使发现终点要搜索的格子更少(但是不考虑这个过程是否绕远)。
A*算法的设计同时融合了BFS和DFS的优势,既考虑到了从起点通过当前路线的代价(保证了不会绕路),又不断的计算当前路线方向是否更趋近终点的方向(保证了不会搜索很多图块),是一种静态路网中最有效的直接搜索算法。
3、项目实现及结果分析
1)迷宫界面展示:
4、实验总结
1·地图需要自己提前设计二位数组来分辨路径和墙体,如果需要多付地图则需要更多的文件。
2·自己需要将地图导入进入程序之中