代码
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.Scanner;
import java.util.Stack;
public class 算法评测 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
int n = scan.nextInt(); //创建 N 个随机数赋值给数组
if (n < 2) break;
Random ran = new Random();
int[] arr = new int[n];
for (int i = 0; i < arr.length; i++) { //填充随机数给数组
arr[i] = ran.nextInt(n);
}
int[] bak = arr.clone(); //备份
System.out.println(n + " 个数据排序 排序中... Running...");
for (int i = 5; i <= 12; i++) { //控制排序算法种类范围
long startTime = System.currentTimeMillis();
String mode = "";
try {
switch (i) {
case 1:
mode = "选择排序";
selectSort(arr);
break;
case 2:
mode = "冒泡排序";
bubbleSort(arr);
break;
case 3:
mode = "插入排序";
insertSort(arr);
break;
case 4:
mode = "希尔排序";
shellSort(arr);
break;
case 5:
mode = "快速排序";
quickSort(arr);
break;
case 6:
mode = "迭代快速";
quickSortStack(arr);
break;
case 7:
mode = "归并排序";
mergeSort(arr);
break;
case 8:
mode = "迭代归并";
mergeSortStack(arr);
break;
case 9:
mode = "计数排序";
countSort(arr);
break;
case 10:
mode = "计数TWO";
countTwoSort(arr);
break;
case 11:
mode = "基数排序";
// baseSort(arr);
break;
case 12:
mode = "API并行排序(数量 < N 快速 > N 归并 (???))";
Arrays.parallelSort(arr);
break;
}
} catch (Error e) {
System.out.print("狗带了 ————> ");
}
double runningTime = System.currentTimeMillis() - startTime;
System.out.println(mode + " == " + (runningTime / 1000) + "s");
arr = bak.clone(); //重置数组
}
System.out.println("Sort Over!");
}
scan.close();
}
public static void swap(int[] arr, int a, int b) {
arr[a] = arr[a] ^ arr[b];
arr[b] = arr[a] ^ arr[b];
arr[a] = arr[a] ^ arr[b];
}
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int targetIndex = i; //一直寻找最小值的下标
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[targetIndex]) {
targetIndex = j;
}
}
if (targetIndex != i) {
swap(arr, i, targetIndex);
}
}
}
public static 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]) {
swap(arr, j, j + 1);
}
}
}
}
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) { //默认第一个为最小
int bak = arr[i]; //备份找到的当前索引
int insertIndex = i; //记录插入索引位置
for (int j = i - 1; j >= 0; j--) {
if (bak < arr[j]) { //不使用交换方法
arr[insertIndex] = arr[j]; //移动
insertIndex = j;
} else { //比前面的大,直接停止
break;
}
}
if (insertIndex != i) { //自身无需交换
arr[insertIndex] = bak;
}
}
}
public static void shellSort(int[] arr) { //插入排序升级
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int bak = arr[i];
int insertIndex = i; //定义倒退索引
while (insertIndex - gap >= 0 && bak < arr[insertIndex - gap]) {
arr[insertIndex] = arr[insertIndex - gap]; //移动
insertIndex -= gap; //最多等于 0,不怕越界
}
if (insertIndex != i) { //不等于本身时,插入
arr[insertIndex] = bak;
}
}
}
}
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int startIndex, int endIndex) { //优化版
int i = startIndex;
int j = endIndex;
while (i < j) {
while (j >= arr[startIndex] && j > i) j--; //寻找小于基数值
while (i <= arr[startIndex] && i < j) i++; //寻找大于基数值
if (i < j) { //不是交换基数时
swap(arr, i, j);
}
}
if (i != startIndex) { //不交换自己
swap(arr, startIndex, i);
}
if (startIndex < i - 1) quickSort(arr, startIndex, i - 1);
if (i + 1 < endIndex) quickSort(arr, i + 1, endIndex);
}
static class Quick {
public int startIndex;
public int endIndex;
public Quick(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
}
}
public static void quickSortStack(int[] arr) { //Stack 非递归优化版,快排特性是每个区域的排序都是互不相干的,所以也可以使用队列
Stack<Quick> stack = new Stack<>();
stack.push(new Quick(0, arr.length - 1));
while (! stack.isEmpty()) {
Quick pop = stack.pop(); //快速排序直接取出即可,因为只有区间排序
//二分每次最左边的数作为基数
int i = pop.startIndex;
int j = pop.endIndex;
while (i < j) { //实行检查交换, 基数从最左边开始时,一定要让 j 先走
while (arr[j] >= arr[pop.startIndex] && j > i) j--; //寻找小于 base 的数
while (arr[i] <= arr[pop.startIndex] && i < j) i++; //寻找大于 base 的数
if (i < j) { //交换
swap(arr, i, j);
}
}
//交换基数
if (i != pop.startIndex) { //基数自己不需要交换
swap(arr, pop.startIndex, i);
}
if (pop.startIndex < i - 1) { //压栈左区间
stack.push(new Quick(pop.startIndex, i - 1));
}
if (i + 1 < pop.endIndex) { //压栈右区间
stack.push(new Quick(i + 1, pop.endIndex));
}
}
}
public static int[] countSort(int[] arr) {
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) { //打擂台
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
int[] prefix = new int[max - min + 1]; //创建前缀和数组
for (int i = 0; i < arr.length; i++) prefix[arr[i] - min]++; //记录每个数出现个数
for (int i = 1; i < prefix.length; i++) prefix[i] = prefix[i] + prefix[i - 1]; //计算前缀和
int[] result = new int[arr.length]; //创建排序数组
for (int i = 0; i < arr.length; i++) {
result[prefix[arr[i] - min] - 1] = arr[i]; //精准打击
prefix[arr[i] - min]--; //更新数个数
}
return result;
}
public static void countTwoSort(int[] arr) {
// 打擂台
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
// 创建长度
int[] count = new int[max - min + 1]; // + 1 是加上 min 本身被减去的数
// 计算每个数出现个数
for (int i = 0; i < arr.length; i++) count[arr[i] - min]++;
// 计算并写入源数组
for (int i = 0, index = 0; i < count.length; i++) {
while (count[i] > 0) {
arr[index++] = i + min;
count[i]--;
}
}
}
public static void mergeSort(int[] arr) {
mergeSort(arr, new int[arr.length], 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int[] temp, int startIndex, int endIndex) { //优化版
if (startIndex < endIndex) { //直到分成 1 个,递归回归后,从最低 2 个开始合并
int mid = (endIndex + startIndex) / 2;
mergeSort(arr, temp, startIndex, mid);
mergeSort(arr, temp, mid + 1, endIndex);
merge(arr, temp, startIndex, mid, endIndex);
}
}
public static void merge(int[] arr, int[] temp, int startIndex, int mid, int endIndex) {
int i = startIndex;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= endIndex) { //要么左边区域先存完,要么右边先存完
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) temp[k++] = arr[i++]; //把没存完的直接一次性存完
while (j <= endIndex) temp[k++] = arr[j++];
for (int x = 0; x < k; x++) { //把排序好的存进主数组,x < k 时则是小于之前的 k++,所以避免越开真实排序数据
arr[startIndex++] = temp[x]; //从 startIndex 开始存入
}
}
static class Area {
public int left;
public int right;
public boolean isBinary; //重要标识,判断是否已经二分过,没有则继续二分,然后标识 true
public Area (int left, int right) {
this.left = left;
this.right = right;
}
}
static class Merge extends Area {
public int mid;
public Merge(int left, int mid, int right, boolean isBinary) {
super(left, right);
this.mid = mid;
this.isBinary = isBinary;
}
}
public static void mergeSortStack(int[] arr) { //非递归版
Stack<Area> stack = new Stack<>();
stack.push(new Merge(0, (arr.length - 1) / 2, arr.length - 1, false)); //初始化最底层合并对象
int[] temp = new int[arr.length];
while (! stack.isEmpty()) {
Area peek = stack.peek(); //归并需要拿出来看,而不是直接取,最后需要合并
if (peek.isBinary) { //判断是否已二分
if (peek instanceof Merge) { //此时要么是 Merge 对象,要么是 Area 对象
merge(arr, temp, peek.left, ((Merge) peek).mid, peek.right);
stack.pop();
} else { //Area 直接弹栈
stack.pop();
}
} else {
int left = peek.left;
int right = peek.right;
if (left < right) { //直至剩余一个时,则弹栈,然后就是从最小两个开始合并
peek.isBinary = true; //标记已二分操作
int mid = (left + right) / 2;
// 添加对象入栈时,要按常规递归版倒序入栈,把合并对象先压栈,区域对象后入栈,是为了查看区域对象是否已经二分,没有则继续二分
// 最关键是,存储的压栈关系,Area <—— Area <—— Merge <—— Area <—— Area <—— Merge <—— Area <—— Area <—— Merge(这个是最开始入栈的合并对象)
stack.push(new Merge(left, mid, right, true)); //之后的操作一直为是已经二分的状态 true
stack.push(new Area(mid + 1, right)); //右区间
stack.push(new Area(left, mid)); //左区间
} else { //在等于 1 个元素时,直接弹出栈
stack.pop();
}
}
}
}
@SuppressWarnings("unchecked")
public static void baseSort(int[] arr) { //优化版
Queue<Integer>[] queue = new LinkedList[10]; //创建10个桶
for (int i = 0; i < queue.length; i++) queue[i] = new LinkedList<>();
int maxRadix = 1;
for (int i = 0; i < arr.length; i++) if (arr[i] > maxRadix) maxRadix = arr[i];
maxRadix = String.valueOf(maxRadix).length(); //转换成遍历的最大次数与数各位
for (int i = 0; i < maxRadix; i++) {
int pow = (int) Math.pow(10, i); //直接利用数学,把后面的数除掉,取最后一个位置,即可达到目的
for (int j = 0; j < arr.length; j++) { //入桶
queue[arr[j] / pow % 10].offer(arr[j]);
}
for (int j = 0, x = 0; j < arr.length; x++) { //回写源数组
while (! queue[x].isEmpty()) {
arr[j++] = queue[x].poll();
}
}
}
}
}
输出
10000000
10000000 个数据排序 排序中... Running...
狗带了 ————> 快速排序 == 0.588s
迭代快速 == 1.162s
归并排序 == 1.186s
迭代归并 == 1.474s
计数排序 == 0.349s
计数TWO == 0.164s
基数排序 == 0.0s
API并行排序(数量 < N 快速 > N 归并 (???)) == 0.381s
Sort Over!
100000000
100000000 个数据排序 排序中... Running...
狗带了 ————> 快速排序 == 0.612s
迭代快速 == 12.981s
归并排序 == 13.947s
迭代归并 == 16.608s
计数排序 == 4.622s
计数TWO == 1.946s
基数排序 == 0.0s
API并行排序(数量 < N 快速 > N 归并 (???)) == 1.014s
Sort Over!