可视化排序实践之冒泡排序

如果排序过程程序执行能结合起来,那么这个过程会更加直观。 本文给出一个冒泡排序的可视化排序实现, 效果如下图所示:

一、界面组成

界面很简单就包括两个部分:界面左侧是可视化排序部分右侧是冒泡排序的代码

二、如何实现代码和排序的视觉同步?

2.1 关键点

  • 如何在页面上表示出排序程序的运行过程。
  • 如何将排序程序的运行过程和可视化排序结合起来,保持状态一致。

2.2 解决方法

在这个例子中,我用了javax.swing.JList模拟程序的运行。

javax.swing.JList有一个setSelectedIndex的方法,能高亮显示指定的行。 通过改变selectedIndex的值,能够达到模拟冒泡排序程序执行的效果。在这个过程中,记录下两个循环的索引状态值,根据这些状态值去调整可视化排序。

三、页面展示

3.1 初始化页面

程序随机产生10个数字,然后展示在左侧的排序面板中,每个数都用绿色的矩形展示。

  private List<NumberRectangle> initialNumberRectangles() {
      List<NumberRectangle> list = new ArrayList<NumberRectangle>();
      /**
       * 随机产生10个数组
       */
      Random random = new Random();
      for (int i = 1; i <= 10; i++) {
        list.add(new NumberRectangle(i, 1, random.nextInt(15) + 1,
            Color.GREEN));
      }
      return list;
  }

冒泡程序存放在左侧的javax.swing.JList中。

  private static final String[] BUBBLE_SOURCE_CODE = {
      "public void bubbleSort(int[] data) {       ",
      "  for (int i = 0; i < data.length - 1; i++) {",
      "    for (int j = 0; j < data.length - i - 1; j++) {",
      "      if (data[j] > data[j + 1]) {          ",
      "        int temp = data[j + 1];         ",
      "        data[j + 1] = data[j];           ",
      "        data[j] = temp;             ",
      "      }                              ",
      "    }                                ",
      "  }                                  ",
      "}                                    " };

  private JList<String> codeList = new JList<String>(BUBBLE_SOURCE_CODE);

3.2 运行速度设置

点击菜单栏Set下的Speed可以设置程序执行的速度。

选择不同的速度项,其会修改Timer延迟的时间,从而达到程序速度改变的效果。

  private class SpeedAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      Object speed = event.getSource();
      if (speed == speedMI1) {
        speedFlag = 1;
      } else if (speed == speedMI2) {
        speedFlag = 2;
      } else if (speed == speedMI3) {
        speedFlag = 3;
      } else if (speed == speedMI4) {
        speedFlag = 4;
      } else if (speed == speedMI5) {
        speedFlag = 5;
      }

      panel.timer.setDelay(1000 - 200 * (speedFlag - 1));
    }
  }

3.3 程序开始

点击菜单栏Set下的Start选项,程序就开始运行。

3.4 程序中间运行过程

排序的主要逻辑主要写在TimeAction中,

该类主要通过java.swing.JList列表的选中的索引的改变,从而决定左侧排序面板的变化,设置不同的颜色。

  private class TimerAction implements ActionListener, Serializable {

    private static final long serialVersionUID = -8671813189049345697L;

    public void actionPerformed(ActionEvent event) {
      if (completed) {
        return;
      }

      switch (selectedIndex) {
      case 1:
        if (outerLoop < 10) {
          innerLoop = 0;
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          selectedIndex = 10;
        }
        break;
      case 2:
        if (innerLoop < 10 - outerLoop - 1) {
          numbers.get(innerLoop).setColor(Color.RED);
          numbers.get(innerLoop + 1).setColor(Color.BLUE);
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          outerLoop++;
          selectedIndex = 1;
        }
        break;
      case 3:
        if (numbers.get(innerLoop).getValue() > numbers.get(
            innerLoop + 1).getValue()) {
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          numbers.get(innerLoop + 1).setColor(Color.GREEN);
          numbers.get(innerLoop).setColor(Color.GREEN);
          innerLoop++;
          selectedIndex = 2;
        }
        break;
      case 4:
        codeList.setSelectedIndex(selectedIndex++);
        break;
      case 5:
        codeList.setSelectedIndex(selectedIndex++);
        break;

      case 6:
        codeList.setSelectedIndex(selectedIndex);
        int tempValue1 = numbers.get(innerLoop).getValue();
        int tempValue2 = numbers.get(innerLoop + 1).getValue();
        numbers.get(innerLoop + 1).setValue(tempValue1);
        numbers.get(innerLoop).setValue(tempValue2);
        numbers.get(innerLoop + 1).setColor(Color.GREEN);
        numbers.get(innerLoop).setColor(Color.GREEN);
        selectedIndex = 2;
        innerLoop++;
        break;

      case 10:
        if (selectedIndex == 10) {
          completed = true;
          codeList.setSelectedIndex(selectedIndex);
        }
        break;
      default:
        break;
      }

      repaint();

    }
  }

