publicclassHelper{ //交换arr[i]和arr[j] publicstaticvoidswap(int[] arr, int i, int j){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //产生一个范围在rangeL-rangeR,容量为n的数组 publicstaticint[] generateArray(int n, int rangeL, int rangeR) { Random random = new Random(); int[] arr = newint[n]; for (int i = 0; i < n; i++) { arr[i] = random.nextInt(rangeR - rangeL + 1) + rangeL; }
return arr; } //判断数组是否有序(从小到大) publicstaticbooleanisSorted(int[] arr){ for (int i = 0; i < arr.length - 1; i++) { if (arr[i] > arr[i + 1]) { returnfalse; } } returntrue; } //测试排序需要的时间 需要传入一个函数接口Sort publicstaticdoubletestTime(int[] arr, Sort sort){ long startTime = System.nanoTime(); sort.sort(arr); long endTime = System.nanoTime(); return (endTime - startTime) / 1000000000.0; } }
publicstaticvoidsort(int[] arr){ int N = arr.length; int h = 1; while (h < N / 3) { h = 3*h + 1; // 1 4 13 40 121 ... }
// h = 1的时候就是对整个数组进行插入排序 while (h >= 1) { //每隔h个元素(将数组分成了h组)进行插入排序 for (int i = h; i < N; i++) { for (int j = i; j > h - 1; j -= h) { if (arr[j - h] > arr[j]) { Helper.swap(arr, j, j-h); } else { break; } } } h = h/3; } }
publicstaticvoidmain(String[] args){ int n = 100000; int m = 10; double time = 0; for (int i = 0; i < m; i++) { int[] arr = Helper.generateArray(n, 0, n); time += Helper.testTime(arr, ShellSort::sort); System.out.println("isSorted: " + Helper.isSorted(arr)); } time = time / m; System.out.println(time + "s"); }
在看快速排序算法前我们先来看一个问题,给定一个数,要求数组左边的数都小于等于这个数,数组右边的数都大于这个数,请问这个算法怎么写。首先我们将数组标记为小于等于区和大于区,并用 less 和 more 标记区域的范围,所有 index <= less 的元素都小于指定数,所有 index >= more 的都大于指定数,下图将讲解具体的算法
上面的过程我们称为 partition,对应的代码如下
int e = 6; publicstaticintpartition(int[] arr, int L, int R){ int less = L - 1; int more = R + 1; int cur = L; while (cur < more) { if (arr[cur] <= e) { //如果当前元素小于指定元素 less和cur向前移动 less++; cur++; } elseif (arr[cur] > e) { Helper.swap(arr,--more,cur); } else { cur++; } } //返回两个数组的分界处 return less; }
publicstaticvoidquickSort(int[] arr, int l, int r){ //当数组较小时,使用插入排序 if (r - l < 15) { Helper.insertSort(arr, l, r); return; } if (l < r) { int p = partition(arr, l, r); quickSort(arr, l, p); quickSort(arr, p + 1, r); } }
publicstaticintpartition(int[] arr, int L, int R){ int less = L - 1; int more = R + 1;
int cur = L; while (cur < more) { if (arr[cur] <= arr[R]) { less++; cur++; } elseif (arr[cur] > arr[R]) { Helper.swap(arr,--more,cur); } } return less; } }
//将数组变成堆结构 for (int i = 0; i < arr.length; i++) { heapInsert(arr,i); }
int heapSize = arr.length; while (heapSize > 0) { Helper.swap(arr,0,--heapSize); heapify(arr,0,heapSize); } }
publicstaticvoidheapInsert(int[] arr, int index){ while (arr[index] > arr[(index-1)/2]) { Helper.swap(arr,index,(index-1)/2); index = (index - 1)/2; }
}
publicstaticvoidheapify(int[] arr, int index, int heapSize){ int left = 2 * index + 1;
while (left < heapSize) { int right = left + 1; //假设左更大 int larger = left; //如果右比左大 改变larger为右 if (right < heapSize && arr[right] > arr[left]) { larger = right; }
//如果父比左右子节点都大,说明还是堆,直接退出循环 if (arr[larger] < arr[index]) { break; }
//如果不是,那么就和比较大的交换交换 Helper.swap(arr,larger,index); index = larger; left = index * 2 + 1; } }
publicstaticvoidmain(String[] args){ int n = 100000; int m = 10; double time = 0; for (int i = 0; i < m; i++) { int[] arr = Helper.generateArray(n, 0, n); time += Helper.testTime(arr, HeapSort::sort); System.out.println("isSorted: " + Helper.isSorted(arr)); } time = time / m; System.out.println(time + "s"); } }