引言
想象一下这样的场景:一架无人机正在执行侦查任务,它需要扫描周围环境,记录各个入侵者的位置,并计算这些点之间的距离关系,便于攻击等。这正是我们项目要解决的问题,而图数据结构就是解决这类问题的完美选择。
什么是图?
1.图的基本概念
图是由顶点(Vertex) 和**边(Edge)**组成的非线性数据结构。在我们的雷达扫描项目中:
-
顶点:每个雷达扫描到的目标点(如X, A, B, C等)
-
边:两个目标点之间的连接,带有距离(权重)信息
数学上,一个图G可以表示为:G = (V, E),其中:
-
V是顶点的集合 -
E是边的集合
2.图的类型
在我们的项目中,我们构建的是无向加权图:
-
无向:边没有方向,A到B的距离等于B到A的距离
-
加权:每条边都有一个权重值(距离)
我们的图结构示意图:

如何构建图?
1.基础数据结构设计
首先,我们需要定义图的基本组成元素。在Graph类中,我们设计了两个核心类:
Edge类:表示图的边
class Edge {
String name; // 目标节点的名称
int distance; // 到目标节点的距离(权重)
public Edge(String name, int distance) {
this.name = name;
this.distance = distance;
}
}
每个边记录
目标节点名称和距离适合表示雷达扫描中"从当前点到目标点"的关系
MPoint类:表示图的顶点
class MPoint {
String name; // 节点的名称
int x, y; // 节点的坐标位置
public MPoint(String name, int x, int y) {
this.name = name;
this.x = x;
this.y = y;
}
}
除了节点名称,还存储了
坐标位置这反映了雷达扫描的实际需求:知道目标点的具体位置
坐标信息可以用于计算两点之间的直线距离
2.图的核心数据结构
在Graph类中,我们使用邻接表来表示图:
public class Graph {
Map<String, List<Edge>> graph = new HashMap<>();
// 其他方法...
}
2.1 而什么是邻接表呢?
邻接表是图的一种链式存储结构,它通过为图中的每个顶点建立一个链表,来存储该顶点的所有邻接点(相邻顶点)。
在我们的代码中,邻接表通过以下方式实现:
Map<String, List<Edge>> graph = new HashMap<>();
数据结构解析:
-
键(Key):顶点的名称(String类型)
-
值(Value):该顶点的邻接边列表(List<Edge>类型)
-
Edge对象:存储目标顶点名称和距离权重
2.2 实际存储实例
假设我们有如下雷达扫描图:
顶点:X, A, B, C
边:X-A(150), X-B(200), X-C(180), A-B(100), A-C(120)
在内存中,邻接表的存储结构如下:
graph = {
"X": [Edge("A",150), Edge("B",200), Edge("C",180)],
"A": [Edge("X",150), Edge("B",100), Edge("C",120)],
"B": [Edge("X",200), Edge("A",100)],
"C": [Edge("X",180), Edge("A",120)]
}
核心方法分析
1.add方法:添加顶点
public void add(String name) {
List<Edge> edgeList = new ArrayList<>();
graph.put(name, edgeList);
}
功能:在图中添加一个新的顶点
创建顶点的邻接边列表
为后续添加边做准备
确保每个顶点都有对应的数据结构
2.scan方法:添加边
public void scan(String name, String to, int distance) {
// 1. 检查两个顶点是否存在,不存在则创建
if (!graph.containsKey(name)) {
add(name);
}
if (!graph.containsKey(to)) {
add(to);
}
// 2. 创建从name到to的边
Edge edge = new Edge(to, distance);
List<Edge> edgeList = graph.get(name);
// 3. 检查边是否已存在(避免重复)
for (int i = 0; i < edgeList.size(); i++) {
Edge e1 = edgeList.get(i);
if(e1.name.equals(edge.name)) {
return; // 边已存在,直接返回
}
}
edgeList.add(edge);
// 4. 创建反向边(无向图)
Edge edge1 = new Edge(name, distance);
List<Edge> edgeList1 = graph.get(to);
// 5. 同样检查反向边是否已存在
for (int i = 0; i < edgeList1.size(); i++) {
Edge e1 = edgeList1.get(i);
if(e1.name.equals(edge1.name)) {
return;
}
}
edgeList1.add(edge1);
}
自动顶点创建:如果顶点不存在,自动创建。这简化了调用逻辑。
重复边检查:避免图中出现重复的边,保证数据的一致性。
双向边添加:因为我们构建的是无向图,所以需要添加两个方向的边。
3.showInfo方法:显示图信息
public void showInfo() {
Set<String> keySet = graph.keySet();
System.out.println("=== 雷达扫描图 ===");
for (String key : keySet) {
List<Edge> edges = graph.get(key);
System.out.println("顶点 -> " + key);
// 根据距离排序(冒泡排序)
for (int i = 0; i < edges.size(); i++) {
for (int j = 0; j < edges.size()-1; j++) {
Edge e1 = edges.get(j);
Edge e2 = edges.get(j+1);
if(e1.distance > e2.distance) {
edges.set(j, e2);
edges.set(j+1, e1);
}
}
}
// 打印排序后的边
for (Edge edge : edges) {
System.out.print(edge.name + "(" + edge.distance + ") ");
}
System.out.println("---------");
}
}
信息展示:清晰地展示图的完整结构
距离排序:使用冒泡排序将边按距离从小到大排列
可读性:格式化输出,便于调试和理解图结构
交互系统设计
1.GraphRobot类:用户界面
public class GraphRobot {
public void showUI() {
JFrame jf = new JFrame();
jf.setTitle("图算法应用");
jf.setSize(800, 1000);
// ... 界面初始化代码
// 创建起点标记
g.fillRect(200, 700, 100, 100);
}
}
2.RobotListener类:监听器
public class RobotListener extends MouseAdapter implements MouseListener {
Graphics g;
final int x = 200;
final int y = 700;
char name = 'A';
Graph scanGraph = new Graph();
ArrayList<MPoint> pointList = new ArrayList<>();
public RobotListener() {
scanGraph.add("X");
pointList.add(new MPoint("X", 200, 700));
}
@Override
public void mousePressed(MouseEvent e) {
int x1 = e.getX();
int y1 = e.getY();
// 绘制新点
g.setColor(Color.GREEN);
g.fillOval(x1, y1, 50, 50);
// 计算到起点的距离
int distance = (int) Math.sqrt(Math.pow(x1-x, 2) + Math.pow(y1-y, 2));
scanGraph.scan("X", name + "", distance);
// 计算与其他所有点的距离
for (int i = 0; i < pointList.size(); i++) {
MPoint p = pointList.get(i);
int distance1 = (int) Math.sqrt(Math.pow(x1 - p.x, 2) + Math.pow(y1 - p.y, 2));
scanGraph.scan(p.name, name + "", distance1);
}
// 记录新点
MPoint p1 = new MPoint(name + "", x1, y1);
pointList.add(p1);
name++;
// 显示图信息
scanGraph.showInfo();
}
}
交互系统展示

(可以看到这样的交互性和观感太差了,距离啥的只能通过输出框展现,数据冗杂且阅读性差)
因此我们进行以下两个方面的优化:
- 1.在UI界面右侧新增距离显示功能,将距离算法的结果用滑杆的方式直观表达。
- 2.将距离黑色方块最短距离的圆的颜色由绿变红,直观迅速模拟雷达扫描。
项目优化
那么根据以上优化思路:
1.第一步:修改 GraphRobot类中的主界面(新增滑杆组件)
// 创建距离显示面板(滑杆+标签)
JPanel sliderPanel = new JPanel();
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));
JLabel distanceLabel = new JLabel("当前距离:0");
//设置字体大小
distanceLabel.setFont(new Font("宋体", Font.PLAIN, 20));
// 滑杆范围0-1000(适配窗口尺寸800x1000),初始值0
JSlider distanceSlider = new JSlider(JSlider.VERTICAL, 0, 800, 0);
distanceSlider.setMajorTickSpacing(100); // 主刻度间隔
distanceSlider.setMinorTickSpacing(50); // 副刻度间隔
distanceSlider.setPaintTicks(true); // 显示刻度
distanceSlider.setPaintLabels(true); // 显示刻度值
sliderPanel.add(distanceLabel);
sliderPanel.add(distanceSlider);
// 将滑杆面板添加到窗口右侧
jf.add(sliderPanel, BorderLayout.EAST);

- 在
GraphRobot中新增滑杆(JSlider)和标签组件,布局改为BorderLayout将滑杆放在右侧;- 同时将滑杆和标签传给
RobotListener,在鼠标点击时更新滑杆值和标签文本,直观显示当前点击点到初始点 X 的距离。
2.第二步:修改 RobotListener核心逻辑(让最短距离圆变红)
//更新滑杆和距离标签
distanceSlider.setValue(distance);
distanceLabel.setText("距离:"+distance);
//其他方法......//
//重绘所有节点,最短距离变红
repaintPoints();
}
private void repaintPoints() {
// 1. 找到最短距离的节点(相对于初始点X)
MPoint shortestPoint = findShortestDistancePoint();
// 2. 重新绘制所有节点
for (MPoint p : pointList) {
if (p.equals(shortestPoint)) {
// 最短距离节点:红色
g.setColor(Color.RED);
} else {
// 其他节点:绿色
g.setColor(Color.GREEN);
}
// 绘制圆形节点(尺寸50x50,和原有逻辑一致)
g.fillOval(p.x, p.y, 50, 50);
}
// 重新绘制初始矩形(避免被覆盖)
g.setColor(Color.BLACK);
g.fillRect(200,700,100,100);
}
// 查找距离初始点X最短的节点
private MPoint findShortestDistancePoint() {
MPoint shortestPoint = pointList.get(0); // 初始化为X节点
int minDistance = Integer.MAX_VALUE;
for (MPoint p : pointList) {
// 跳过X节点本身
if (p.name.equals("X")) {
continue;
}
// 计算当前节点到X的距离
int distance = (int) Math.sqrt(Math.pow(p.x - x, 2) + Math.pow(p.y - y, 2));
if (distance < minDistance) {
minDistance = distance;
shortestPoint = p;
}
}
return shortestPoint;
}
}
- 新增
findShortestDistancePoint()方法,遍历所有节点找到距离初始点 X 最短的节点(原有逻辑)- 新增
repaintPoints()方法,重新绘制所有节点,最短距离节点用红色,其余保持绿色;- 在鼠标点击后调用
repaintPoints(),确保每次新增节点后立即更新颜色。
3.保留原有逻辑的关键点
- 未修改
Graph类的核心图结构和算法;- 未改变原有鼠标点击绘制绿色圆的基础逻辑,仅新增了最短距离节点的颜色区分;
- 滑杆组件为新增,未破坏原有窗口布局的核心逻辑。
项目优化后展示



结语
图数据结构是计算机科学中最美、最强大的数据结构之一。图结构就像一张智慧的网,连接着数据与现实世界,帮助我们洞察复杂的关系和模式。通过这个雷达扫描项目,我不仅学会了如何实现图,更重要的是理解了好的数据结构不是目的,而是解决问题的手段。当我们面对复杂的关系网络时,图数据结构就是我们最有力的工具之一。