当前位置: 首页 > news >正文

使用JDK11标准 实现 图数据结构的增删查改遍历 可视化程序

在这里插入图片描述

package com.alvin.datastruct;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;// 顶点类
class Vertex {private int id;private int x, y;public static final int DIAMETER = 40; // 改为public static finalprivate Color color = Color.LIGHT_GRAY;public Vertex(int id, int x, int y) {this.id = id;this.x = x;this.y = y;}public int getId() { return id; }public int getX() { return x; }public int getY() { return y; }public void setPosition(int x, int y) { this.x = x; this.y = y; }public Color getColor() { return color; }public void setColor(Color color) { this.color = color; }public boolean contains(int x, int y) {int centerX = this.x + DIAMETER/2;int centerY = this.y + DIAMETER/2;return Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)) <= DIAMETER/2;}public void draw(Graphics g) {g.setColor(color);g.fillOval(x, y, DIAMETER, DIAMETER);g.setColor(Color.BLACK);g.drawOval(x, y, DIAMETER, DIAMETER);g.setFont(new Font("Arial", Font.BOLD, 16));FontMetrics fm = g.getFontMetrics();String text = String.valueOf(id);int textX = x + (DIAMETER - fm.stringWidth(text)) / 2;int textY = y + (DIAMETER - fm.getHeight()) / 2 + fm.getAscent();g.drawString(text, textX, textY);}
}// 边类
class Edge {private Vertex from, to;private int weight;private Color color = Color.BLACK;public Edge(Vertex from, Vertex to, int weight) {this.from = from;this.to = to;this.weight = weight;}public Vertex getFrom() { return from; }public Vertex getTo() { return to; }public int getWeight() { return weight; }public void setWeight(int weight) { this.weight = weight; }public Color getColor() { return color; }public void setColor(Color color) { this.color = color; }public void draw(Graphics g) {int x1 = from.getX() + Vertex.DIAMETER/2;int y1 = from.getY() + Vertex.DIAMETER/2;int x2 = to.getX() + Vertex.DIAMETER/2;int y2 = to.getY() + Vertex.DIAMETER/2;g.setColor(color);g.drawLine(x1, y1, x2, y2);// 绘制权重int midX = (x1 + x2) / 2;int midY = (y1 + y2) / 2;g.setFont(new Font("Arial", Font.PLAIN, 12));g.drawString(String.valueOf(weight), midX, midY);// 绘制箭头drawArrow(g, x1, y1, x2, y2);}private void drawArrow(Graphics g, int x1, int y1, int x2, int y2) {double angle = Math.atan2(y2 - y1, x2 - x1);int arrowSize = 10;int x3 = (int) (x2 - arrowSize * Math.cos(angle - Math.PI / 6));int y3 = (int) (y2 - arrowSize * Math.sin(angle - Math.PI / 6));int x4 = (int) (x2 - arrowSize * Math.cos(angle + Math.PI / 6));int y4 = (int) (y2 - arrowSize * Math.sin(angle + Math.PI / 6));g.drawLine(x2, y2, x3, y3);g.drawLine(x2, y2, x4, y4);}
}// 图类
class Graph {private Map<Integer, Vertex> vertices = new HashMap<>();private List<Edge> edges = new ArrayList<>();private boolean directed = false;public void addVertex(int id, int x, int y) {vertices.put(id, new Vertex(id, x, y));}public void removeVertex(int id) {Vertex v = vertices.remove(id);if (v != null) {edges.removeIf(edge -> edge.getFrom().getId() == id || edge.getTo().getId() == id);}}public void addEdge(int fromId, int toId, int weight) {Vertex from = vertices.get(fromId);Vertex to = vertices.get(toId);if (from != null && to != null) {edges.add(new Edge(from, to, weight));if (!directed) {edges.add(new Edge(to, from, weight));}}}public void removeEdge(int fromId, int toId) {edges.removeIf(edge ->(edge.getFrom().getId() == fromId && edge.getTo().getId() == toId) ||(!directed && edge.getFrom().getId() == toId && edge.getTo().getId() == fromId));}public Vertex getVertexAt(int x, int y) {for (Vertex v : vertices.values()) {if (v.contains(x, y)) {return v;}}return null;}public void draw(Graphics g) {// 先绘制边for (Edge edge : edges) {edge.draw(g);}// 再绘制顶点for (Vertex vertex : vertices.values()) {vertex.draw(g);}}public void clearColors() {for (Vertex v : vertices.values()) {v.setColor(Color.LIGHT_GRAY);}for (Edge e : edges) {e.setColor(Color.BLACK);}}public void setDirected(boolean directed) {this.directed = directed;}public boolean isDirected() {return directed;}public Map<Integer, Vertex> getVertices() {return vertices;}public List<Edge> getEdges() {return edges;}public int getNextAvailableId() {int maxId = 0;for (int id : vertices.keySet()) {if (id > maxId) {maxId = id;}}return maxId + 1;}// 深度优先遍历public List<Integer> dfs(int startId) {List<Integer> result = new ArrayList<>();Set<Integer> visited = new HashSet<>();Vertex start = vertices.get(startId);if (start != null) {dfsHelper(start, visited, result);}return result;}private void dfsHelper(Vertex current, Set<Integer> visited, List<Integer> result) {if (visited.contains(current.getId())) return;visited.add(current.getId());result.add(current.getId());for (Edge edge : edges) {if (edge.getFrom().getId() == current.getId()) {dfsHelper(edge.getTo(), visited, result);}}}// 广度优先遍历public List<Integer> bfs(int startId) {List<Integer> result = new ArrayList<>();Set<Integer> visited = new HashSet<>();Queue<Vertex> queue = new LinkedList<>();Vertex start = vertices.get(startId);if (start == null) return result;queue.add(start);visited.add(startId);while (!queue.isEmpty()) {Vertex current = queue.poll();result.add(current.getId());for (Edge edge : edges) {Vertex neighbor = edge.getTo();if (!visited.contains(neighbor.getId())) {visited.add(neighbor.getId());queue.add(neighbor);}}}return result;}// 查找顶点public boolean containsVertex(int id) {return vertices.containsKey(id);}// 查找边public boolean containsEdge(int fromId, int toId) {for (Edge edge : edges) {if (edge.getFrom().getId() == fromId && edge.getTo().getId() == toId) {return true;}}return false;}// 获取边的权重public int getEdgeWeight(int fromId, int toId) {for (Edge edge : edges) {if (edge.getFrom().getId() == fromId && edge.getTo().getId() == toId) {return edge.getWeight();}}return -1; // 表示边不存在}// 更新边的权重public void updateEdgeWeight(int fromId, int toId, int newWeight) {for (Edge edge : edges) {if (edge.getFrom().getId() == fromId && edge.getTo().getId() == toId) {edge.setWeight(newWeight);// 如果是无向图,还需要更新反向边if (!directed) {for (Edge reverseEdge : edges) {if (reverseEdge.getFrom().getId() == toId && reverseEdge.getTo().getId() == fromId) {reverseEdge.setWeight(newWeight);break;}}}break;}}}
}// 主界面
public class GraphVisualization extends JFrame {private Graph graph = new Graph();private JPanel canvas;private JTextField vertexIdField, fromField, toField, weightField;private JButton addVertexBtn, removeVertexBtn, addEdgeBtn, removeEdgeBtn;private JButton dfsBtn, bfsBtn, clearBtn, updateWeightBtn, generateRandomBtn;private JCheckBox directedCheckbox;private Vertex selectedVertex = null;private Random random = new Random();public GraphVisualization() {setTitle("图数据结构可视化");setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setSize(1000, 700);setLayout(new BorderLayout());// 创建画布canvas = new JPanel() {@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);graph.draw(g);}};canvas.setBackground(Color.WHITE);canvas.addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {if (SwingUtilities.isRightMouseButton(e)) {Vertex vertex = graph.getVertexAt(e.getX(), e.getY());if (vertex != null) {graph.removeVertex(vertex.getId());canvas.repaint();}} else {if (selectedVertex == null) {int nextId = graph.getNextAvailableId();graph.addVertex(nextId, e.getX() - Vertex.DIAMETER/2, e.getY() - Vertex.DIAMETER/2);canvas.repaint();} else {Vertex toVertex = graph.getVertexAt(e.getX(), e.getY());if (toVertex != null && toVertex != selectedVertex) {String weightStr = JOptionPane.showInputDialog("请输入边的权重:", "1");try {int weight = Integer.parseInt(weightStr);graph.addEdge(selectedVertex.getId(), toVertex.getId(), weight);canvas.repaint();} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(GraphVisualization.this,"权重必须是整数", "错误", JOptionPane.ERROR_MESSAGE);}}}}}@Overridepublic void mousePressed(MouseEvent e) {selectedVertex = graph.getVertexAt(e.getX(), e.getY());}@Overridepublic void mouseReleased(MouseEvent e) {selectedVertex = null;}});canvas.addMouseMotionListener(new MouseMotionAdapter() {@Overridepublic void mouseDragged(MouseEvent e) {if (selectedVertex != null) {selectedVertex.setPosition(e.getX() - Vertex.DIAMETER/2, e.getY() - Vertex.DIAMETER/2);canvas.repaint();}}});add(canvas, BorderLayout.CENTER);// 创建控制面板JPanel controlPanel = new JPanel();controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));// 顶点操作面板JPanel vertexPanel = new JPanel();vertexPanel.setBorder(BorderFactory.createTitledBorder("顶点操作"));vertexIdField = new JTextField(5);addVertexBtn = new JButton("添加顶点");removeVertexBtn = new JButton("删除顶点");generateRandomBtn = new JButton("随机生成10个顶点");addVertexBtn.addActionListener(e -> {try {int id = Integer.parseInt(vertexIdField.getText());if (graph.containsVertex(id)) {JOptionPane.showMessageDialog(this, "顶点ID已存在", "错误", JOptionPane.ERROR_MESSAGE);} else {graph.addVertex(id, 100, 100);canvas.repaint();}} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的顶点ID", "错误", JOptionPane.ERROR_MESSAGE);}});removeVertexBtn.addActionListener(e -> {try {int id = Integer.parseInt(vertexIdField.getText());if (!graph.containsVertex(id)) {JOptionPane.showMessageDialog(this, "顶点不存在", "错误", JOptionPane.ERROR_MESSAGE);} else {graph.removeVertex(id);canvas.repaint();}} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的顶点ID", "错误", JOptionPane.ERROR_MESSAGE);}});generateRandomBtn.addActionListener(e -> {generateRandomVertices(10);canvas.repaint();});vertexPanel.add(new JLabel("顶点ID:"));vertexPanel.add(vertexIdField);vertexPanel.add(addVertexBtn);vertexPanel.add(removeVertexBtn);vertexPanel.add(generateRandomBtn);// 边操作面板JPanel edgePanel = new JPanel();edgePanel.setBorder(BorderFactory.createTitledBorder("边操作"));fromField = new JTextField(3);toField = new JTextField(3);weightField = new JTextField(3);addEdgeBtn = new JButton("添加边");removeEdgeBtn = new JButton("删除边");updateWeightBtn = new JButton("更新权重");directedCheckbox = new JCheckBox("有向图");addEdgeBtn.addActionListener(e -> {try {int from = Integer.parseInt(fromField.getText());int to = Integer.parseInt(toField.getText());int weight = Integer.parseInt(weightField.getText());if (!graph.containsVertex(from) || !graph.containsVertex(to)) {JOptionPane.showMessageDialog(this, "顶点不存在", "错误", JOptionPane.ERROR_MESSAGE);return;}if (graph.containsEdge(from, to)) {JOptionPane.showMessageDialog(this, "边已存在", "错误", JOptionPane.ERROR_MESSAGE);return;}graph.addEdge(from, to, weight);canvas.repaint();} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的顶点ID和权重", "错误", JOptionPane.ERROR_MESSAGE);}});removeEdgeBtn.addActionListener(e -> {try {int from = Integer.parseInt(fromField.getText());int to = Integer.parseInt(toField.getText());if (!graph.containsEdge(from, to)) {JOptionPane.showMessageDialog(this, "边不存在", "错误", JOptionPane.ERROR_MESSAGE);return;}graph.removeEdge(from, to);canvas.repaint();} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的顶点ID", "错误", JOptionPane.ERROR_MESSAGE);}});updateWeightBtn.addActionListener(e -> {try {int from = Integer.parseInt(fromField.getText());int to = Integer.parseInt(toField.getText());int weight = Integer.parseInt(weightField.getText());if (!graph.containsEdge(from, to)) {JOptionPane.showMessageDialog(this, "边不存在", "错误", JOptionPane.ERROR_MESSAGE);return;}graph.updateEdgeWeight(from, to, weight);canvas.repaint();} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的顶点ID和权重", "错误", JOptionPane.ERROR_MESSAGE);}});directedCheckbox.addActionListener(e -> {graph.setDirected(directedCheckbox.isSelected());JOptionPane.showMessageDialog(this,"图类型已设置为: " + (directedCheckbox.isSelected() ? "有向图" : "无向图"),"提示", JOptionPane.INFORMATION_MESSAGE);});edgePanel.add(new JLabel("从:"));edgePanel.add(fromField);edgePanel.add(new JLabel("到:"));edgePanel.add(toField);edgePanel.add(new JLabel("权重:"));edgePanel.add(weightField);edgePanel.add(addEdgeBtn);edgePanel.add(removeEdgeBtn);edgePanel.add(updateWeightBtn);edgePanel.add(directedCheckbox);// 遍历操作面板JPanel traversalPanel = new JPanel();traversalPanel.setBorder(BorderFactory.createTitledBorder("遍历操作"));dfsBtn = new JButton("深度优先遍历");bfsBtn = new JButton("广度优先遍历");clearBtn = new JButton("清除颜色");dfsBtn.addActionListener(e -> {try {int startId = Integer.parseInt(vertexIdField.getText());if (!graph.containsVertex(startId)) {JOptionPane.showMessageDialog(this, "顶点不存在", "错误", JOptionPane.ERROR_MESSAGE);return;}List<Integer> result = graph.dfs(startId);graph.clearColors();highlightTraversal(result, Color.RED);JOptionPane.showMessageDialog(this, "DFS遍历结果: " + result);} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的起始顶点ID", "错误", JOptionPane.ERROR_MESSAGE);}});bfsBtn.addActionListener(e -> {try {int startId = Integer.parseInt(vertexIdField.getText());if (!graph.containsVertex(startId)) {JOptionPane.showMessageDialog(this, "顶点不存在", "错误", JOptionPane.ERROR_MESSAGE);return;}List<Integer> result = graph.bfs(startId);graph.clearColors();highlightTraversal(result, Color.BLUE);JOptionPane.showMessageDialog(this, "BFS遍历结果: " + result);} catch (NumberFormatException ex) {JOptionPane.showMessageDialog(this, "请输入有效的起始顶点ID", "错误", JOptionPane.ERROR_MESSAGE);}});clearBtn.addActionListener(e -> {graph.clearColors();canvas.repaint();});traversalPanel.add(dfsBtn);traversalPanel.add(bfsBtn);traversalPanel.add(clearBtn);// 添加所有面板到控制面板controlPanel.add(vertexPanel);controlPanel.add(edgePanel);controlPanel.add(traversalPanel);add(controlPanel, BorderLayout.SOUTH);setVisible(true);}private void generateRandomVertices(int count) {int width = canvas.getWidth();int height = canvas.getHeight();for (int i = 0; i < count; i++) {int id = graph.getNextAvailableId();int x = random.nextInt(width - Vertex.DIAMETER);int y = random.nextInt(height - Vertex.DIAMETER);graph.addVertex(id, x, y);}// 随机添加一些边List<Integer> vertexIds = new ArrayList<>(graph.getVertices().keySet());if (vertexIds.size() >= 2) {for (int i = 0; i < count / 2; i++) {int fromIdx = random.nextInt(vertexIds.size());int toIdx = random.nextInt(vertexIds.size());if (fromIdx != toIdx) {int fromId = vertexIds.get(fromIdx);int toId = vertexIds.get(toIdx);int weight = random.nextInt(10) + 1; // 权重1-10if (!graph.containsEdge(fromId, toId)) {graph.addEdge(fromId, toId, weight);}}}}}private void highlightTraversal(List<Integer> traversalOrder, Color color) {if (traversalOrder.isEmpty()) return;// 使用javax.swing.Timer而不是java.util.Timerjavax.swing.Timer timer = new javax.swing.Timer(1000, null);timer.setRepeats(true);final int[] index = {0};timer.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (index[0] < traversalOrder.size()) {int vertexId = traversalOrder.get(index[0]);for (Vertex v : graph.getVertices().values()) {if (v.getId() == vertexId) {v.setColor(color);break;}}canvas.repaint();index[0]++;} else {((javax.swing.Timer) e.getSource()).stop();}}});timer.start();}public static void main(String[] args) {SwingUtilities.invokeLater(() -> new GraphVisualization());}
}
http://www.dtcms.com/a/350688.html

相关文章:

