排序算法(sorting algorithm) 之 归并排序(merge sort)
初版(探索):运行很慢,写法有问题
import java.util.Arrays; import cn.hutool.core.util.RandomUtil; public class MergeSortTest { public static void main(String[] args) { // int[] arr = new int[] {0,4,1,9,3,6,2,7,5,8}; int[] arr = RandomUtil.randomInts(101); Arena arena = new Arena(arr, false); arena.scanAndMerge(); int[] merged = arena.getMerged(); for (int i : merged) { System.out.println(i); } } public static class Arena { private int[] arr, merged; private boolean asc; private int l, r; public Arena(int[] arr, boolean asc) { this.arr = arr; this.merged = new int[0]; this.asc = asc; this.l = 0; this.r = 1; } // 需要申请额外两块数组,最后一次合并,重写原始数组即可 // 合并是只一个子序列和sorted合并,需要记录sorted当前指针,即head // 子序列是原始数组通过相对位置进行的记录,需要l,r // 扫描过程是r++ // 合并后l和r 移动到下一个子序列开始位置 public void scanAndMerge() { while (r < arr.length) { if (asc && arr[r - 1] <= arr[r] || !asc && arr[r - 1] >= arr[r]) { r++; if (r == arr.length) { int[] subSeq = Arrays.copyOfRange(arr, l, r); merged = merge(merged, subSeq, asc); } } else { int[] subSeq = Arrays.copyOfRange(arr, l, r); merged = merge(merged, subSeq, asc); l = r++; } } } public static int[] merge(int[] subSeq1, int[] subSeq2, boolean asc) { int len1 = subSeq1.length; int len2 = subSeq2.length; int head = 0; int[] merged = new int[len1 + len2]; int i1 = 0; int i2 = 0; while (true) { if (i1 == len1 || i2 == len2) { break; } int a = subSeq1[i1]; int b = subSeq2[i2]; if (asc) { if (a <= b) { merged[head++] = a; i1++; } else { merged[head++] = b; i2++; } } else { if (a >= b) { merged[head++] = a; i1++; } else { merged[head++] = b; i2++; } } } if (i1 == len1) { System.arraycopy(subSeq2, i2, merged, head, len2 - i2); } else { System.arraycopy(subSeq1, i1, merged, head, len1 - i1); } return merged; } public int[] getMerged() { return merged; } } }
探索(依旧很慢,单向扫描不可行,还得看二分)
public class MergeSortTestXXXXX { public static void main(String[] args) { int[] arr = new int[] { 1, 4, 2, 3, 6, 5 }; // int[] arr = new int[] { 1,2,3,4,5,6 }; // int[] arr = RandomUtil.randomInts(10011111); // int[] arr = ArrayUtil.range(10011111); mergeSort(arr, true); // int[] arr = new int[] {4,1,3}; // merge(arr, 0, 1, 2, false); // System.out.println(scan(arr,1, true)); for (int i : arr) { System.out.println(i); } } public static void mergeSort(int[] arr, boolean asc) { int r0 = -1; for (;;) { int r1 = scan(arr, r0 + 1, asc); if (r1 < arr.length) { merge(arr, 0, r0, r1, asc); r0 = r1; } else { break; } } } public static int scan(int[] arr, int start, boolean asc) { int len = arr.length; if (start >= arr.length - 1) { return start; } while (start < len - 1) { if (asc && arr[start] <= arr[start + 1] || !asc && arr[start] >= arr[start + 1]) { start++; } else { break; } } return start; } // 初始 []1,4,2,3,6,5 // 扫描[][1,4]2,3,6,5 // 合并[1,4]2,3,6,5 // 扫描[1,4][2,3,6]5 // 合并[1,2,3,4,6]5 // 扫描[1,2,3,4,6][5] // 合并[1,2,3,4,5,6] // 扫描[1,2,3,4,5,6][] // 结束 /** * 合并相邻的、分别有序、总体无序的数组 ,比如[1,4][2,3] -> [1,2,3,4] * * @param arr * @param l * @param f * @param r * @param asc */ public static void merge(int[] arr, int l, int f, int r, boolean asc) { if (l == r) { // System.out.println(String.format("[%s,%s][%s,%s]失败", l, f,f+1,r)); return; } int l0 = l; int l1 = f; int r0 = f + 1; int r1 = r; int t = 0; int[] tmp = new int[r1 - l0 + 1]; while (l0 <= l1 && r0 <= r1) { int lv = arr[l0]; int rv = arr[r0]; tmp[t++] = (asc && lv <= rv || !asc && lv >= rv) ? arr[l0++] : arr[r0++]; } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r0 <= r1) { tmp[t++] = arr[r0++]; } System.arraycopy(tmp, 0, arr, l, tmp.length); // System.out.println(String.format("[%s,%s][%s,%s]成功", l, f,f+1,r)); } }
这个2000使用了类,便于理解
import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.RandomUtil; public class MergeSortTest2000 { public static void main(String[] args) { int[] arr = RandomUtil.randomInts(10011111); StopWatch stopWatch = new StopWatch(); stopWatch.start(); mergeSort(new Arena(arr, 0, arr.length - 1, false)); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); } //警告: 递归要用二分场景,不然会有栈内存溢出 public static void mergeSort(Arena arena) { if (arena == null) { return; } mergeSort(arena.left()); mergeSort(arena.right()); arena.merge(); } public static class Arena { private int[] arr; private int l0, r0, l1, r1; private boolean asc; // 2, 1, 3, 6, 4, 5 public Arena(int[] arr, int l, int r, boolean asc) { this.arr = arr; this.asc = asc; this.l0 = l; this.r0 = r; this.l1 = (l + r) / 2; this.r1 = l1 + 1; // System.out.println(String.format("[%s,%s],[%s,%s]", l0,l1,r1,r0)); } public Arena left() { return l0 >= l1 ? null : new Arena(arr, l0, l1, asc); } public Arena right() { return r1 >= r0 ? null : new Arena(arr, r1, r0, asc); } public void merge() { if (l0 >= r0) { return; } int a = l0; int t = 0; int[] tmp = new int[r0 - l0 + 1]; while (l0 <= l1 && r1 <= r0) { int lv = arr[l0]; int rv = arr[r1]; tmp[t++] = (asc && lv <= rv || !asc && lv >= rv) ? arr[l0++] : arr[r1++]; } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r1 <= r0) { tmp[t++] = arr[r1++]; } System.arraycopy(tmp, 0, arr, a, tmp.length); } } }
不使用类,单纯函数
import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.RandomUtil; public class MergeSortTest1800 { public static void main(String[] args) { int[] arr = RandomUtil.randomInts(10011111); StopWatch stopWatch = new StopWatch(); stopWatch.start(); mergeSort(arr, 0, arr.length - 1, true); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); } //警告: 递归要用二分场景,不然会有栈内存溢出 public static void mergeSort(int[] arr, int l, int r, boolean asc) { int l0 = l; int r0 = r; int l1 = (l + r) / 2; int r1 = l1 + 1; if(l >= r) { return; } mergeSort(arr, l0, l1, asc); mergeSort(arr, r1, r0, asc); int a = l0; int t = 0; int[] tmp = new int[r0 - l0 + 1]; while (l0 <= l1 && r1 <= r0) { int lv = arr[l0]; int rv = arr[r1]; tmp[t++] = (asc && lv <= rv || !asc && lv >= rv) ? arr[l0++] : arr[r1++]; } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r1 <= r0) { tmp[t++] = arr[r1++]; } System.arraycopy(tmp, 0, arr, a, tmp.length); } }
优化了三目运算
import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.RandomUtil; public class MergeSortTest1700 { public static void main(String[] args) { int[] arr = RandomUtil.randomInts(10011111); StopWatch stopWatch = new StopWatch(); stopWatch.start(); mergeSort(arr, 0, arr.length - 1, true); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); } //警告: 递归要用二分场景,不然会有栈内存溢出 public static void mergeSort(int[] arr, int l, int r, boolean asc) { int l0 = l; int r0 = r; int l1 = (l + r) / 2; int r1 = l1 + 1; if(l >= r) { return; } mergeSort(arr, l0, l1, asc); mergeSort(arr, r1, r0, asc); int a = l0; int t = 0; int[] tmp = new int[r0 - l0 + 1]; while (l0 <= l1 && r1 <= r0) { int lv = arr[l0]; int rv = arr[r1]; if((asc && lv <= rv || !asc && lv >= rv)) { tmp[t++] = arr[l0++]; }else { tmp[t++] = arr[r1++]; } } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r1 <= r0) { tmp[t++] = arr[r1++]; } System.arraycopy(tmp, 0, arr, a, tmp.length); } }
优化了赋值
import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.RandomUtil; public class MergeSortTest1650 { public static void main(String[] args) { int[] arr = RandomUtil.randomInts(10011111); StopWatch stopWatch = new StopWatch(); stopWatch.start(); mergeSort(arr, 0, arr.length - 1, true); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); } //警告: 递归要用二分场景,不然会有栈内存溢出 public static void mergeSort(int[] arr, int l, int r, boolean asc) { int l0 = l; int r0 = r; int l1 = (l + r) / 2; int r1 = l1 + 1; if(l >= r) { return; } mergeSort(arr, l0, l1, asc); mergeSort(arr, r1, r0, asc); int a = l0; int t = 0; int[] tmp = new int[r0 - l0 + 1]; while (l0 <= l1 && r1 <= r0) { int lv = arr[l0]; int rv = arr[r1]; if((asc && lv <= rv || !asc && lv >= rv)) { tmp[t++] = lv; l0++; }else { tmp[t++] = rv; r1++; } } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r1 <= r0) { tmp[t++] = arr[r1++]; } System.arraycopy(tmp, 0, arr, a, tmp.length); } }
优化了临时数组
import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.RandomUtil; public class MergeSortTest1500 { public static void main(String[] args) { int[] arr = RandomUtil.randomInts(10011111); StopWatch stopWatch = new StopWatch(); stopWatch.start(); int[] tmp = new int[arr.length]; mergeSort(arr, 0, arr.length - 1, true, tmp); stopWatch.stop(); System.out.println(stopWatch.getTotalTimeMillis()); } //警告: 递归要用二分场景,不然会有栈内存溢出 public static void mergeSort(int[] arr, int l, int r, boolean asc, int[] tmp) { int l0 = l; int r0 = r; int l1 = (l + r) / 2; int r1 = l1 + 1; if(l >= r) { return; } mergeSort(arr, l0, l1, asc, tmp); mergeSort(arr, r1, r0, asc, tmp); int a = l0; int t = 0; int len = r0 - l0 + 1; while (l0 <= l1 && r1 <= r0) { int lv = arr[l0]; int rv = arr[r1]; if((asc && lv <= rv || !asc && lv >= rv)) { tmp[t++] = lv; l0++; }else { tmp[t++] = rv; r1++; } } while (l0 <= l1) { tmp[t++] = arr[l0++]; } while (r1 <= r0) { tmp[t++] = arr[r1++]; } System.arraycopy(tmp, 0, arr, a, len); } }