【恋上数据结构】排序算法前置知识及代码环境准备

感谢小码哥的恋上数据结构,记录课程笔记。

建议先看一下这篇博客: 复杂度知识以及LeetCode刷题指南

我的《恋上数据结构》源码(第1季 + 第2季):https://github.com/szluyu99/Data_Structure_Note
在这里插入图片描述

何为排序?

  • 排序前: 3 , 1 , 6 , 9 , 2 , 5 , 8 , 4 , 7 3, 1, 6, 9, 2, 5, 8, 4, 7 3,1,6,9,2,5,8,4,7
  • 升序排序: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 1, 2, 3, 4, 5, 6, 7, 8, 9 1,2,3,4,5,6,7,8,9
  • 降序排序: 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 9, 8, 7, 6, 5, 4, 3, 2, 1 9,8,7,6,5,4,3,2,1

何为稳定性?

如果相等的 2 个元素,在排序前后的相对位置保持不变那么这是稳定的排序算法。

  • 排序前: 5 , 1 , 3 a , 7 , 3 b 5, 1, 3_a, 7, 3_b 5,1,3a,7,3b
  • 稳定的排序: 1 , 3 a , 3 b , 4 , 5 , 7 1, 3_a, 3_b, 4, 5, 7 1,3a,3b,4,5,7
  • 不稳定的排序: 1 , 3 b , 3 a , 4 , 5 , 7 1, 3_b, 3_a, 4, 5, 7 1,3b,3a,4,5,7

自定义对象进行排序时,稳定性会影响最终的排序效果。

何为原地算法?

  • 不依赖额外的资源或者依赖少数的额外资源,仅依靠输出来覆盖输入

  • 空间复杂度为 O ( 1 ) O(1) O(1)的都可以认为是原地算法

  • 非原地算法,称为 Not-in-place 或者 Out-of-place

时间复杂度的知识

请看这个:复杂度知识以及LeetCode刷题指南

写排序算法前的准备

项目结构

创建一个项目,大体结构是这样的:
在这里插入图片描述
以下包名省略 com.mj

  • sort:各种排序类
    • cmp:比较类排序
    • sort.java 排序抽象类
  • tools:排序中用到的工具
    • Asserts.java 单元测试
    • Inegers.java 生成测试数据
    • Time.java 计算时间
  • Main 主函数,写排序测试
  • Student.java 利用自定义类测试稳定性(sort.java中使用)

Sort.java

写一个抽象类 Sort.java

  • 实现自定义对象的比较(cmp),交换(swap)方法
  • 同时可以用来计算比较次数(cmpCount),交换次数(swapCount),花费时间(time)
  • 排序是否稳定()
@SuppressWarnings("unchecked")
public abstract class Sort<T extends Comparable<T>> implements Comparable<Sort<T>> {
	protected T[] array;
	private int cmpCount;
	private int swapCount;
	private long time;
	private DecimalFormat fmt = new DecimalFormat("#.00");
	
	public void sort(T[] array) {
		if (array == null || array.length < 2) return;
		
		this.array = array;
		
		long begin = System.currentTimeMillis();
		sort();
		time = System.currentTimeMillis() - begin;
	}
	
	@Override
	public int compareTo(Sort<T> o) {
		int result = (int)(time - o.time);
		if (result != 0) return result;
		
		result = cmpCount - o.cmpCount;
		if (result != 0) return result;
		
		return swapCount - o.swapCount;
	}
	
	protected abstract void sort();
	
	/*
	 * 返回值等于0,代表 array[i1] == array[i2]
	 * 返回值小于0,代表 array[i1] < array[i2]
	 * 返回值大于0,代表 array[i1] > array[i2]
	 */
	protected int cmp(int i1, int i2) {
		cmpCount++;
		return array[i1].compareTo(array[i2]);
	}
	
	protected int cmp(T v1, T v2) {
		cmpCount++;
		return v1.compareTo(v2);
	}
	
	protected void swap(int i1, int i2) {
		swapCount++;
		T tmp = array[i1];
		array[i1] = array[i2];
		array[i2] = tmp;
	}
	
