Java排序动态可视化(冒泡+希尔+堆排+选择+归并)

Java排序可视化(冒泡+希尔+堆排+选择+递归

1. 效果展示

Java排序动态可视化(冒泡+希尔+堆排+选择+归并)_第1张图片
all是五种排序同时进行,以便更好观察,文末会附图说明。
Java排序动态可视化(冒泡+希尔+堆排+选择+归并)_第2张图片
这里选择的是选择排序升序。
Java排序动态可视化(冒泡+希尔+堆排+选择+归并)_第3张图片

2. 思路+代码

在熟练掌握了这五种排序思想后,首先我们在Algorithm.java中写好排序算法的代码,同时写好GUI界面总体规划(GUI.java),实例化JFrame组件,为后面的主要界面设计(RandomGUI.javaChoiceAlgorithm.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();
	}
}

3. 后续说明

一共是八个文件,由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);
			}
		});

	}
}

4. 最终效果

Java排序动态可视化(冒泡+希尔+堆排+选择+归并)_第4张图片
可以看到,最快的是选择排序,然后是希尔、堆排。当然,我们后续可以加入更多的排序算法,由于分包管理,代码修改也不会很麻烦。
Java排序动态可视化(冒泡+希尔+堆排+选择+归并)_第5张图片

5. 排序比较

最后的最后,附上八大排序比较表,供大家参考。

排序方式 平均时间复杂度
插入 O(n²)
希尔 O(n^1.3)
冒泡 O(n²)
快速 O(nlog2n)
选择 O(n²)
堆排 O(nlog2n)
归并 O(nlog2n)
基数 O(d(n+r))

你可能感兴趣的:(算法)