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

java 复制代码
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 final
    private 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() {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                graph.draw(g);
            }
        };
        canvas.setBackground(Color.WHITE);
        canvas.addMouseListener(new MouseAdapter() {
            @Override
            public 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);
                            }
                        }
                    }
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {
                selectedVertex = graph.getVertexAt(e.getX(), e.getY());
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                selectedVertex = null;
            }
        });

        canvas.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public 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-10

                    if (!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.Timer
        javax.swing.Timer timer = new javax.swing.Timer(1000, null);
        timer.setRepeats(true);

        final int[] index = {0};
        timer.addActionListener(new ActionListener() {
            @Override
            public 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());
    }
}
相关推荐
没有bug.的程序员几秒前
AOT 编译与 GraalVM 实战:Java 云原生的终极进化
java·python·云原生·graalvm·aot
找不到、了15 分钟前
常用的分布式ID设计方案
java·分布式
野区捕龙为宠24 分钟前
Unity Netcode for GameObjects(多人联机小Demo)
java·unity·游戏引擎
寻星探路29 分钟前
数据结构青铜到王者第三话---ArrayList与顺序表(1)
数据结构
今后12329 分钟前
【数据结构】顺序表详解
数据结构·顺序表
啟明起鸣32 分钟前
【数据结构】B 树——高度近似可”独木成林“的榕树——详细解说与其 C 代码实现
c语言·开发语言·数据结构
这周也會开心32 分钟前
数据结构-ArrayList
数据结构
nonono32 分钟前
数据结构——线性表(链表,力扣中等篇,技巧型)
数据结构·leetcode·链表
hrrrrb32 分钟前
【数据结构】栈和队列——队列
数据结构
XMZH0304232 分钟前
数据结构:单向链表的逆置;双向循环链表;栈,输出栈,销毁栈;顺序表和链表的区别和优缺点;0825
数据结构·链表·