	@Override
	public String toString() { 
		String timeStr = "耗时:" + (time / 1000.0) + "s(" + time + "ms)";
		String compareCountStr = "比较:" + numberString(cmpCount);
		String swapCountStr = "交换:" + numberString(swapCount);
		String stableStr = "稳定性:" + isStable();
		return "【" + getClass().getSimpleName() + "】\n" 
				+ stableStr + " \t"
				+ timeStr + " \t"
				+ compareCountStr + "\t "
				+ swapCountStr + "\n"
				+ "------------------------------------------------------------------";
	}
	
	private String numberString(int number) {
		if (number < 10000) return "" + number;
		
		if (number < 100000000) return fmt.format(number / 10000.0) + "万";
		return fmt.format(number / 100000000.0) + "亿";
	}
	
	private boolean isStable() {
		if (this instanceof RadixSort) return true;
		if (this instanceof CountingSort) return true;
		if (this instanceof ShellSort) return false;
		if (this instanceof SelectionSort) return false;
		
		Student[] students = new Student[20];
		for (int i = 0; i < students.length; i++) {
			students[i] = new Student(i * 10, 10);
		}
		sort((T[]) students);
		for (int i = 1; i < students.length; i++) {
			int score = students[i].score;
			int prevScore = students[i - 1].score;
			if (score != prevScore + 10) return false;
		}
		return true;
	}
}

Asserts.java

public class Asserts {
	public static void test(boolean value) {
		try {
			if (!value) throw new Exception("测试未通过");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Integers.java

public class Integers {
	public static Integer[] random(int count, int min, int max) {
		if (count <= 0 || min > max) return null;
		Integer[] array = new Integer[count];
		int delta = max - min + 1;
		for (int i = 0; i < count; i++) {
			array[i] = min + (int)(Math.random() * delta);
		}
		return array;
	}
	
	public static Integer[] combine(Integer[] array1, Integer[] array2) {
		if (array1 == null || array2 == null) return null;
		Integer[] array = new Integer[array1.length + array2.length];
		for (int i = 0; i < array1.length; i++) {
			array[i] = array1[i];
		}
		for (int i = 0; i < array2.length; i++) {
			array[i + array1.length] = array2[i];
		}
		return array;
		
	}
	
	public static Integer[] same(int count, int unsameCount) {
		if (count <= 0 || unsameCount > count) return null;
		Integer[] array = new Integer[count];
		for (int i = 0; i < unsameCount; i++) {
			array[i] = unsameCount - i;
		}
		for (int i = unsameCount; i < count; i++) {
			array[i] = unsameCount + 1;
		}
		return array;
	}
	
	public static Integer[] headTailAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		
		int begin = (array.length - disorderCount) >> 1;
		reverse(array, begin, begin + disorderCount);
		return array;
	}
	
	public static Integer[] centerAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		int left = disorderCount >> 1;
		reverse(array, 0, left);
		
		int right = disorderCount - left;
		reverse(array, array.length - right, array.length);
		return array;
	}
	
	public static Integer[] headAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		reverse(array, array.length - disorderCount, array.length);
		return array;
	}
	
	public static Integer[] tailAscOrder(int min, int max, int disorderCount) {
		Integer[] array = ascOrder(min, max);
		if (disorderCount > array.length) return array;
		reverse(array, 0, disorderCount);
		return array;
	}
	
	public static Integer[] ascOrder(int min, int max) {
		if (min > max) return null;
		Integer[] array = new Integer[max - min + 1];
		for (int i = 0; i < array.length; i++) {
			array[i] = min++;
		}
		return array;
	}
	
	public static Integer[] descOrder(int min, int max) {
		if (min > max) return null;
		Integer[] array = new Integer[max - min + 1];
		for (int i = 0; i < array.length; i++) {
			array[i] = max--;
		}
		return array;
	}
	
	/**
	 * 反转一个数组,索引范围是[begin, end)
	 */
	private static void reverse(Integer[] array, int begin, int end) {
		int count = (end - begin) >> 1;
		int sum = begin + end - 1;
		for (int i = begin; i < begin + count; i++) {
			int j = sum - i;
			int tmp = array[i];
			array[i] = array[j];
			array[j] = tmp;
		}
	}
	
	public static Integer[] copy(Integer[] array) {
		return Arrays.copyOf(array, array.length);
	}
	
	public static boolean isAscOrder(Integer[] array) {
		if (array == null || array.length == 0) return false;
		for (int i = 1; i < array.length; i++) {
			if (array[i - 1] > array[i]) return false;
		}
		return true;
	}
	
