工具类
编写的排序算法需要经过大量测试保证准确性,所以需要一个工具类生成大量测试案例
public class SortTest {
/**
* @param maxSize 数组大小
* @param maxValue 数组值范围
* @return 随机生成数组集合
*/
public static Integer[] generateRandomArray(int maxSize, int maxValue) {
Integer[] arr = new Integer[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
// [-? , +?]
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
/**
* 拷贝新的数组
*/
public static Integer[] copyArray(Integer[] arr) {
if (arr == null) {
return null;
}
Integer[] res = new Integer[arr.length];
System.arraycopy(arr, 0, res, 0, arr.length);
return res;
}
public static void printArray(Integer[] arr) {
if (arr == null) {
return;
}
StringBuffer sb = new StringBuffer("{");
for (int i= 0; i < arr.length; i++) {
if (i == arr.length - 1) {
sb.append(arr[i]);
} else {
sb.append(arr[i] + ",");
}
}
sb.append("}");
System.out.println(sb.toString());
}
public static void main(String[] args) {
//测试的次数
int testTime = 50000;
//测试的数组大小
int maxSize = 100;
//测试的数组值范围
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
Integer[] arr1 = generateRandomArray(maxSize, maxValue);
Integer[] arr2 = copyArray(arr1);
//测试的排序算法
SelectSort.sort(arr1);
Arrays.sort(arr2);
//发现不相等
if (!Arrays.equals(arr1, arr2)) {
succeed = false;
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "ERROR!");
}
}
选择排序
思路:
- 第一层循环代表需要存放的最小值索引
- 通过第二层循环找到最小值的索引
- 交换第一层索引和最小索引的值
public class SelectSort<T extends Comparable<T>> {
public static void sort(Comparable[] arr) {
for (int i = 0; i < arr.length; i++) {
int minIdx = i;
for (int j = i + 1; j < arr.length; j++) {
minIdx = less(arr[j], arr[minIdx]) ? j : minIdx;
}
swap(arr, i, minIdx);
}
}
private static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
private static void swap(Comparable[] arr, int i, int minIdx) {
Comparable tmp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = tmp;
}
}
冒泡排序
思路:
- 第一层循环缩小需要排序的范围
- 第二层循环交换最大值到末尾
- 第一层循环等于1就结束,第二次循环最大值<=第一层循环的值
public static void sort(Comparable[] arr) {
for (int i = arr.length - 1; i > 0; i--) {
//第一层循环是当做需要排序的范围
for (int j = 0; j < i; j++) {
//通过第二层循环冒泡 将最大值向后移动
if (less(arr[j + 1], arr[j])) {
swap(arr, j, j + 1);
}
}
}
}
插入排序
思路:
- 左边的都是排序好的
- 新的数在左边排序好的数组里面从右向左插入
for + while
public static void sort(Comparable[] arr) {
for (int end = 1; end < arr.length; end++) {
//第一层循环是需要选择插入的已排序好的数组范围
int pre = end;
//第二层循环从右向左开始比较 选择合适位置插入
while (pre - 1 >= 0 && less(arr[pre] , arr[pre - 1])) {
swap(arr, pre, pre - 1);
pre--;
}
}
}
双层for
public static void sort(Comparable[] arr) {
for (int end = 1; end < arr.length; end++) {
//pre -1 >=0 没有索引溢出,并且新插入的值比左边的小 需要向左移动
for (int pre = end; pre - 1 >= 0 && less(arr[pre] , arr[pre - 1]); pre--) {
swap(arr, pre, pre - 1);
}
}
}
归并排序
public class MergeSort<T extends Comparable<T>> {
public static void mergeSort(Comparable[] arr) {
//边界判断
if(arr == null || arr.length == 0) {
return;
}
process(arr, 0, arr.length - 1);
}
private static void process(Comparable[] arr, int L, int R) {
if (L == R) {
return;
}
//计算数组中间位置 防止超过int最大值,使用 L + (R -L)/2计算
int mid = L + ((R - L) >> 1);
//左边排序
process(arr, L, mid);
//右边排序
process(arr, mid + 1, R);
//合并左右有序数组
merge(arr, L, mid, R);
}
private static void merge(Comparable[] arr, int L,
int mid, int R) {
Comparable[] helper = new Comparable[R - L + 1]; //收集排序好的数据
int i = 0; //辅助数组索引位置
int p1 = L;
int p2 = mid + 1;
while (p1 <= mid && p2 <= R) {
helper[i++] = less(arr[p1], arr[p2]) ? arr[p1++] : arr[p2++];
}
while (p1 <= mid) {
helper[i++] = arr[p1++];
}
while (p2 <= R) {
helper[i++] = arr[p2++];
}
for (i = 0; i < helper.length; i++) {
arr[L + i] = helper[i];
}
}
private static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
}
快速排序
思路:
- 先找到一个基准点pivot,通过left和right指针记录数组头部和尾部
- right指针向左扫描 找到一个比pivot的值小的元素
- left指针向右扫描,找到一个比pivot的值大的元素
- 交换left和right位置的元素
- 重复2、3、4步骤 直到left>=right的值,说明这轮循环结束
- swap(pivot, right) 返回right索引的位置将原数组分隔成左右两个数组 重复1、2、3、4、5步骤
public class QuickSort<T extends Comparable<T>> {
public static void quickSort(Comparable[] arr) {
//边界判断
if(arr == null || arr.length == 0) {
return;
}
sort(arr, 0, arr.length - 1);
}
private static void sort(Comparable[] arr, int L, int R) {
if (L >= R) {
return;
}
//对L到R范围内的数组进行切分
int pivot = pivot(arr, L, R);
//左边排序
sort(arr, L, pivot - 1);
//右边排序
sort(arr, pivot + 1, R);
}
private static int pivot(Comparable[] arr, int L, int R) {
Comparable key = arr[L];
int left = L;
int right = R + 1;
while(true) {
//从左向右扫描 找到一个比key大的元素
while (less(arr[++left], key)) {
//扫描到最右边 停止扫描
if(left == R) {
break;
}
}
//从右向左扫描 找到一个比key小的元素
while (less(key, arr[--right])) {
//扫描到最左边 停止扫描
if(right == L) {
break;
}
}
if(left < right) {
swap(arr, left, right);
} else {
break;
}
}
swap(arr, L, right);
return right;
}
private static void swap(Comparable[] arr, int left, int right) {
Comparable tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
private static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
}