3.5 排序完成页面

四、实现代码

  • BubbleSortVisualizationFrame.java
package my.visualization.sort.bubble;

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;

/**
 * 
 * @author wangmengjun
 *
 */
public class BubbleSortVisualizationFrame extends JFrame {

  private static final long serialVersionUID = -6725108659717827278L;

  private Container contentPane;

  /**
   * 设置三个Menu Item,分别用于开始程序,调整运行的速度以及退出程序
   * 
   */
  private JMenuItem startMI = new JMenuItem("Start");

  private JMenu speedMenu = new JMenu("Speed");

  private JMenuItem exitMI = new JMenuItem("Exit");

  /**
   * 设定5个速度级别
   */
  private JRadioButtonMenuItem speedMI1 = new JRadioButtonMenuItem("Speed1",
      true);

  private JRadioButtonMenuItem speedMI2 = new JRadioButtonMenuItem("Speed2",
      false);

  private JRadioButtonMenuItem speedMI3 = new JRadioButtonMenuItem("Speed3",
      false);

  private JRadioButtonMenuItem speedMI4 = new JRadioButtonMenuItem("Speed4",
      false);

  private JRadioButtonMenuItem speedMI5 = new JRadioButtonMenuItem("Speed5",
      false);

  public int speedFlag = 1;
  
  /**
   * 冒泡排序可视化的Panel
   */
  private BubbleSortPanel panel;

  public BubbleSortVisualizationFrame(){
    
    setTitle("可视化排序之冒泡排序");
    setSize(700, 400);
    setResizable(false);

    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);

    JMenu setMenu = new JMenu("Set");
    
    setMenu.setMnemonic('s');

    menuBar.add(setMenu);

    setMenu.add(startMI);
    setMenu.addSeparator();

    setMenu.addSeparator();
    setMenu.add(speedMenu);
    setMenu.addSeparator();
    setMenu.add(exitMI);

    ButtonGroup group = new ButtonGroup();
    group.add(speedMI1);
    group.add(speedMI2);
    group.add(speedMI3);
    group.add(speedMI4);
    group.add(speedMI5);

    speedMenu.add(speedMI1);
    speedMenu.add(speedMI2);
    speedMenu.add(speedMI3);
    speedMenu.add(speedMI4);
    speedMenu.add(speedMI5);

    startMI.addActionListener(new StartAction());
    speedMI1.addActionListener(new SpeedAction());
    speedMI2.addActionListener(new SpeedAction());
    speedMI3.addActionListener(new SpeedAction());
    speedMI4.addActionListener(new SpeedAction());
    speedMI5.addActionListener(new SpeedAction());
    exitMI.addActionListener(new ExitAction());
    
    contentPane = getContentPane();
    
    panel = new BubbleSortPanel(this);
    contentPane.add(panel);
    startMI.setEnabled(true);
  }
  
  private class StartAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      startMI.setEnabled(false);
      panel.timer.start();
    }
  }
  
  private class ExitAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      System.exit(0);
    }
  }
  
  private class SpeedAction implements ActionListener {
    public void actionPerformed(ActionEvent event) {
      Object speed = event.getSource();
      if (speed == speedMI1) {
        speedFlag = 1;
      } else if (speed == speedMI2) {
        speedFlag = 2;
      } else if (speed == speedMI3) {
        speedFlag = 3;
      } else if (speed == speedMI4) {
        speedFlag = 4;
      } else if (speed == speedMI5) {
        speedFlag = 5;
      }

      panel.timer.setDelay(1000 - 200 * (speedFlag - 1));
    }
  }
  
}
  • BubbleSortPanel.java
package my.visualization.sort.bubble;

/**
 * @author wangmengjun
 *
 */
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

public class BubbleSortPanel extends JPanel {

  private static final long serialVersionUID = -9149581857139587792L;

  private static final String[] BUBBLE_SOURCE_CODE = {
      "public void bubbleSort(int[] data) {       ",
      "  for (int i = 0; i < data.length - 1; i++) {",
      "    for (int j = 0; j < data.length - i - 1; j++) {",
      "      if (data[j] > data[j + 1]) {          ",
      "        int temp = data[j + 1];         ",
      "        data[j + 1] = data[j];           ",
      "        data[j] = temp;             ",
      "      }                              ",
      "    }                                ",
      "  }                                  ",
      "}                                    " };

  private JList<String> codeList = new JList<String>(BUBBLE_SOURCE_CODE);

  /**
   * 初始化10个数据
   */
  private List<NumberRectangle> numbers = initialNumberRectangles();

  
  public TimerAction timerAction;

  public Timer timer;

  public BubbleSortVisualizationFrame frame;

  public BubbleSortPanel(BubbleSortVisualizationFrame frame) {

    timerAction = new TimerAction();
    timer = new Timer(1000, timerAction);

    codeList.setSelectedIndex(1);
    JScrollPane scrollPane1 = new JScrollPane(codeList);
    this.setLayout(new BorderLayout());
    this.add(scrollPane1, BorderLayout.EAST);

    this.frame = frame;
  }

  /**
   * 判断排序是否已经结束
   */
  private boolean completed = false;

  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;

    drawNumberRectangles(g2);
  }

  private void drawNumberRectangles(Graphics2D g2) {
    for (NumberRectangle rectangle : numbers) {
      rectangle.draw(g2);
    }
  }

  int outerLoop = 0;
  int innerLoop = 0;
  int selectedIndex = 1;

  private class TimerAction implements ActionListener, Serializable {

    private static final long serialVersionUID = -8671813189049345697L;

    public void actionPerformed(ActionEvent event) {
      if (completed) {
        return;
      }

      switch (selectedIndex) {
      case 1:
        if (outerLoop < 10) {
          innerLoop = 0;
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          selectedIndex = 10;
        }
        break;
      case 2:
        if (innerLoop < 10 - outerLoop - 1) {
          numbers.get(innerLoop).setColor(Color.RED);
          numbers.get(innerLoop + 1).setColor(Color.BLUE);
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          outerLoop++;
          selectedIndex = 1;
        }
        break;
      case 3:
        if (numbers.get(innerLoop).getValue() > numbers.get(
            innerLoop + 1).getValue()) {
          codeList.setSelectedIndex(selectedIndex++);
        } else {
          numbers.get(innerLoop + 1).setColor(Color.GREEN);
          numbers.get(innerLoop).setColor(Color.GREEN);
          innerLoop++;
          selectedIndex = 2;
        }
        break;
      case 4:
        codeList.setSelectedIndex(selectedIndex++);
        break;
      case 5:
        codeList.setSelectedIndex(selectedIndex++);
        break;

      case 6:
        codeList.setSelectedIndex(selectedIndex);
        int tempValue1 = numbers.get(innerLoop).getValue();
        int tempValue2 = numbers.get(innerLoop + 1).getValue();
        numbers.get(innerLoop + 1).setValue(tempValue1);
        numbers.get(innerLoop).setValue(tempValue2);
        numbers.get(innerLoop + 1).setColor(Color.GREEN);
        numbers.get(innerLoop).setColor(Color.GREEN);
        selectedIndex = 2;
        innerLoop++;
        break;

      case 10:
        if (selectedIndex == 10) {
          completed = true;
          codeList.setSelectedIndex(selectedIndex);
        }
        break;
      default:
        break;
      }

      repaint();

    }
  }

  private List<NumberRectangle> initialNumberRectangles() {
    List<NumberRectangle> list = new ArrayList<NumberRectangle>();
    /**
     * 随机产生10个数组
     */
    Random random = new Random();
    for (int i = 1; i <= 10; i++) {
      list.add(new NumberRectangle(i, 1, random.nextInt(15) + 1,
          Color.GREEN));
    }
    return list;
  }

}
  • NumberRectangle.java
package my.visualization.sort.bubble;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;

/**
 * 
 * @author wangmengjun
 *
 */
public class NumberRectangle {

  private int x;

  private int y;

  private int value;

  private Color color;

  public NumberRectangle() {
  }

  public NumberRectangle(int x, int y, int value, Color color) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.value = value;

  }

  public void draw(Graphics2D g2) {
    int clientX = 30 + x * 30;
    int clientY = 20 + y * 10;
    Rectangle2D.Double rect = new Rectangle2D.Double(clientX, clientY, 20,
        value * 20);
    g2.setPaint(color);
    g2.fill(rect);
    g2.setPaint(Color.BLACK);
    g2.draw(rect);
    g2.drawString(String.valueOf(value), clientX, clientY - 10);
  }

  /**
   * @return the color
   */
  public Color getColor() {
    return color;
  }

  /**
   * @param color
   *            the color to set
   */
  public void setColor(Color color) {
    this.color = color;
  }

  /**
   * @return the x
   */
  public int getX() {
    return x;
  }

  /**
   * @param x
   *            the x to set
   */
  public void setX(int x) {
    this.x = x;
  }

  /**
   * @return the y
   */
  public int getY() {
    return y;
  }

  /**
   * @param y
   *            the y to set
   */
  public void setY(int y) {
    this.y = y;
  }

  /**
   * @return the value
   */
  public int getValue() {
    return value;
  }

  /**
   * @param value
   *            the value to set
   */
  public void setValue(int value) {
    this.value = value;
  }

}
  • BubbleSortApplication.java
package my.visualization.sort.bubble;

import javax.swing.JFrame;

public class BubbleSortApplication {
  @SuppressWarnings("deprecation")
  public static void main(String[] args) {
    BubbleSortVisualizationFrame frame = new BubbleSortVisualizationFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.show();
  }
}

五、某次运行效果

这样,一个简单地,模拟执行结合排序变化的例子就完成了。:) 有兴趣的读者可以试一试。

本文分享自微信公众号 - 孟君的编程札记(gh_0f0f5e0ae1de)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券