java图形化实现图的遍历(DFS,BFS)深度搜索与广度搜索算法



功能:可以双击一个节点从任意节点遍历,默认从头节点开始遍历,两种遍历方式,可以拖拽节点任意
移动节点的位置。

没有实现:删除功能,鼠标也可以实现,但是加入键盘监听想让D和delete键来删除,但是加完节点和关系
以后键盘监听就不好使了!!!求指导!!!!!

代码如下:
Graphic类:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Graphic extends JFrame implements ActionListener, ItemListener {
MyPanel mp = null;
JPanel jp = null;
JButton add = null;
JButton add1 = null;
JButton tra = null;
JButton cancel = null;
JLabel jl = null;
JComboBox jList = null;// 下拉列表
String[] travel = { “DFS”, “BFS” };
JLabel jlb = null;// 装饰标签
public Graphic() {
mp = new MyPanel();
jl = new JLabel(“遍历方式”);
jList = new JComboBox(travel);
jList.addItemListener(this);
add = new JButton(“加节点”);
add.addActionListener(this);
add.setActionCommand(“a”);
add1 = new JButton(“加关系”);
add1.addActionListener(this);
add1.setActionCommand(“aa”);
jlb = new JLabel(” “);// 让布局好看点!!
tra = new JButton(“确认遍历”);
tra.addActionListener(this);
tra.setActionCommand(“b”);
cancel = new JButton(“取消”);
cancel.addActionListener(this);
cancel.setActionCommand(“c”);
jp = new JPanel();
jp.add(add);
jp.add(add1);
jp.add(cancel);
jp.add(jlb);
jp.add(jl);
jp.add(jList);
jp.add(tra);
mp.addMouseListener(mp);
mp.addMouseMotionListener(mp);
mp.addKeyListener(mp);
jp.addKeyListener(mp);
this.setFocusable(true);
this.addKeyListener(mp);
this.add(mp);
this.addKeyListener(mp);
this.add(jp, BorderLayout.SOUTH);
this.setSize(600, 500);
this.setLocation(350, 150);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args) {
new Graphic();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(“a”)) {// 加节点
mp.isTravel = false;
mp.isAdd1 = false;
mp.isAdd = true;
} else if (e.getActionCommand().equals(“aa”)) {// 加关系
mp.isTravel = false;
mp.isAdd = false;
mp.isAdd1 = true;
} else if (e.getActionCommand().equals(“c”)) {// 取消
mp.isTravel = true;
mp.isAdd = false;
mp.isAdd1 = false;
} else if (e.getActionCommand().equals(“b”)) {// 确认遍历
mp.isTravel = true;
String s = jList.getSelectedItem().toString();// 得到选项
if (s == “DFS”) {
mp.Dfs();
} else if (s == “BFS”) {
mp.Bfs();
}
for (int i = 0; i < mp.nodes.size(); i++) {// 遍历后把正在访问均重新变为空
Node node = mp.nodes.get(i);
node.isVisit = false;
}
}
}
@Override
public void itemStateChanged(ItemEvent e) {// 监听遍历状态改变时候,将正在遍历设置回假
for (int i = 0; i < mp.nodes.size(); i++) {
Node node = mp.nodes.get(i);
node.isVisit = false;
}
mp.repaint();
}
}