  • Spring Cloud Alibaba
  • 机器学习三大核心思想:数据驱动、自动优化与泛化能力
  • 搭建python自动化测试环境
  • kmeans
  • 【Kotlin】Kotlin 常用注解详解与实战
  • 2025山东国际大健康产业博览会外贸优品中华行活动打造内外贸一体化高效平台
  • 瑞惯科技双轴倾角传感器厂家指南
  • 发射机功能符号错误直方图(Transmitter Functional Symbol Error Histogram)
  • 多级数据结构导出Excel工具类,支持多级数据导入导出,支持自定义字体颜色和背景颜色,支持自定义转化器
  • Java 并发编程总结
  • SCSS上传图片占位区域样式
  • 基于多通道同步分析的智能听诊系统应用程序
  • 动态住宅代理:跨境电商数据抓取的稳定解决方案
  • vue-admin-template vue-cli 4升5(vue2版)
  • C语言中哪些常见的坑
  • Linux的奇妙冒险———进程信号
  • 滲透測試工具
  • Microsoft 365 中的 Rules-Based Classification 功能深度解析:企业数据治理与合规的智能基石
  • 25年8月通信基础知识补充2:星座的峭度(Kurtosis)、ISAC
  • 朴素贝叶斯分类器
  • A股市场高级日历效应详解与实战指南
  • 【P2P】P2P主要技术及RELAY服务1:python实现
  • 【Git】fatal: Unable to create ‘.git/index.lock’: File exists.
  • 迁移面试题
  • 亚远景- 从算法到刹车片:ISO/PAS 8800如何量化自动驾驶的“安全冗余”?
  • Life:Internship in OnSea Day 64
  • PyTorch损失函数全解析与实战指南
  • 高性能C++实践:原子操作与无锁队列实现
  • C++ #pragma
  • C++初阶(3)C++入门基础2