	public static void println(Integer[] array) {
		if (array == null) return;
		StringBuilder string = new StringBuilder();
		for (int i = 0; i < array.length; i++) {
			if (i != 0) string.append("_");
			string.append(array[i]);
		}
		System.out.println(string);
	}
}

Times.java

public class Times {
	private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
	
	public interface Task {
		void execute();
	}
	
	public static void test(String title, Task task) {
		if (task == null) return;
		title = (title == null) ? "" : ("【" + title + "】");
		System.out.println(title);
		System.out.println("开始:" + fmt.format(new Date()));
		long begin = System.currentTimeMillis();
		task.execute();
		long end = System.currentTimeMillis();
		System.out.println("结束:" + fmt.format(new Date()));
		double delta = (end - begin) / 1000.0;
		System.out.println("耗时:" + delta + "秒");
		System.out.println("-------------------------------------");
	}
}

Student.java

public class Student implements Comparable<Student> {
	public int score;
	public int age;
	
	public Student(int score, int age) {
		this.score = score;
		this.age = age;
	}
	
	@Override
	public int compareTo(Student o) {
		return age - o.age;
	}
}

排序算法测试

把上面的工具代码配置好,我们就可以来写排序算法并且测试啦。

首先写一个简单的冒泡排序

由于冒泡排序属于比较排序,我们将它放到cmp包下。
在这里插入图片描述

BubbleSort1.java

/**
 * 冒泡排序-无优化
 */
public class BubbleSort1<T extends Comparable<T>> extends Sort<T>{
	@Override
	protected void sort() {
		for (int end = array.length - 1; end > 0; end--) {
			for (int begin = 1; begin <= end; begin++) {
				if (cmp(begin, begin - 1) < 0) {
					swap(begin, begin - 1);
				}
			}
		}
	}
}

此时目录结构应该与我一开始发的完全一样了,可以来测试排序代码了。

Main.java

@SuppressWarnings({ "rawtypes", "unchecked" })
public class Main {
	// 在 main 函数里写测试代码
	public static void main(String[] args) {
		// 产生 20000 个数据,每个数据的范围是 1~10000
		Integer[] array = Integers.random(20000, 1, 10000);	
		// 在这里面写要测试的代码
		testSorts(array, 
				new BubbleSort1()		// 冒泡排序
				);
	}
	
	// 下面这个复制就可以
	static void testSorts(Integer[] array, Sort... sorts) {
		for (Sort sort : sorts) {
			Integer[] newArray = Integers.copy(array);
			sort.sort(newArray);
			Asserts.test(Integers.isAscOrder(newArray));
		}
		Arrays.sort(sorts);
		for (Sort sort : sorts) {
			System.out.println(sort);
		}
	}

}

目前只有一个冒泡排序,运行结果如下:
在这里插入图片描述

以后我们写了多个排序算法做测试,效果是这样的:

@SuppressWarnings({ "rawtypes", "unchecked" })
public class Main {
	public static void main(String[] args) {
		// 产生 20000 个数据,每个数据的范围是 1~10000
		Integer[] array = Integers.random(20000, 1, 10000);	

		testSorts(array, 
				new BubbleSort1(),		// 冒泡排序
//				new BubbleSort2(),		// 冒泡排序-优化1
//				new BubbleSort3(),		// 冒泡排序-优化2
				new SelectionSort(), 	// 选择排序
				new HeapSort()  		// 堆排序
//				new InsertionSort1(),   // 插入排序
//				new InsertionSort2(), 	// 插入排序-挪动优化
				new InsertionSort3(),	// 插入排序-二分查找优化
				new MergeSort(),		// 归并排序
				new QuickSort(),		// 快速排序
				new ShellSort(),		// 希尔排序
				new CountingSort(),		// 计数排序
				new RadixSort()			// 基数排序
				);
	}
	
	static void testSorts(Integer[] array, Sort... sorts) {
		for (Sort sort : sorts) {
			Integer[] newArray = Integers.copy(array);
			sort.sort(newArray);
			Asserts.test(Integers.isAscOrder(newArray));
		}
		Arrays.sort(sorts);
		for (Sort sort : sorts) {
			System.out.println(sort);
		}
	}

}

在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌宅鹿同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值