Java GUI : 实现排序算法的动态演示
0.用到的jar包
jcommon-1.0.16.jar、jfreechart-1.0.13.jar
1.实现思路
自定义一个类---用于存放排序算法的操作状态--SortEntity
定义一个方法类---定义一个List<SortEntity>,存放当前排序的算法所有的时刻的状态值---SortUtils
定义一个工具类---传入一个SortEntity,根据SortEntity构建一个JPanel
在UI中通过线程来循环遍历List<SortEntity>,实现动画
3.SortEntity的定义
package com.edusys.utils; import java.util.Arrays; public class SortEntity { public int[] dataList=new int[20]; public int index_i; public int index_j; public int pivot=0; public int[] getDataList() { return dataList; } public void setDataList(int[] dataList) { for(int i=0;i<dataList.length;i++){ this.dataList[i] = dataList[i]; } } public int getIndex_i() { return index_i; } public void setIndex_i(int index_i) { this.index_i = index_i; } public int getIndex_j() { return index_j; } public void setIndex_j(int index_j) { this.index_j = index_j; } public int getPivot() { return pivot; } public void setPivot(int pivot) { this.pivot = pivot; } public SortEntity() { } public SortEntity(int[] dataList, int index_i, int index_j, int pivot) { for(int i=0;i<dataList.length;i++){ this.dataList[i] = dataList[i]; } this.index_i = index_i; this.index_j = index_j; this.pivot = pivot; } @Override public String toString() { return "SortEntity [dataList=" + Arrays.toString(dataList) + ", index_i=" + index_i + ", index_j=" + index_j + ", pivot=" + pivot + "]"; } }
PS:这里填个坑,数组赋值时不能直接赋值,必须进行拷贝
this.dataList=dataList; 替换为: for(int i=0;i<dataList.length;i++){ this.dataList[i] = dataList[i]; }
否则拷贝出的只是一个长度为传入长度的有序数组:[1,2,3,4,5......]
4.SortUtils的定义
package com.edusys.utils; import java.util.ArrayList; import java.util.List; public class SortUtils { private static int length=20; private int[] dataList; private List<SortEntity> quickList=new ArrayList<SortEntity>();; private List<SortEntity> bubbleList=new ArrayList<SortEntity>();; private List<SortEntity> shellList=new ArrayList<SortEntity>();; public int[] getDataList() { return dataList; } public void setDataList() { dataList = new int[length]; boolean flag=false; int temp; int index = 0; while(index < length){ temp = (int)(Math.random()*length)+1; //Check the same element for(int j=0;j<index;j++){ if(temp == dataList[j]){ flag=true; break; } else{ flag = false; } } if(!flag ){ dataList[index++] = temp; } } } /** * 对初始数组进行赋值 */ public SortUtils(){ setDataList(); } /** * 得到快速排序的结果集 * @return */ public List<SortEntity> getQuickList(){ quickSort(getDataList(),0,length-1); return quickList; } /** * 得到冒泡排序结果集 * @return */ public List<SortEntity> getBubbleList() { bubbleList.add(new SortEntity(getDataList(), 0, 1, 0)); bubbleSort(getDataList()); return bubbleList; } /** * 得到希尔排序的结果集 * @return */ public List<SortEntity> getShellList() { shellList.add(new SortEntity(getDataList(), 0, 1, 0)); shellSort(getDataList()); return shellList; } /** * 快速排序 * @param a * @param left * @param right */ public void quickSort(int[] arr,int left,int right) { if(left>right){ return; } int pivot=arr[left]; quickList.add(new SortEntity(arr, left, right, pivot)); //定义基准值为数组第一个数 int i=left; int j=right; while(i<j) { //从右往左找比基准值小的数 while(pivot<=arr[j]&&i<j){ j--; quickList.add(new SortEntity(arr, left, right, pivot)); } //从左往右找比基准值大的数 while(pivot>=arr[i]&&i<j){ i++; quickList.add(new SortEntity(arr, left, right, pivot)); } //如果i<j,交换它们 if(i<j){ int temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; quickList.add(new SortEntity(arr, left, right, pivot)); } } //把基准值放到合适的位置 arr[left]=arr[i]; arr[i]=pivot; //对左边的子数组进行快速排序 quickSort(arr,left,i-1); //对右边的子数组进行快速排序 quickSort(arr,i+1,right); } /** * 冒泡排序 * @param arr */ public void bubbleSort(int[] arr){ for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数 for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次 if(arr[j]>arr[j+1]){ int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } bubbleList.add(new SortEntity(arr, i, j, 0)); } } } /** * 希尔排序 * @param arr */ public void shellSort(int[] arr){ //增量 int incrementNum = arr.length/2; while(incrementNum >=1){ for(int i=0;i<arr.length;i++){ //进行插入排序 for(int j=i;j<arr.length-incrementNum;j=j+incrementNum){ if(arr[j]>arr[j+incrementNum]){ int temple = arr[j]; arr[j] = arr[j+incrementNum]; arr[j+incrementNum] = temple; } shellList.add(new SortEntity(arr, i, j, 0)); } } //设置新的增量 incrementNum = incrementNum/2; } } }
此方法可以定义多个List<SortEntity>,实现各种不同的算法排序
5.HistogramUtils的实现
package com.edusys.utils; import java.awt.Font; import javax.swing.JPanel; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.annotations.CategoryTextAnnotation; import org.jfree.chart.axis.CategoryAnchor; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.ui.TextAnchor; public class HistogramUtils { private SortEntity sortEntity; public HistogramUtils(SortEntity sortEntity) { this.sortEntity=sortEntity; } /** * 创建柱状图数据集 * @return */ public CategoryDataset createDataset(){ DefaultCategoryDataset dataset=new DefaultCategoryDataset(); int[] dataList=sortEntity.getDataList(); for(int i=0;i<dataList.length;i++){ if(sortEntity.getIndex_i()==i){ dataset.setValue(sortEntity.getDataList()[i],"", sortEntity.getDataList()[i]+" i"); }else if(sortEntity.getIndex_j()==i){ dataset.setValue(sortEntity.getDataList()[i],"", sortEntity.getDataList()[i]+" j"); }else{ dataset.setValue(sortEntity.getDataList()[i],"", sortEntity.getDataList()[i]+""); } } return dataset; } /** * 用数据集创建一个图表 * @param dataset * @return */ public JFreeChart createChart(CategoryDataset dataset){ JFreeChart chart=ChartFactory.createBarChart("", "","", dataset, PlotOrientation.VERTICAL, true, true, false); //创建一个JFreeChart chart.setTitle(new TextTitle("",new Font("宋体",Font.BOLD+Font.ITALIC,40)));//可以重新设置标题,替换“hi”标题 CategoryPlot plot=(CategoryPlot)chart.getPlot();//获得图标中间部分,即plot CategoryAxis categoryAxis=plot.getDomainAxis();//获得横坐标 categoryAxis.setLabelFont(new Font("微软雅黑",Font.BOLD,10));//设置横坐标字体 categoryAxis.setMaximumCategoryLabelWidthRatio(2f); categoryAxis.setCategoryLabelPositions(CategoryLabelPositions.DOWN_90); return chart; } /** * 生成一个Jpanel * @return */ public JPanel createPanel(){ JFreeChart chart =createChart(createDataset()); CategoryPlot categoryplot =(CategoryPlot)chart.getCategoryPlot(); BarRenderer renderer = new CustomRenderer(sortEntity.getIndex_i(), sortEntity.getIndex_j()); //在柱子上显示相应信息 renderer.setBaseItemLabelsVisible(true); CategoryTextAnnotation a = new CategoryTextAnnotation("_________________________________________________________", "", sortEntity.getPivot()); a.setCategoryAnchor(CategoryAnchor.START); a.setFont(new Font("SansSerif", Font.PLAIN, 12)); a.setTextAnchor(TextAnchor.BOTTOM_LEFT); categoryplot.addAnnotation(a); categoryplot.setRenderer(renderer); return new ChartPanel(chart); //将chart对象放入Panel面板中去,ChartPanel类已继承Jpanel } }
这个使用的是JFreeChart 来进行构图的,具体的百度参考,需要的jar在文章的开头处
5.1自定义的BarRenderer
package com.edusys.utils; import java.awt.Color; import java.awt.Paint; import org.jfree.chart.renderer.category.IntervalBarRenderer; public class CustomRenderer extends IntervalBarRenderer { private int index_i,index_j; private Paint[] colors; public CustomRenderer(int index_i,int index_j) { this.index_i=index_i; this.index_j=index_j; colors = new Paint[3]; colors[0]=Color.RED; colors[1]=Color.RED; colors[2]=Color.GREEN; } public CustomRenderer() { super(); } //对i,j赋予不同于其他柱子的颜色 public Paint getItemPaint(int i, int j) { if(j==index_i){ return colors[0]; }else if(j==index_j){ return colors[1]; }else { return colors[2]; } } }
这里重写BarRenderer 类是为了对每次状态中的i,j进行特殊颜色处理,并规范JFreeChart 构图中的颜色
6.实现类
package com.edusys.ui; import java.awt.Dimension; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ScrollPaneConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.edusys.utils.FileUtils; import com.edusys.utils.HistogramUtils; import com.edusys.utils.SortEntity; import com.edusys.utils.SortUtils; import com.edusys.utils.TimeUtils; public class MainUI { private static int sort_type=0; private static int speed=1; private static int sleeptime=100; private static int count=0; private static boolean play_flag=false; private static boolean start_flag=false; private static List<SortEntity> sortList; public static void main(String[] args) { MainUI.animation(); } /** * 算法动画演示 */ public static void animation(){ final JFrame frame=new JFrame(); frame.setSize(1000,620); frame.setVisible(true); frame.setResizable(false); frame.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width-1000)/2, (Toolkit.getDefaultToolkit().getScreenSize().height-620)/2); frame.setTitle("Algorithm Teaching System"); frame.setLayout(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); sleeptime=100/speed; final SortUtils sortUtils=new SortUtils(); if(sort_type==0){ sortList=sortUtils.getQuickList(); }else if(sort_type==1){ sortList=sortUtils.getBubbleList(); }else{ sortList=sortUtils.getShellList(); } int len=sortList.size(); JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); JLabel jLabel_title=new JLabel("Sort Algorithm"); frame.add(jLabel_title); jLabel_title.setBounds(500, 20, 120, 30); JRadioButton jRadioButton_quick=new JRadioButton("Qucik Sort"); frame.add(jRadioButton_quick); if(sort_type==0){ jRadioButton_quick.setSelected(true); } jRadioButton_quick.setBounds(500, 50, 120, 30); jRadioButton_quick.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sort_type=0; frame.dispose(); MainUI.animation(); } }); JRadioButton jRadioButton_bubble=new JRadioButton("Bubble Sort"); frame.add(jRadioButton_bubble); jRadioButton_bubble.setBounds(500, 80, 120, 30); if(sort_type==1){ jRadioButton_bubble.setSelected(true); } jRadioButton_bubble.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sort_type=1; frame.dispose(); MainUI.animation(); } }); JRadioButton jRadioButton_shell=new JRadioButton("Shell Sort"); frame.add(jRadioButton_shell); jRadioButton_shell.setBounds(500, 110, 120, 30); if(sort_type==2){ jRadioButton_shell.setSelected(true); } jRadioButton_shell.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { sort_type=2; frame.dispose(); MainUI.animation(); } }); ButtonGroup buttonGroup_sort=new ButtonGroup(); buttonGroup_sort.add(jRadioButton_shell); buttonGroup_sort.add(jRadioButton_bubble); buttonGroup_sort.add(jRadioButton_quick); JLabel jLabel_speed=new JLabel("Speed"); frame.add(jLabel_speed); jLabel_speed.setBounds(620, 160, 120, 30); JSlider jSlider_speed = new JSlider(1, 5, 1); frame.add(jSlider_speed); jSlider_speed.setBounds(500, 190, 300, 40); jSlider_speed.setMajorTickSpacing(1); jSlider_speed.setPaintTicks(true); jSlider_speed.setPaintLabels(true); jSlider_speed.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { speed=jSlider_speed.getValue(); sleeptime=100/speed; } }); JButton jButton_start=new JButton("Start"); frame.add(jButton_start); jButton_start.setBounds(500, 320, 80, 30); jButton_start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { start_flag=true; new Thread(){ @Override public void run(){ while (count<len&&start_flag){ //休眠 try { Thread.sleep(sleeptime); } catch (InterruptedException e1) { e1.printStackTrace(); } JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); count++; } } }.start(); jButton_start.setEnabled(false); } }); JButton jButton_restart=new JButton("Restart"); frame.add(jButton_restart); jButton_restart.setBounds(600, 320, 80, 30); jButton_restart.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { frame.dispose(); MainUI.animation(); } }); JButton jButton_pause=new JButton("Pause"); frame.add(jButton_pause); jButton_pause.setBounds(700, 320, 80, 30); jButton_pause.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(!play_flag){ start_flag=false; jButton_pause.setText("Play"); play_flag=true; }else{ start_flag=true; jButton_pause.setText("Pause"); play_flag=false; new Thread(){ @Override public void run(){ while (count<len&&start_flag){ //休眠 try { Thread.sleep(sleeptime); } catch (InterruptedException e1) { e1.printStackTrace(); } JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); count++; } } }.start(); } } }); JButton jButton_forward=new JButton("Forward"); frame.add(jButton_forward); jButton_forward.setBounds(800, 320, 80, 30); jButton_forward.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(count<len-1){ count++; JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); } } }); JButton jButton_back=new JButton("Back"); frame.add(jButton_back); jButton_back.setBounds(900, 320, 80, 30); jButton_back.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(count>0){ count--; JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); } } }); JButton jButton_back2menu=new JButton("Back To Menu"); frame.add(jButton_back2menu); jButton_back2menu.setBounds(700, 360, 120, 30); jButton_back2menu.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { frame.dispose(); MainUI.explanation(); } }); new Thread(){ @Override public void run(){ while (count<len&&start_flag){ //休眠 try { Thread.sleep(sleeptime); } catch (InterruptedException e1) { e1.printStackTrace(); } JPanel jPanel_sort=new JPanel(); HistogramUtils histogramUtils=new HistogramUtils(sortList.get(count)); jPanel_sort=histogramUtils.createPanel(); frame.add(jPanel_sort); jPanel_sort.setBounds(0, 0, 450, 600); count++; } } }.start(); } }
这里是实现类,线程上做的不是很好,因为对线程不是很了解,这里我也尝试过用while循环的,但是跳出去之后就不行了,重新打开另外的页面时,会卡死
因此这个线程虽然写的不是很好,但是刚刚能用
代码冗余,见谅!
效果图如下: