三种算法包括DFS、BFS、A*,程序具有可视化界面,拥有打开文件、保存文件的功能,鼠标左键、右键分别选择当前绘制线路的两个端点,鼠标中键选择搜索路径的起点和终点,附上代码。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main{
public static void main(String[] args) {
MainWnd wd = new MainWnd();
}
}
class MainWnd extends JFrame{
private int X1[],Y1[],X2[],Y2[];
private int X1_b[],Y1_b[],X2_b[],Y2_b[];
private int X1_r[],Y1_r[],X2_r[],Y2_r[];
private int X_s = -1,Y_s = -1,X_e = -1 ,Y_e = -1;
private int mapp[][];
private int vst[];
private int dx = 20, dy = 45;
private int st = 0,st_b = 0, st_r = 0;
private int start = -1,end = -1;
JButton btn[]= {new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton(),
new JButton(),new JButton(),new JButton(),new JButton(),new JButton()};
JButton Dfs = new JButton("深度优先搜索");
JButton Bfs = new JButton("宽度优先搜索");
JButton A_ = new JButton("A*搜索");
JButton Return = new JButton("重新描绘");
JButton OPEN = new JButton("打开");
JButton SAVE = new JButton("保存");
JLabel Result = new JLabel();
JLabel signature = new JLabel("<html><body><p style='color :orange; font-size:15px; font-family = '宋体''>by:成亦峻<br/>AI Assignment</p></body></html>");
private void Open() {
FileDialog openDia = new FileDialog(new Frame(), "打开", FileDialog.LOAD);
openDia.setVisible(true);
String address = openDia.getDirectory();
String name = openDia.getFile();
File f = new File(address,name);
BufferedReader reader = null;
try {
InputStreamReader isr = new InputStreamReader(new FileInputStream(f) );
reader = new BufferedReader(isr);
String msg = "";
int line_sum = -6;
while((msg = reader.readLine()) != null) {
if(line_sum == -6) {
X_s = Integer.valueOf(msg).intValue();
}
else if(line_sum == -5) {
Y_s = Integer.valueOf(msg).intValue();
}
else if(line_sum == -4) {
X_e = Integer.valueOf(msg).intValue();
}
else if(line_sum == -3) {
Y_e = Integer.valueOf(msg).intValue();
}
else if(line_sum == -2) {
start = Integer.valueOf(msg).intValue();
}
else if(line_sum == -1) {
end = Integer.valueOf(msg).intValue();
}
else {
if(line_sum % 4 == 0) {
X1[st] = Integer.valueOf(msg).intValue();
}
else if(line_sum % 4 == 1) {
Y1[st] = Integer.valueOf(msg).intValue();
}
else if(line_sum % 4 == 2) {
X2[st] = Integer.valueOf(msg).intValue();
}
else {
Y2[st] = Integer.valueOf(msg).intValue();
mapp[(X2[st]-dx)/60 + (Y2[st]-dy)/60 * 10][(X1[st]-dx)/60 + (Y1[st]-dy)/60 * 10] = 1;
mapp[(X1[st]-dx)/60 + (Y1[st]-dy)/60 * 10][(X2[st]-dx)/60 + (Y2[st]-dy)/60 * 10] = 1;
st ++;
}
}
line_sum ++;
}
reader.close();
}catch(IOException e) {
e.printStackTrace();
}
}
private void Save() {
FileDialog saveDia = new FileDialog(new Frame(), "保存", FileDialog.SAVE);
saveDia.setVisible(true);
String adress = saveDia.getDirectory();
String name = saveDia.getFile();
String msg = "";
msg += X_s + "\r\n" + Y_s + "\r\n" + X_e + "\r\n" + Y_e + "\r\n" + start + "\r\n" +end + "\r\n";
for(int i = 0;i < st; i ++) {
msg += X1[i] + "\r\n" + Y1[i] + "\r\n" + X2[i] + "\r\n" + Y2[i] + "\r\n";
}
File f = new File(adress,name);
try {
FileWriter fw = new FileWriter(f);
fw.write(msg);
fw.close();
}catch(IOException e){
System.out.println(e.getMessage());
}
}
private MouseListener ML = new MouseListener() {
public void mouseClicked(MouseEvent e) {
String now = e.getSource().toString();
String l = "",r = "";
int nexti = 0;
for(int i = 21;;i++) {
if(now.charAt(i) == ',') {
nexti = i+1;
break;
}
l += now.charAt(i);
}
for(int i = nexti;;i++) {
if(now.charAt(i) == ',') {
break;
}
r += now.charAt(i);
}
int x = Integer.valueOf(l).intValue() + dx;
int y = Integer.valueOf(r).intValue() + dy;
int p = e.getButton();
if(p == 1) {
X1[st] = x;
Y1[st] = y;
}
else if(p == 3){
if(X1[st] == -1&& Y1[st] == -1) return;
X2[st] = x;
Y2[st] = y;
mapp[(x-dx)/60 + (y-dy)/60 * 10][(X1[st]-dx)/60 + (Y1[st]-dy)/60 * 10] = 1;
mapp[(X1[st]-dx)/60 + (Y1[st]-dy)/60 * 10][(x-dx)/60 + (y-dy)/60 * 10] = 1;
st ++;
}
else {
int Mark = (y-dy)/6 + (x-dx)/60;
if(start == -1) {
start = Mark;
X_s = x;
Y_s = y;
}
else {
end = Mark;
X_e = x;
Y_e = y;
}
}
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
};
private ActionListener RT = new ActionListener() {
public void actionPerformed(ActionEvent e) {
st = 0;
st_b = 0;
st_r = 0;
start = -1;
end = -1;
X_s = X_e = Y_s = Y_e = -1;
X1[0] = Y1[0] = -1;
mapp = null; mapp = new int[100][100];
Result.setFont(Result.getFont().deriveFont(20.0f));
Result.setText("<html><body>请连接节点并选取起点与终点<br/>操作方法:<br/>鼠标左键与右键分别选择当前需要连接的两个节点<br/>鼠标中键选择起点和终点<br/>注:任两节点间距离为1,搜索速度为1</body></html>");
repaint();
}
};
private ActionListener OP = new ActionListener() {
public void actionPerformed(ActionEvent e) {
st = 0;
st_b = 0;
st_r = 0;
start = -1;
end = -1;
X_s = X_e = Y_s = Y_e = -1;
X1[0] = Y1[0] = -1;
mapp = null; mapp = new int[100][100];
Result.setFont(Result.getFont().deriveFont(20.0f));
Result.setText("<html><body>请连接节点并选取起点与终点<br/>操作方法:<br/>鼠标左键与右键分别选择当前需要连接的两个节点<br/>鼠标中键选择起点和终点<br/>注:任两节点间距离为1,搜索速度为1</body></html>");
repaint();
Open();
repaint();
}
};
private ActionListener SA = new ActionListener() {
public void actionPerformed(ActionEvent e) {
Save();
}
};
private ActionListener DFS = new ActionListener() {
int min_step = 0x3f3f3f3f,all_time = 0;
int go_Arr[];
int st_Arr = 0;
public void actionPerformed(ActionEvent e) {
vst = null;
vst = new int[100];
st_b = 0;
st_r = 0;
go_Arr = null;
go_Arr = new int[100];
st_Arr = 0;
all_time = -1;
min_step = 0x3f3f3f3f;
go_Arr[st_Arr] = start;
st_Arr++;
dfs(start,0);
String res = "<html><body>dfs搜索结果如下:<br/>最短距离为:"+min_step+"<br/>搜索所需时间为:"+all_time+"<br/></body></html>";
Result.setFont(Result.getFont().deriveFont(24.0f));
Result.setText(res);
repaint();
}
void dfs(int x,int step) {
all_time ++;
if(x == end) {
min_step = Math.min(step,min_step);
mark_r();
}
if(step >= min_step) return;
vst[x] = 1;
for(int i = 0;i < 100;i++) {
if(i == x) continue;
if(mapp[x][i] == 1) {
if(vst[i] == 0) {
vst[i] = 1;
X1_b[st_b] = (x%10) * 60 + dx;
Y1_b[st_b] = (x/10) * 60 + dy;
X2_b[st_b] = (i%10) * 60 + dx;
Y2_b[st_b] = (i/10) * 60 + dy;
st_b ++;
go_Arr[st_Arr++] = i;
dfs(i,step + 1);
vst[i] = 0;
if(st_Arr>0) st_Arr--;
}
}
}
}
void mark_r() {
st_r = 0;
for(int i=0;i+1<st_Arr;i++) {
X1_r[st_r] = (go_Arr[i]%10) * 60 + dx;
Y1_r[st_r] = (go_Arr[i]/10) * 60 + dy;
X2_r[st_r] = (go_Arr[i+1]%10) * 60 + dx;
Y2_r[st_r] = (go_Arr[i+1]/10) * 60 + dy;
st_r ++ ;
}
}
};
private ActionListener BFS = new ActionListener() {
int all_time = 0;
Queue<Integer> queue;
boolean ok;
int Father[],step[];
public void actionPerformed(ActionEvent e) {
all_time = 0;
st_b = st_r = 0;
vst = null;
vst = new int[100];
queue = new LinkedList<Integer>();
ok = true;
Father = new int[100];
step = new int[100];
queue.offer(start);
vst[start] = 1;
bfs();
mark_r();
String res = "<html><body>bfs搜索结果如下:<br/>最短距离为:"+step[end]+"<br/>搜索所需时间为:"+all_time+"<br/></body></html>";
Result.setFont(Result.getFont().deriveFont(24.0f));
Result.setText(res);
repaint();
}
void bfs() {
while(!queue.isEmpty() && ok) {
int x = queue.poll();
for(int i=0;i<100;i++) {
if(mapp[x][i] == 1) {
if(vst[i] == 0) {
vst[i] = 1;
all_time ++;
queue.offer(i);
Father[i] = x; step[i] = step[x] + 1;
X1_b[st_b] = (x%10) * 60 + dx;
Y1_b[st_b] = (x/10) * 60 + dy;
X2_b[st_b] = (i%10) * 60 + dx;
Y2_b[st_b] = (i/10) * 60 + dy;
st_b ++;
if(i == end) {
ok = false;
break;
}
}
}
}
}
}
void mark_r() {
int x1 = end, x2 = Father[end];
while(x1 != start) {
X1_r[st_r] = (x1 %10) * 60 + dx;
Y1_r[st_r] = (x1 /10) * 60 + dy;
X2_r[st_r] = (x2 %10) * 60 + dx;
Y2_r[st_r] = (x2 /10) * 60 + dy;
st_r ++ ;
x1 = x2;
x2 = Father[x1];
}
}
};
private ActionListener A_x = new ActionListener(){
int all_time = 0;
Queue<Integer> queue;
boolean ok;
int Father[],G[],F[],H[];
public void actionPerformed(ActionEvent e) {
all_time = 0;
st_b = st_r = 0;
vst = null;
vst = new int[100];
queue = new LinkedList<Integer>();
ok = true;
Father = new int[100];
G = new int[100];
F = new int[100];
H = new int[100];
queue.offer(start);
vst[start] = 1;
A__();
mark_r();
String res = "<html><body>A*搜索结果如下:<br/>最短距离为:"+G[end]+"<br/>搜索所需时间为:"+all_time+"<br/>代价为:"+F[end]+"</body></html>";
Result.setFont(Result.getFont().deriveFont(24.0f));
Result.setText(res);
repaint();
}
void A__() {
while(!queue.isEmpty() && ok) {
int x = queue.poll();
for(int i=0;i<100;i++) {
if(mapp[x][i] == 1) {
if(vst[i] == 0) {
vst[i] = 1;
all_time ++;
queue.offer(i);
Father[i] = x; G[i] = G[x] + 1;
H[i] = H[x] + 1;
F[i] = H[i] + G[i];
X1_b[st_b] = (x%10) * 60 + dx;
Y1_b[st_b] = (x/10) * 60 + dy;
X2_b[st_b] = (i%10) * 60 + dx;
Y2_b[st_b] = (i/10) * 60 + dy;
st_b ++;
if(i == end) {
ok = false;
break;
}
}
}
}
}
}
void mark_r() {
int x1 = end, x2 = Father[end];
while(x1 != start) {
X1_r[st_r] = (x1 %10) * 60 + dx;
Y1_r[st_r] = (x1 /10) * 60 + dy;
X2_r[st_r] = (x2 %10) * 60 + dx;
Y2_r[st_r] = (x2 /10) * 60 + dy;
st_r ++ ;
x1 = x2;
x2 = Father[x1];
}
}
};
public MainWnd() {
X1 = new int[100];X2 = new int[100];Y1 = new int[100];Y2 = new int[100];
X1[0] = Y1[0] = -1;
X1_b = new int[100];X2_b = new int[100];Y1_b = new int[100];Y2_b = new int[100];
X1_r = new int[100];X2_r = new int[100];Y1_r = new int[100];Y2_r = new int[100];
mapp = new int [100][100];
vst = new int [100];
setTitle("三种搜索及最短路查找");
setSize(900,800);
setVisible(true);
setLocation(450,180);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container cp = getContentPane();
cp.setLayout(null);
int X = 0 , Y = 0;
for(int i = 0; i < 100; i++) {
cp.add(btn[i]);
btn[i].setLocation(X,Y);
btn[i].setSize(20,20);
btn[i].addMouseListener(ML);
Y = Y + 60;
if((i+1)%10==0) {
X = X + 60;
Y = 0;
}
}
cp.add(OPEN);OPEN.setLocation(700,30);OPEN.setSize(150,50);OPEN.addActionListener(OP);
cp.add(SAVE);SAVE.setLocation(700,145);SAVE.setSize(150,50);SAVE.addActionListener(SA);
cp.add(Dfs);Dfs.setLocation(700,260);Dfs.setSize(150,50);Dfs.addActionListener(DFS);
cp.add(Bfs);Bfs.setLocation(700,375);Bfs.setSize(150,50);Bfs.addActionListener(BFS);
cp.add(A_);A_.setLocation(700,490);A_.setSize(150,50);A_.addActionListener(A_x);
cp.add(Return);Return.setLocation(700,605);Return.setSize(150,50);Return.addActionListener(RT);
OPEN.setFont(OPEN.getFont().deriveFont(16.0f));
SAVE.setFont(SAVE.getFont().deriveFont(16.0f));
Dfs.setFont(Result.getFont().deriveFont(16.0f));
Bfs.setFont(Bfs.getFont().deriveFont(16.0f));
A_.setFont(A_.getFont().deriveFont(16.0f));
Return.setFont(Return.getFont().deriveFont(16.0f));
cp.add(Result);Result.setLocation(30,550);Result.setText("<html><body>请连接节点并选取起点与终点<br/>操作方法:<br/>鼠标左键与右键分别选择当前需要连接的两个节点<br/>鼠标中键选择起点和终点<br/>注:任两节点间距离为1,搜索速度为1</body></html>");
Result.setSize(600,200);
Result.setFont(Result.getFont().deriveFont(20.0f));
cp.add(signature);
signature.setSize(300,100);
signature.setLocation(700,650);
cp.validate();
}
public void paint(Graphics g2) {
super.paint(g2);
Graphics2D g = (Graphics2D)g2;
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(3.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
for(int i = 0;i < st; i++) {
g.drawLine(X1[i], Y1[i], X2[i], Y2[i]);
g.fillOval(X1[i] - 9, Y1[i] - 5, 15, 15);
g.fillOval(X2[i] - 9, Y2[i] - 5, 15, 15);
}
g.setColor(Color.BLUE);
for(int i = 0;i < st_b; i++) {
g.drawLine(X1_b[i], Y1_b[i], X2_b[i], Y2_b[i]);
g.fillOval(X1_b[i] - 9, Y1_b[i] - 5, 15, 15);
g.fillOval(X2_b[i] - 9, Y2_b[i] - 5, 15, 15);
}
g.setColor(Color.RED);
for(int i = 0;i < st_r; i++) {
g.drawLine(X1_r[i], Y1_r[i], X2_r[i], Y2_r[i]);
g.fillOval(X1_r[i] - 9, Y1_r[i] - 5, 15, 15);
g.fillOval(X2_r[i] - 9, Y2_r[i] - 5, 15, 15);
}
g.setFont(g.getFont().deriveFont(18.0f));
if(X_s != 0 && Y_s !=-1) {
g.drawString("start", X_s + 10, Y_s + 20);
g.fillOval(X_s - 12, Y_s - 8, 22, 22);
}
if(X_e != 0 && Y_e != -1) {
g.drawString("end", X_e + 10, Y_e + 20);
g.fillOval(X_e - 12, Y_e - 8, 22, 22);
}
}
}
其中默认任意直接相连两点间距离为单位距离,则A* 算法估价函数H(n)恒唯一,则G(n)所占比例很大,A* 很接近于BFS,各位也可以将两点间距离改为实际距离然后重新A* 。
若有不足还望指正