MyPanel类:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.tools.JavaCompiler;
import java.util.*;
class MyPanel extends JPanel implements MouseListener, MouseMotionListener,
KeyListener {
int x, y;// 节点左上角坐标
int x1, y1;// 画直线时候另一个点
int width = 36;// 节点宽高
int height = 36;
int total = 1000;// 总的节点个数
int X[][] = new int[total][total];// 邻接矩阵,判断x方向是否相连
int Y[][] = new int[total][total];// 邻接矩阵,判断y方向是否相连
boolean visit[][] = new boolean[total][total];// 判断结点是否访问过
boolean isOther = false;// 画关系时候判断是否找另一个点
boolean isTravel = false;// 判断是否正在遍历
Vector<Node> nodes = new Vector<Node>();// 节点集合
Vector<LineNode> lines = new Vector<LineNode>();// 直线集合
LinkedList<Node> lists = new LinkedList<Node>();// bfs时候用
Node start = new Node(0, 0);// 遍历初始节点
boolean isAdd = false;// 判断是否为加节点
boolean isAdd1 = false;// 判断是否为加关系
boolean isFirst = true;// 拖拽时候用
boolean canDelete = false;// 双击才可删除
int xx, yy, ii;// 拖拽时候用
Vector<Choose> choose = new Vector<Choose>();// 拖拽时候用,用来把所有与拖拽节点有关系的直线放进来
public MyPanel() {// 初始化
for (int i = 0; i < total; i++) {
for (int j = 0; j < total; j++) {
X[i][j] = 0;
Y[i][j] = 0;
visit[i][j] = false;
}
}
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, 600, 450);
for (int i = 0; i < nodes.size(); i++) {// 画节点
Node node = nodes.get(i);
this.draw(g, node.x, node.y);
if (node.isVisit) {// 如果节点正在访问,则图上黄色
g.setColor(Color.YELLOW);
g.fill3DRect(node.x, node.y, width, height, false);
// System.out.println(“!!!!!!”);
// node.isVisit = false;
}
}
for (int i = 0; i < lines.size(); i++) {// 画直线
LineNode line = lines.get(i);
this.draw1(g, line.x1, line.y1, line.x, line.y);
}
if (isAdd) { // 如果是加节点则不断绘制矩形
this.draw(g, x, y);
}
if (isAdd1) {
if (isOther) {// 如果是加关系,则当绘制直线第二个时候不断绘制这条直线
this.draw1(g, x1, y1, x, y);
}
}
}
public void draw(Graphics g, int x, int y) {// 画节点
g.setColor(Color.BLACK);
g.draw3DRect(x, y, width, height, false);
}
public void draw1(Graphics g, int x1, int y1, int x, int y) {// 画关系
g.setColor(Color.RED);
g.drawLine(x1, y1, x, y);
}
@Override
public void mouseClicked(MouseEvent e) {// 监听鼠标单击事件
// TODO Auto-generated method stub
if (isAdd) { // 如果加节点
Node node = new Node(e.getX(), e.getY());
nodes.add(node);
start = nodes.get(0);// 初始节点默认赋值为第一个节点
} else if (isAdd1) {// 加关系
if (isOther == false) {// 绘制直线第一个点的话
x1 = e.getX();
y1 = e.getY();
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
if ((x1 >= node.x && x1 <= node.x + width)// 找到在哪个节点里
&& (y1 >= node.y && y1 <= node.y + height)) {
x1 = node.x + width / 2;// 将关系直线的两个端点均赋值为节点的中心
y1 = node.y + height / 2;
break;
}
}
isOther = true;
} else {// 绘制直线第二个点
x = e.getX();
y = e.getY();
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
if ((x >= node.x && x <= node.x + width)// 找到在哪个节点里
&& (y >= node.y && y <= node.y + height)) {
x = node.x + width / 2;
y = node.y + height / 2;
break;
}
}
LineNode line = new LineNode(x1, y1, x, y);// 新建一条直线,并加入到直线集合中
lines.add(line);
X[x][x1] = 1; // 用于表示两个节点之间有连接,用于后面遍历
X[x1][x] = 1;
Y[y][y1] = 1;
Y[y1][y] = 1;
isOther = false;
}
}
if (e.getClickCount() == 2) {// 双击将该节点颜色变为黄色,可以从当前节点遍历
x = e.getX();
y = e.getY();
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
node.isVisit = false;
if ((x >= node.x && x <= node.x + width)// 找到在哪个节点里
&& (y >= node.y && y <= node.y + height)) {
node.isVisit = true;
Graphics g = this.getGraphics();// 这样得到画笔
g.setColor(Color.YELLOW);
g.fill3DRect(node.x, node.y, width, height, false);
start = node;// 将初始节点赋值为当前
break;
}
}
this.repaint();
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
this.isFirst = true;// 设置回真
}
@Override
public void mouseDragged(MouseEvent e) {// 监听拖拽事件
// TODO Auto-generated method stub
x = e.getX();
y = e.getY();
if (this.isFirst) {// 设置这个变量目的是完成一次整个拖拽过程只判断一次在哪个节点里
for (ii = 0; ii < nodes.size(); ii++) {
Node node = nodes.get(ii);
if ((x >= node.x && x <= node.x + width)// 找到在哪个节点里
&& (y >= node.y && y <= node.y + height)) {
xx = x – node.x;// 把当前间距分别算出来
yy = y – node.y;
choose.clear();// 每一次判断节点的时候要新清空
for (int jj = 0; jj < lines.size(); jj++) {
LineNode l = lines.get(jj);
if ((node.x + width / 2) == l.x1
&& (node.y + height / 2) == l.y1) {
Choose c = new Choose(jj, false);// 如果该点是直线第一个定的点即为x1,y1,则为假
choose.add(c);
int x4 = l.x;
int y4 = l.y;
int x5 = l.x1;
int y5 = l.y1;
X[x4][x5] = 0; // 重新刷新连接关系
X[x5][x4] = 0;
Y[y4][y5] = 0;
Y[y5][y4] = 0;
}
if ((node.x + width / 2) == l.x
&& (node.y + height / 2) == l.y) {// 如果该点是直线,第二个点
Choose c = new Choose(jj, true);
choose.add(c);
int x4 = l.x;
int y4 = l.y;
int x5 = l.x1;
int y5 = l.y1;
X[x4][x5] = 0; // 重新刷新连接关系
X[x5][x4] = 0;
Y[y4][y5] = 0;
Y[y5][y4] = 0;
}
}
this.isFirst = false;// 只判断一次在哪个节点里,知道松开以后再重新判断
break;
}
}
}
if (ii != nodes.size()) {// 拖动点再节点里才会考虑
nodes.get(ii).x = x – xx;// 不断将node的坐标值重复成拖拽过后的坐标值
nodes.get(ii).y = y – yy;
for (int i = 0; i < choose.size(); i++) {// 将与节点有关系的直线也不断赋值,直线会跟着走
Choose c = choose.get(i);
if (c.isX) {
lines.get(c.index).x = nodes.get(ii).x + width / 2;
lines.get(c.index).y = nodes.get(ii).y + height / 2;
} else {
lines.get(c.index).x1 = nodes.get(ii).x + width / 2;
lines.get(c.index).y1 = nodes.get(ii).y + height / 2;
}
int x4 = lines.get(c.index).x;
int y4 = lines.get(c.index).y;
int x5 = lines.get(c.index).x1;
int y5 = lines.get(c.index).y1;
X[x4][x5] = 1; // 重新刷新连接关系
X[x5][x4] = 1;
Y[y4][y5] = 1;
Y[y5][y4] = 1;
}
this.repaint();
}
}
@Override
public void mouseMoved(MouseEvent e) {// 监听鼠标移动事件,不断的重绘
if (this.isTravel == false) {
x = e.getX();
y = e.getY();
this.repaint();
}
}
public void delete() {// 删除函数,目前监听进不来!!!
for (ii = 0; ii < nodes.size(); ii++) {
Node node = nodes.get(ii);
if ((x >= node.x && x <= node.x + width)// 找到在哪个节点里
&& (y >= node.y && y <= node.y + height)) {
for (int jj = 0; jj < lines.size(); jj++) {
LineNode l = lines.get(jj);
if ((node.x + width / 2) == l.x1
&& (node.y + height / 2) == l.y1) {
lines.remove(l);
}
if ((node.x + width / 2) == l.x
&& (node.y + height / 2) == l.y) {// 如果该点是直线,第二个点
lines.remove(l);
}
}
nodes.remove(node);
break;
}
}
this.repaint();
}
public void dfs(Node node) {// 深度优先搜索
int x2 = node.x + width / 2;// 都是判断中间点
int y2 = node.y + height / 2;
visit[x2][y2] = true;
node.isVisit = true;// 将正在访问的点变为黄色
this.repaint();// 不会起作用,会循环完一次重绘,因此要强制更新
this.update(this.getGraphics());// 循环里要强制更新。。画笔是this.getGraphics()得到
try { // 睡眠1.5秒
Thread.currentThread().sleep(1500);
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println(x2 + ” ” + y2);
for (int j = 0; j < nodes.size(); j++) {
Node node1 = nodes.get(j);
int x3 = node1.x + width / 2;
int y3 = node1.y + height / 2;
if ((X[x2][x3] != 0) && (Y[y2][y3] != 0 && !visit[x3][y3])) {
dfs(node1);
}
}
}
public void Dfs() {
for (int i = 0; i < total; i++) {
for (int j = 0; j < total; j++) {
visit[i][j] = false;
}
}
dfs(start);// 从当前选中节点开始遍历
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
if (node != start) {// 选中节点就不用遍历了
if (!visit[node.x + width / 2][node.y + height / 2]) {
dfs(node);
}
}
}
}
public void bfs(Node node) {// 广度优先搜索
int x2 = node.x + width / 2;
int y2 = node.y + height / 2;
visit[x2][y2] = true;
node.isVisit = true;
lists.push(node);
this.repaint();
this.update(this.getGraphics());// 循环里要强制更新。。画笔是this.getGraphics()得到
try {
Thread.currentThread().sleep(1500);
} catch (Exception e) {
e.printStackTrace();
}
while (lists.size() > 0) {
Node node1 = lists.peekFirst();
lists.poll();// 这句才是删除队首元素
int x3 = node1.x + width / 2;
int y3 = node1.y + height / 2;
for (int k = 0; k < nodes.size(); k++) {
Node node2 = nodes.get(k);
int x4 = node2.x + width / 2;
int y4 = node2.y + height / 2;
if ((X[x3][x4] != 0) && (Y[y3][y4] != 0 && !visit[x4][y4])) {
node2.isVisit = true;
visit[x4][y4] = true;
lists.addLast(node2);// 这才是放到队尾
// lists.push(node2);//LinkedList的push函数默认放在队首!!!!!
this.repaint();
this.update(this.getGraphics());// 循环里要强制更新。。画笔是this.getGraphics()得到
try {
Thread.currentThread().sleep(1500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
this.repaint();
}
public void Bfs() {
lists.clear();
for (int i = 0; i < total; i++) {
for (int j = 0; j < total; j++) {
visit[i][j] = false;
}
}
bfs(start);
}
@Override
public void keyPressed(KeyEvent ee) {// 加完节点就会失去焦点!!!键盘不监听!!!
int temp = ee.getKeyCode();
if (temp == KeyEvent.VK_D || temp == KeyEvent.VK_DELETE) {
this.delete();
}
}
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}

Node类:

import javax.swing.*;
public class Node {
int x, y;// 节点左上角坐标
String s = “”;// 节点信息
boolean isVisit = false;
Node(int x, int y) {
this.x = x;
this.y = y;
}
}
class LineNode {// 直线节点
int x, y, x1, y1;
LineNode(int x, int y, int x1, int y1) {
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
}
}
class Choose {// 拖拽时候用,用来把一个节点内所有关联直线放进来
int index;
boolean isX;
public Choose(int index, boolean isX) {
this.index = index;
this.isX = isX;
}
}