首先是快速排序:
package sorting; import java.util.Arrays; public class Sort1 { public static void quickSort(long[] array) { quickSortRange(array, 0, array.length - 1); } // 为了代码书写方便,我们选择使用左闭右闭的区间表示形式 // 让我们对 array 中的从 from 到 to 的位置进行排序,其他地方不用管 // 其中,from,to 下标的元素都算在区间的元素中 // 左闭右闭的情况下,区间内的元素个数 = to - from + 1; /* 时间复杂度: partition 的时间复杂度 O(n) * 树的高度 最好/平均: O(n * log(n)) 最坏: O(n * n) (快排是唯一一个空间复杂度也分情况讨论的排序算法) 空间复杂度:partition 的空间复杂度 O(1) * 树的高度 最好/平均: O(log(n)) 最坏: O(n) 稳定性:保证不了,因为没有可以在做到 O(n) 的情况下,还能保证稳定性的 partition 算法。 */ private static void quickSortRange(long[] array, int from, int to) { // if (to <= from) { // if (to - from < 1) { // if (to - from <= 0) { if (to - from + 1 <= 1) { // 区间中元素个数 <= 1 个 return; } // 挑选中区间最右边的元素 array[to] // array[to] 还是 array[to - 1] 还是 array[array.length] 还是 array[array.length - 1] 呢? int[] pi = partitionMethodD(array, from, to); // 小于等于 pivot 的元素所在的区间如何表示 array, from, pi - 1 // 大于等于 pivot 的元素所在的区间如何表示 array, pi + 1, to // 按照分治算法的思路,使用相同的方式,处理相同性质的问题,只是问题的规模在变小 quickSortRange(array, from, pi[0]); // 针对小于等于 pivot 的区间做处理 quickSortRange(array, pi[1], to); // 针对大于等于 pivot 的区间做处理 } /** * 以区间最右边的元素 array[to] 最为 pivot,遍历整个区间,从 from 到 to,移动必要的元素 * 进行分区 * @param array * @param from * @param to * @return 最终 pivot 所在的下标 */ private static int partitionMethodA(long[] array, int from, int to) { // 1. 先把 pivot 找出来 long pivot = array[to]; // 2. 通过定义 left 和 right 两个下标,将区间划分出来 int left = from; int right = to; // [from, left) 都是 <= pivot 的 // [left, right) 都是未参与比较的 // [right, to] 都是 >= pivot 的 // 循环,保证每个元素都参与了和 pivot 的比较 // 也就是,只要 [left, right) 区间内还有元素,循环就应该继续 while (left < right) { // while (right - left > 0) { // 先让左边进行比较 // 随着 left 在循环过程中一直在 left++,请问 left < right 的条件能一定保证么 // 不一定,所以,我们时刻进行 left < right 条件的保证 // 并且,只有在 left < right 成立的情况下,array[left] 和 pivot 的比较才有意义 // left < right && array[left] <= pivot 的顺序不能交换 while (left < right && array[left] <= pivot) { left++; } // 循环停止时,说明 array[left] > pivot while (left < right && array[right] >= pivot) { right--; } // 循环停止时,说明 array[right] < pivot // 两边都卡住时,交换 [left] 和 [right] 位置的元素 long t = array[left]; array[left] = array[right]; array[right] = t; } // 说明 left == right,说明 [left, right) 区间内一个元素都没有了 // 所有元素都和 pivot 进行过比较了,然后都在各自应该的位置上了 // 并且 array[left] 一定是 >= pivot 的第一个元素(不给大家证明了) long t = array[to]; array[to] = array[left]; array[left] = t; // 返回 pivot 最终所在下标 return left; } public static int partitionMethodB(long[]array,int form,int to){ long pi=array[to]; int left=form; int right=to; while (left<right){ while (left<right&&array[left]<pi) { left++; } array[right]=array[left]; while (left<right&&array[right]>pi) { right--; } array[left]=array[right]; } array[to]=array[left]; array[left]=pi; return left; } /** * 对 array 的 [from, to] 区间进行分区 * 分区完成之后,区间被分割为 [<= pivot] pivot [>= pivot] * 分区过程中,始终保持 * [from, s) 小于 pivot * [s, i) 大于等于 pivot * [i, to) 未比较过的元素 * [to, to] pivot * @param array * @param from * @param to * @return pivot 最终所在下标 */ private static int partitionMethodC(long[] array, int from, int to) { int s = from; long pivot = array[to]; for (int i = from; i < to; i++) { // 遍历 [from, to) // 这里加 == 号也保证不了稳定性,有交换操作 if (array[i] < pivot) { // TODO: 可以进行简单的优化:如果 i == s,就不交换 long t = array[i]; array[i] = array[s]; array[s] = t; s++; } } array[to] = array[s]; array[s] = pivot; return s; } public static int[] partitionMethodD(long[]array,int form,int to){ int i=form; int g=to; int s=form; long pri=array[to]; while (i<g){ if(array[i]==pri){ i++; }else if(array[i]>pri){ long e=array[i]; array[i]=array[g]; array[g]=e; g--; } else { long a=array[i]; array[i]=array[s]; array[s]=a; i++; s++; } } return new int[]{s-1,i}; } public static void insertSortRange(long[]array,int form,int to){ int size=to-form; for(int i=0;i<size;i++){ long key=array[form+1+i]; int j; for(j=form+i;j>=form&&array[j]>key;j--){ array[j+1]=array[j]; } array[j+1]=key; } } public static void main(String[] args) { // long[] array = {-1, -1, -1, -1, 3, 5, 2, 7, 6, 4, 9, 8, -1, -1, -1 }; // long[] array = {-1, -1, -1, -1, 3, 5, 2, 7, 6, 4, 9, 4, -1, -1, -1 }; long[] array = {-1, -1, -1, -1, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1 }; // long[] array = {-1, -1, -1, -1, 8, 7, 6, 5, 4, 3, 2, 1, -1, -1, -1 }; insertSortRange(array,4,11); System.out.println(Arrays.toString(array)); } }
冒泡排序:
public static void sort(long[] array, int size) { for (int i = 0; i < array.length - 1; i++) { for (int j = 0; j < array.length - 1 - i; j++) { if (array[j] > array[j + 1]) { swap(array, j, j + 1); } } } } public static void swap(long[] array, int a, int b) { long e = array[a]; array[a] = array[b]; array[b] = e; }
插入排序:
public static void swap(long[] array, int a, int b) { long e = array[a]; array[a] = array[b]; array[b] = e; } public static void inserSort(long[] array) { // 一共要取多少个元素来进行插入过程(无序区间里有多少个元素) for (int i = 0; i < array.length - 1; i++) { // 有序区间 [0, i] 至少在 i == 0 的时候得有一个元素 // 无序区间 [i + 1, n) // 先取出无序区间的第一个元素,记为 k long k = array[i + 1]; // 从后往前,遍历有序区间{ // 找到合适的位置退出 // 所谓合适的位置,就是第一次 k >= array[j] 的位置 int j; for (j = i; j >= 0 && k < array[j]; j--) { array[j + 1] = array[j]; // 将不符合条件的数据往后般一格 } array[j + 1] = k; } }
希尔排序:
public static void inserSortWithGap(long[] array, int gap) { // 外围的循环次数是 n - gap 次 for (int i = 0; i < array.length - gap; i++) { // 一共有 gap 个分组 // 认为一开始 [0, i + gap) 有序 // [i + gap, n) 无序 long k = array[i + gap]; int j; // j 下标只去找同一组的元素比较,所以每次跳过 gap for (j = i; j >= 0 && k < array[j]; j = j - gap) { array[j + gap] = array[j]; } array[j + gap] = k; } } public static void shellSort(long[] arry) { int gap = arry.length / 2; while (gap > 1) { inserSortWithGap(arry, gap); gap = gap / 2; } inserSort(arry); }
选择排序:
public static void selectSort(long[] array) { // 每次选择出最大的数,放到最后去 // 一共要选择出 n - 1 个数 for (int i = 0; i < array.length - 1; i++) { // 通过遍历无序区间,只需要找到最大的数最在的位置就行(以下标的形式体现)不要做交换 // 无序 [0, n - i) int maxIndex = 0; // 假设最大的数放在一开始 for (int j = 1; j < array.length - i; j++) { if (array[j] > array[maxIndex]) { // 说明,找到了无序区间的新的最大的数 // 所以,记录最大数的下标 maxIndex = j; } } // 遍历完成之后,无序区间的最大的数就放在 maxIndex 所在下标处 // array[maxIndex] 是最大数 // 交换 [maxIndex] 和 无序区间的最后一个元素 [n - i - 1] swap(array, maxIndex, array.length - i - 1); } }
public static void swap(long[] array, int a, int b) { long e = array[a]; array[a] = array[b]; array[b] = e; }
堆排序:
public static void heapSort(long[]array,int index){ // 1. 建立大堆 createBigHeap(array); // 2. 遍历 n - 1 次 for (int i = 0; i < array.length - 1; i++) { // 2.1 交换之前的无序区间 [0, n - i) swap(array, 0, array.length - i - 1); // 交换之后的无序区间 [0, n - i - 1),元素个数 n - i - 1 个 // 2.1 对堆的 [0] 进行向下调整,堆里的元素个数就是无序区间的元素个数 shiftDown(array, array.length - i - 1, 0); } } private static void shiftDown(long[] array, int size, int index) { while (2 * index + 1 < size) { int maxIdx = 2 * index + 1; int right = maxIdx + 1; if (right < size && array[right] > array[maxIdx]) { maxIdx = right; } if (array[index] >= array[maxIdx]) { return; } swap(array, index, maxIdx); index = maxIdx; } } private static void createBigHeap(long[] array) { // 从最后一个元素的双亲开始 for (int i = (array.length - 2) / 2; i >= 0; i--) { shiftDown(array, array.length, i); } } }
归并排序:
public static void mergeSort(long[] array) { mergeSortRange(array, 0, array.length); } // 这里的 [from, to) 是左闭右开来表示区间的 private static void mergeSortRange(long[] array, int from, int to) { int size = to - from; // 情况1:如果区间内元素个数 <= 1 个,什么都不需要做 if (size <= 1) { return; } // 其他情况 // 1. 找到区间的中间位置的下标 int mid = from + (size / 2); // 2. 优先对左 [from, mid) 和右 [mid, to) 两个小区间先进行排序(使用同样的方式处理:分治思想) mergeSortRange(array, from, mid); mergeSortRange(array, mid, to); // 3. 有了两个分别各自有序的小区间 [from, mid) 和 [mid, to) // 通过一定的方式,将 [from, mid) [mid, to) 合并到 [from, to) 都是有序的 // 两个有序数组区间合并到一个有序数组区间(需要结果放回原地去) merge(array, from, mid, to); } private static void merge(long[] array, int from, int mid, int to) { // 先计算出来额外空间需要多个,计算两个区间加起来多少个元素 int size = to - from; // 申请一个额外的数组作为临时保存的地方 long[] other = new long[size]; int left = from; // 左边小区间的下标 int right = mid; // 右边小区间的下标 int dest = 0; // 临时空间的下标 // 只要左右两个小区间还有元素要参与比较 while (left < mid && right < to) { if (array[left] <= array[right]) { other[dest++] = array[left++]; } else { other[dest++] = array[right++]; } } // 其中一个区间的元素取完了,另一个区间里一定还有元素,再把剩余的元素,统统放入 other // 看起来左右两个都写了,但执行起来的时候一定只有一个会执行 while (left < mid) { other[dest++] = array[left++]; } while (right < to) { other[dest++] = array[right++]; } // 把 other 中的有序元素,复制回 array 中,要注意下标的问题 for (int i = 0; i < size; i++) { array[from + i] = other[i]; // array 下标的基准是从 [from] 开始,other 下标的基准是从 [0] 开始 // offset(偏移)是一致的,都是 i } } public static void main(String[] args) { long[] array = { 9, 5, 8, 7, 1, 6, 0, 4, 2, 3 }; mergeSort(array); System.out.println(Arrays.toString(array)); } }