Java中的Sort

码农老张 后端 最近

Java中的Sort

Java中的Sort

Arrays

legacyMergeSort归并排序ini

代码解读
复制代码
// 数组排序 public static <T> void sort(T[] a, Comparator<? super T> c) { // 如果没有自定义比大小条件则直接比较 if (c == null) { sort(a); } else { if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); else TimSort.sort(a, 0, a.length, c, null, 0, 0); } } // legacyMergeSort的排序实现 private static void mergeSort(Object[] src, // 源数组,从零开始 Object[] dest, // 源数组的克隆体,用于执行排序 int low, // 开始索引 int high, // 结束索引 int off ) { int length = high - low; // 计算索引范围 // 当length<7进行插入排序,INSERTIONSORT_THRESHOLD=7 // 插入排序,将每个元素放在对应的位置上 if (length < INSERTIONSORT_THRESHOLD) { // i指待排序元素 for (int i=low; i<high; i++) // 从i开始由后向前遍历,当dest[j-1]>dest[j],则进行交换 for (int j=i; j>low && ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--) // 数组交换元素 swap(dest, j, j-1); return; } // 递归对数组进行折半 int destLow = low; int destHigh = high; low += off; high += off; int mid = (low + high) >>> 1; // 中间值 mergeSort(dest, src, low, mid, -off); mergeSort(dest, src, mid, high, -off); // 如果list已经有序, 只需要拷贝src到dest数组。 // 目前左边数组有序,右边数组有序,则左边最大值小于等于右边最小值,则一定有序 if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) { // 由源数组src的low开始,拷贝到dest数组,destlow开始,长度为length System.arraycopy(src, low, dest, destLow, length); return; } // 已排好序的两数组,从小开始比较,小的先出 for(int i = destLow, p = low, q = mid; i < destHigh; i++) { // p从开始到中间(左),q从中间到最后(右),当src[p]<=src[q],则dest[i]=src[p],否则dest[i]=src[q] if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0) dest[i] = src[p++]; else dest[i] = src[q++]; } }

TimSort

TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的 run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。Timsort算法的过程包括

  1. 如何数组长度小于某个值,直接用二分插入排序算法
  2. 找到各个run,并入栈
  3. 按规则合并runjava
代码解读
复制代码
static <T> void sort(T[] a, // 被排序的数组 int lo, // 首元素索引 int hi, // 尾元素索引 Comparator<? super T> c, T[] work, int workBase, int workLen) { // 断言,参数检查 assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length; int nRemaining = hi - lo; // 递归结束条件,nRemaining排序数组长度,数组长度为0或者1的时候已经有序。 if (nRemaining < 2) return; // Arrays of size 0 and 1 are always sorted // 当数组很小时,使用mini-TimSort,而不归并,长度阀值时32 if (nRemaining < MIN_MERGE) { // 正数统计升序个数,负数统计降序个数 int initRunLen = countRunAndMakeAscending(a, lo, hi, c); // 二分插入排序 binarySort(a, lo, hi, lo + initRunLen, c); return; } /** * March over the array once, left to right, finding natural runs, * extending short natural runs to minRun elements, and merging runs * to maintain stack invariant. */ TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen); // 拆分大数组成不大于32的n等份,每份数量是minRun,然后逐份插入排序 int minRun = minRunLength(nRemaining); do { // 正数统计升序个数,负数统计降序个数 int runLen = countRunAndMakeAscending(a, lo, hi, c); // If run is short, extend to min(minRun, nRemaining) if (runLen < minRun) { // force为minRun与nRemaining的最小值 int force = nRemaining <= minRun ? nRemaining : minRun; binarySort(a, lo, lo + force, lo + runLen, c); runLen = force; } // Push run onto pending-run stack, and maybe merge // 把已经排好序的数列压入栈中,检查是不是需要合并 ts.pushRun(lo, runLen); // 执行合并排序结果操作 ts.mergeCollapse(); // 指向下一组数据开头 lo += runLen; // 待排序数量减少 nRemaining -= runLen; } while (nRemaining != 0); // Merge all remaining runs to complete sort assert lo == hi; ts.mergeForceCollapse(); assert ts.stackSize == 1; }

binarySort二分插入排序java

代码解读
复制代码
// 优化后的插入排序方法,start代表从start后的元素执行插入,start以前已有序 private static <T> void binarySort(T[] a, int lo, int hi, int start, Comparator<? super T> c) { assert lo <= start && start <= hi; if (start == lo) start++; for ( ; start < hi; start++) { // 最右边初始值 T pivot = a[start]; // Set left (and right) to the index where a[start] (pivot) belongs int left = lo; int right = start; assert left <= right; // 在已有序的部分,找寻pivot应当插入的地方 // 利用二分法定位应当插入的位置 while (left < right) { int mid = (left + right) >>> 1; // 当最右面的初始值比中值小 if (c.compare(pivot, a[mid]) < 0) right = mid; else left = mid + 1; } assert left == right; /* * The invariants still hold: pivot >= all in [lo, left) and * pivot < all in [left, start), so pivot belongs at left. Note * that if there are elements equal to pivot, left points to the * first slot after them -- that's why this sort is stable. * Slide elements over to make room for pivot. */ int n = start - left; // The number of elements to move // Switch is just an optimization for arraycopy in default case // 数组移位 // switch语句是一条小优化,1-2个元素的移动就不需要System.arraycopy了。 switch (n) { case 2: a[left + 2] = a[left + 1]; case 1: a[left + 1] = a[left]; break; default: System.arraycopy(a, left, a, left + 1, n); } // 插入对应位置 a[left] = pivot; } }

参考文章:Timsort详解


DualPivotQuicksort

用于数值(numerical)的排序

Listscss

代码解读
复制代码
// list接口默认排序,原理为转化为数组的排序,排序完成写回数组 default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } }

转载来源:https://juejin.cn/post/7092780685793951758

Apipost 私有化火热进行中

评论