all是五种排序同时进行,以便更好观察,文末会附图说明。
这里选择的是选择排序升序。
在熟练掌握了这五种排序思想后,首先我们在Algorithm.java中写好排序算法的代码,同时写好GUI界面总体规划(GUI.java),实例化JFrame组件,为后面的主要界面设计(RandomGUI.java 、 ChoiceAlgorithm.java )做准备:
package com.Algorithm;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import com.GUI.GUI;
public class Algorithm {
private GUI g;//创建对象
public int[] a1;
ArrayList<String> syso = new ArrayList<String>();
String temp;
String filePath[] = new String[] { "./冒泡排序.txt", "./希尔排序.txt", "./堆排序.txt", "./选择排序.txt", "./递归排序.txt", };
public static volatile int i = 0;
public static volatile int j = 0;
public Algorithm(int[] arr, String name, int k) {
this.g = new GUI(arr, 500 * i, 500 * j, name, k);
if (i == 2) {
j++;
i = 0;
}
i++;
}
public void write(File f) {
if (f.canRead()) {
f.delete();
}
if (!f.canRead()) {
try {
if (f.createNewFile()) {
new Exception("文件不能创建,请检查权限");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
for (String t : syso) {
bw.write(t);
bw.newLine();
bw.flush();
}
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void mySleep(int[] a) {
g.jpl.arr = a;
temp = "";
for (int a1 : a) {
temp += a1 + ",";
}
syso.add(temp);
g.jpl.setSyso(syso);
g.jpl.repaint();
try {
Thread.sleep(1500);// 延时刷新时间
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int[] maopao(int a[]) {
// 冒泡排序
for (int i = 0; i < a.length - 1; i++) {
for (int j = 0; j < a.length - i - 1; j++) {
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
mySleep(a);
}
}
}
File f = new File(filePath[0]);
write(f);
return a;
}
public int[] xier(int[] ins) {
//sort(ins);
// 希尔排序
int n = ins.length;
int gap = n / 2;
while (gap > 0) {
for (int j = gap; j < n; j++) {
int i = j;
while (i >= gap && ins[i - gap] > ins[i]) {
int temp = ins[i - gap] + ins[i];
ins[i - gap] = temp - ins[i - gap];
ins[i] = temp - ins[i - gap];
i -= gap;
mySleep(ins);
}
}
gap = gap / 2;
}
File f = new File(filePath[1]);
write(f);
return ins;
}
public void heapSort(int[] a) {
int len = a.length - 1;
// heapSort(b, b.length - 1);
// 推排序
int i;
for (i = len / 2; i >= 0; i--) { /* 把a[]构造成一个大顶堆 */
HeapAdjust(a, i, len);
}
for (i = len; i > 0; i--) {
swap(a, 0, i); /* 交换堆顶最大元素和堆尾最小元素 */
HeapAdjust(a, 0, i - 1); /* 把交换后的堆a[0,i-1],再次构造成大顶顶,使堆顶元素为最大值 */
mySleep(a);
}
File f = new File(filePath[2]);
write(f);
}
private void HeapAdjust(int[] a, int start, int len) {
int temp, j;
temp = a[start];
for (j = 2 * start; j <= len; j *= 2) { /* 从index最大的有孩子的节点开始筛选,堆排 */
if (j < len && a[j] < a[j + 1]) /* 是index=j的元素为较大的元素 */
j++;
if (temp >= a[j])
break;
a[start] = a[j]; /* 将较大元素赋值给父节点 */
start = j;
}
a[start] = temp;
}
private void swap(int a[], int low, int high) {
int temp = a[low];
a[low] = a[high];
a[high] = temp;
mySleep(a);
}
public String selectionSort(int[] arr) {
///selectionSort(arr)
// 选择排序
for (int i = 0; i < arr.length; i++) {
// 最小数的索引
int minIndex = i;
for (int j = i; j < arr.length; j++) {
// 找到最小数,并记录最小数的索引
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 交换符合条件的数
int tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
mySleep(arr);
}
File f = new File(filePath[3]);
write(f);
return Arrays.toString(arr);
}
private void merge(int[] a, int left, int mid, int right) {
int[] tmp = new int[a.length];// 辅助数组
int p1 = left, p2 = mid + 1, k = left;// p1、p2是检测指针,k是存放指针
while (p1 <= mid && p2 <= right) {
if (a[p1] <= a[p2])
tmp[k++] = a[p1++];
else
tmp[k++] = a[p2++];
mySleep(a);
}
while (p1 <= mid)
tmp[k++] = a[p1++];// 如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while (p2 <= right)
tmp[k++] = a[p2++];// 同上
// 复制回原素组
for (int i = left; i <= right; i++)
a[i] = tmp[i];
}
public void mergeSort(int[] a, int start, int end) {
// mergeSort(a, 0, a.length-1);
// 递归排序
if (start < end) {// 当子序列中只有一个元素时结束递归
int mid = (start + end) / 2;// 划分子序列
mergeSort(a, start, mid);// 对左侧子序列进行递归排序
mergeSort(a, mid + 1, end);// 对右侧子序列进行递归排序
merge(a, start, mid, end);// 合并
mySleep(a);
}
File f = new File(filePath[4]);
write(f);
}
}
然后是GUI.java
package com.GUI;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import com.Main.MyJPanel;
public class GUI {
private JFrame jf = new JFrame();
public MyJPanel jpl;
public JTextArea jtr = new JTextArea("hjkl");
public GUI(int[] arr, int x, int y, String name, int i) {
// 实例化JFrame组件
jpl = new MyJPanel(arr, i);
jf.setSize(500, 500);// 窗口大小
jf.setVisible(true);// 设置可见度
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 设置默认关闭动作
jf.setLocation(x, y);// 窗口居中
jf.setResizable(false);
jf.setTitle(name);
jf.add(jpl);// 添加组件
}
}
下来是我们最重要的部分,三大页面的设计。RandomGUI.java负责生成随机数,使用try-catch和if语句检测输入数字合理性,使用File文件类可以把生成的随机数保存到指定文件中,其他的就是一些界面的布局设计,此处不多赘述。
package com.GUI;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class RandomGUI {
private JFrame jf = new JFrame();
private JPanel jpl = new JPanel();
private JTextField jtf1 = new JTextField("产生随机数个数");
private JTextField jtf2 = new JTextField("随机数下届");
private JTextField jtf3 = new JTextField("随机数上届");
private JTextField jtf11 = new JTextField();
private JTextField jtf21 = new JTextField();
private JTextField jtf31 = new JTextField();
private JTextField jtf4 = new JTextField();
private JButton jb = new JButton("确定");
private JButton jb1 = new JButton("取消");
private JButton jb2 = new JButton("read");
public RandomGUI() {
jf.setSize(600, 600);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setTitle("生成随机数");
jf.add(jpl);
// jpl.setLayout(new GridLayout(4, 4));
jpl.setLayout(null);
jpl.add(jtf4);
jpl.add(jb2);
jtf1.setEditable(false);
jtf2.setEditable(false);
jtf3.setEditable(false);
jb2.setSize(new Dimension(100, 50));
// jtf1.setLocation(50, 50);
jtf4.setSize(new Dimension(400, 50));
jtf1.setSize(new Dimension(200, 100));
jtf2.setSize(new Dimension(200, 100));
jtf3.setSize(new Dimension(200, 100));
jtf11.setSize(new Dimension(200, 100));
jtf21.setSize(new Dimension(200, 100));
jtf31.setSize(new Dimension(200, 100));
jtf4.setLocation(100, 350);
jtf1.setLocation(0, 0);
jtf2.setLocation(0, 120);
jtf3.setLocation(0, 240);
jtf11.setLocation(350, 0);
jtf21.setLocation(350, 120);
jtf31.setLocation(350, 240);
// jtf1.setLocation(0, 0);
jpl.add(jtf1);
jpl.add(jtf11);
jpl.add(jtf2);
jpl.add(jtf21);
jpl.add(jtf3);
jpl.add(jtf31);
jb.setSize(new Dimension(100, 50));
jb1.setSize(new Dimension(100, 50));
jb.setLocation(50, 450);
jb1.setLocation(400, 450);
jb2.setLocation(250, 450);
jpl.add(jb);
jpl.add(jb1);
jb1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int arr[];
try {
arr = new Random().ints(Integer.parseInt(jtf11.getText()), Integer.parseInt(jtf21.getText()),
Integer.parseInt(jtf31.getText())).toArray();
} catch (NumberFormatException e2) {//数字格式异常
return;
}
// jtf11.setEditable(false);
String temp = "";
for (int a : arr) {
temp += a + ",";
}
jtf4.setText(temp);
File f = new File("./save.txt");
if (f.canRead()) {
f.delete();
}
try {
if (f.createNewFile()) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
bw.write(temp);
bw.close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// GUI g = new GUI(arr, 0, 0);
new ChoiceAlgorithm(arr);
}
});
jb2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(new File("./save.txt"))));
String temp;
if ((temp = br.readLine()) != null) {
br.close();
String tempArr[] = temp.split(",");
int arr[] = new int[tempArr.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(tempArr[i]);
}
new ChoiceAlgorithm(arr);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
}
}
第二个页面就是我们的选择排序方式界面:ChoiceAlgorithm.java。这里我们最好事先在其他包下创建好线程类,因为在文章开头说了,会存在一个all的选项,从而出现五种排序并发执行的情况。
package com.GUI;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.Algorithm.Algorithm;
public class ChoiceAlgorithm {
JFrame jf = new JFrame();
JPanel jpl = new JPanel();
String algorithm[] = { "冒泡排序", "希尔排序", "堆 排序", "选择排序", "递归排序", "all" };
JButton jbt[];
int[] arr;
int i = 0;
public ChoiceAlgorithm(int[] arr) {
Thread view = new Thread() {
public void run() {
jf.setVisible(false);
int arrr[] = arr.clone();
new Algorithm(arrr, algorithm[0], i).maopao(arrr);// 调用冒泡排序法
};
};
Thread view1 = new Thread() {
public void run() {
jf.setVisible(false);
int arrr[] = arr.clone();
new Algorithm(arrr, algorithm[1], i).xier(arrr);// 调用希尔排序法
};
};
Thread view2 = new Thread() {
public void run() {
jf.setVisible(false);
int arrr[] = arr.clone();
new Algorithm(arrr, algorithm[2], i).heapSort(arrr);// 推排序
};
};
Thread view3 = new Thread() {
public void run() {
jf.setVisible(false);
int arrr[] = arr.clone();
new Algorithm(arrr, algorithm[3], i).selectionSort(arrr);// 选择排序
};
};
Thread view4 = new Thread() {
public void run() {
jf.setVisible(false);
int arrr[] = arr.clone();
new Algorithm(arrr, algorithm[4], i).mergeSort(arrr, 0, arrr.length - 1);// 递归排序
};
};
this.arr = arr;
jf.setSize(400, 200);// 窗口大小
jf.setVisible(true);// 设置可见度
// jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 设置默认关闭动作
jf.setLocationRelativeTo(null);// 窗口居中
jf.add(jpl);
jpl.setLayout(new FlowLayout());
jbt = new JButton[algorithm.length];
for (int j = 0; j < jbt.length; j++) {
jbt[j] = new JButton(algorithm[j]);
jpl.add(jbt[j]);
}
JButton sheng, jiang;
sheng = new JButton("升序");
jiang = new JButton("降序");
jpl.add(sheng);
jpl.add(jiang);
sheng.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
i = 0;
}
});
jiang.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
i = 1;
}
});
jbt[0].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view.start();
}
});
jbt[1].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view1.start();
}
});
jbt[2].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view2.start();
}
});
jbt[3].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view3.start();
}
});
jbt[4].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view4.start();
}
});
jbt[5].addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view.start();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
view1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
view2.start();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
view3.start();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
view4.start();
}
});
}
}
线程MyThread.java:
package com.thread;
public class MyThread {
Thread view = new Thread() {
public void run() {
};
};
}
第三个界面当然就是我们最终的排序可视化界面了:MyJPanel.java。在这里,我进行了坐标的精心调整,以便在数字较多的情况下还可以完整展现出来排序过程。另外,排序的过程会以txt的形式保存在项目中。
package com.Main;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JPanel;
public class MyJPanel extends JPanel {
private static final long serialVersionUID = -5009968632196882066L;
private int pX = 10, pY = 70;// 初始位置
public int[] arr;
public ArrayList<String> syso = new ArrayList<String>();
public int initSysoY = 140;
public int l;
public int q = 0;
public void setSyso(ArrayList<String> syso) {
this.syso = syso;
}
public MyJPanel(int[] arr, int l) {
super();
this.arr = arr;
this.l = l;
}
public void myPaint() {
this.repaint();
q++;
}
@Override
public void paint(Graphics g) {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.paint(g);
g.clearRect(0, 0, 600, 600);
int tempPX = pX;
if (l == 0) {//升序
for (int i = 0; i < arr.length; i++) {
g.fillRect(tempPX, pY - arr[i], 20, arr[i]);
g.drawString(arr[i] + "", tempPX + 6, pY + 15);
// pY += 30;
tempPX += 50;
}
}
if (l == 1) {
for (int i = arr.length - 1; i >= 0; i--) {
g.fillRect(tempPX, pY - arr[i], 20, arr[i]);
g.drawString(arr[i] + "", tempPX + 6, pY + 15);
// pY += 30;
tempPX += 50;
}
}
g.setFont(g.getFont().deriveFont(15f));//改字体大小
for (int i = q * 5; i < syso.size(); i++) {
g.drawString("第" + i + "次交换" + syso.get(i), 0, initSysoY + 25 * i);
}
}
}
差点忘记了,还有我们排序时所看到的矩形的坐标初始化文件:Rect.java。
package com.GUI;
public class Rect {
public int pX, pY, wX, hY;
public Rect(int pX, int pY, int wX, int hY) {
this.pX = pX;
this.pY = pY;
this.wX = wX;
this.hY = hY;
}
}
最终,Main.java,开启你的排序可视化吧!
package com.Main;
//import com.GUI.InitArr;
import com.GUI.RandomGUI;
public class Main {
public static void main(String[] args) {
new RandomGUI();
//new InitArr();
}
}
一共是八个文件,由main为入口,RandomGUI为出发点,灵活地运用java语言写了五大排序算法的可视化过程,生动形象地展示了不同算法之间在不同情况下效率的差异。
细心的同学会发现,在主函数中,我注释掉了一条new InitArr(); ,这其实是我们程序的第四个页面,如果你不想生成随机数,它可供你手动输入一串数字(数组),和RandomGUI同样作为程序入口。
附上其代码:
package com.GUI;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class InitArr {
private JFrame jf = new JFrame();
private JPanel jpl = new JPanel();
private JButton jb = new JButton("确认");// 按钮
private JButton jb1 = new JButton("取消");
private JTextField jtf1 = new JTextField("输入数组");// 文本框
private JTextField jtf11 = new JTextField();
int[] arr;
public InitArr() {
jf.setSize(600, 600);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
jf.setTitle("排序可视化");// 设置标题
jf.add(jpl);
// jpl.setLayout(new GridLayout(2, 2));// 表格布局2行2列
jpl.setLayout(null);
jtf1.setEditable(false);// 文本框不可编辑
jtf1.setSize(new Dimension(200, 200));
jtf11.setSize(new Dimension(200, 200));
jb.setSize(new Dimension(100, 50));
jb1.setSize(new Dimension(100, 50));
jtf1.setLocation(50, 0);
jb.setLocation(50, 300);
jtf11.setLocation(300, 0);
jb1.setLocation(300, 300);
jpl.add(jtf1);
jpl.add(jtf11);
jpl.add(jb);
jpl.add(jb1);
jb1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String tempArr[] = jtf11.getText().split(" ");
arr = new int[tempArr.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(tempArr[i]);
}
// GUI g = new GUI(arr, 0, 0);// 实例化窗口
new ChoiceAlgorithm(arr);
}
});
}
}
可以看到,最快的是选择排序,然后是希尔、堆排。当然,我们后续可以加入更多的排序算法,由于分包管理,代码修改也不会很麻烦。
最后的最后,附上八大排序比较表,供大家参考。
排序方式 | 平均时间复杂度 |
---|---|
插入 | O(n²) |
希尔 | O(n^1.3) |
冒泡 | O(n²) |
快速 | O(nlog2n) |
选择 | O(n²) |
堆排 | O(nlog2n) |
归并 | O(nlog2n) |
基数 | O(d(n+r)) |