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循環的,但是跳出去之后就不行了,重新打開另外的頁面時,會卡死
因此這個線程雖然寫的不是很好,但是剛剛能用
代碼冗余,見諒!
效果圖如下: