1.七大排序详解请见:《Java 七大排序(详解 + 代码 + 变种)》
编辑
2. 代码合集:
import java.util.ArrayDeque; import java.util.Deque; import java.util.concurrent.ThreadLocalRandom; /** * 七大排序 * * @author haomin * @date 2022/03/17 20:34 **/ public class SevenSort { // 取随机数 private static final ThreadLocalRandom random = ThreadLocalRandom.current(); //1.冒泡排序 public static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { boolean isSorted = false; for (int j = 0; j < arr.length - 1; j++) { if (arr[j] > arr[j + 1]) { swap(arr, j, j + 1); //当 isSorted 为 true 时说明当前存在交换 isSorted = true; } } // 当 isSorted 不为 true 时说明当前层不存在交换,数据已经有序,结束循环即可 // 减少时间复杂度 if (!isSorted) { break; } } } //2.1单向选择排序 public static void selectionSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int min = i; for (int j = i + 1; j < arr.length; j++) { //遍历当前层剩下的元素,找到最小数的索引 if (arr[min] > arr[j]) { min = j; } } //将最小数放在当前 i 的位置 swap(arr, min, i); } } //2.2双向选择排序 public static void selectionSortOP(int[] arr) { int low = 0; int high = arr.length - 1; // low = high,无序区间只剩下一个元素,数组已经有序 while (low <= high) { int min = low; int max = low; ////遍历当前层剩下的元素,分别找到最小数和最大数的索引 for (int i = low + 1; i <= high; i++) { if (arr[min] > arr[i]) { min = i; } if (arr[max] < arr[i]) { max = i; } } swap(arr, min, low); if (max == low) { // 最大值在上一步swap已经被换到min这个位置 max = min; } swap(arr, max, high); low += 1; high -= 1; } } // 3.1直接插入排序 // 每次从无序区间中拿第一个值插入到已经排序区间的合适位置,直到整个数组有序 public static void insertionSort(int[] arr) { // 已排序区间[0,i) // 待排序区间[i...n] for (int i = 1; i < arr.length; i++) { // 待排序区间的第一个元素arr[i] // 从待排序区间的第一个元素向前看,找到合适的插入位置 // for (int j = i; j > 0; j--) { // // arr[j - 1]已排序区间的最后一个元素 // if (arr[j] >= arr[j - 1]) { // // 相等我们也不交换,保证稳定性 // // 此时说明arr[j] > 已排序区间的最大值,arr[j]已经有序了~~直接下次循环 // break; // }else { // swap(arr,j,j - 1); // } // } //优化为一行代码即可 for (int j = i; j >= 1 && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } } // 3.2直接插入排序(已排序区间两端插入,插入时使用二分法) public static void insertionSortBS(int[] arr) { // 有序区间[0..i) // 无序区间[i...n] for (int i = 1; i < arr.length; i++) { int val = arr[i]; int left = 0; int right = i; //在有序区间,二分法找到 value 的位置 while (left < right) { int mid = left + ((right - left) >> 1); if (val < arr[mid]) { right = mid; } else { // 当val >= arr[mid] left = mid + 1; } } // 搬移left..i的元素 for (int j = i; j > left; j--) { arr[j] = arr[j - 1]; } // left就是val插入的位置 arr[left] = val; } } //4.希尔排序 public static void shellSort(int[] arr) { int gap = (arr.length - 1) >> 2; while (gap > 1) { // 按照gap分组进行插入排序 insertionSortByGap(arr, gap); gap = gap >> 1; } // 此时gap等于1,整个集合已经接近有序,只需要全集合来一次直接插入排序即可 insertionSort(arr); } private static void insertionSortByGap(int[] arr, int gap) { for (int i = gap; i < arr.length; i++) { for (int j = i; j - gap >= 0 && arr[j] < arr[j - gap]; j -= gap) { swap(arr, j, j - gap); } } } //5.堆排序 public static void heapSort(int[] arr) { // 1.先将arr进行heapify调整为最大堆 // 从最后一个非叶子节点开始进行siftDown操作 for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--) { siftDown(arr, i, arr.length); } // 此时arr为最大堆 for (int i = arr.length - 1; i > 0; i--) { // arr[0] 堆顶元素,就是当前堆的最大值 swap(arr, 0, i); siftDown(arr, 0, i); } } private static void siftDown(int[] arr, int i, int length) { while (2 * i + 1 < length) { int j = (i << 1) + 1; if (j + 1 < length && arr[j + 1] > arr[j]) { j = j + 1; } // j就是左右子树的最大值 if (arr[i] > arr[j]) { // 下沉结束 break; } else { swap(arr, i, j); i = j; } } } //6.归并排序 public static void mergeSort(int[] arr) { mergeSortInternal(arr, 0, arr.length - 1); } //在arr[l...r]进行归并排序,整个arr经过函数后就是一个已经有序的数组 private static void mergeSortInternal(int[] arr, int l, int r) { if (r - l <= 15) { // 2.小区间直接使用插入排序 insertionSort(arr, l, r); return; } int mid = l + ((r - l) >> 1); // 将原数组拆分为左右两个小区间,分别递归进行归并排序 // 走完这个函数之后 arr[l..mid]已经有序 mergeSortInternal(arr, l, mid); // 走完这个函数之后 arr[mid + 1..r]已经有序 mergeSortInternal(arr, mid + 1, r); // 1.只有左右两个子区间还有先后顺序不同时才merge if (arr[mid] > arr[mid + 1]) { merge(arr, l, mid, r); } } //在arr[l..r]使用插入排序 private static void insertionSort(int[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { for (int j = i; j > l && arr[j] < arr[j - 1]; j--) { swap(arr, j, j - 1); } } } //合并两个子数组arr[l..mid] 和 arr[mid + 1...r] private static void merge(int[] arr, int l, int mid, int r) { // 先创建一个新的临时数组aux int[] aux = new int[r - l + 1]; // 将arr元素值拷贝到aux上 for (int i = 0; i < aux.length; i++) { aux[i] = arr[i + l]; } // i就是左侧小数组的开始索引 int i = l; // j就是右侧小数组的开始索引 int j = mid + 1; // k表示当前正在合并的原数组的索引下标 for (int k = l; k <= r; k++) { if (i > mid) { // 左侧区间已经被处理完毕,只需要将右侧区间的值拷贝原数组即可 arr[k] = aux[j - l]; j++; } else if (j > r) { // 右侧区间已经被处理完毕,只需要将左侧区间的值拷贝到原数组即可 arr[k] = aux[i - l]; i++; } else if (aux[i - l] <= aux[j - l]) { // 此时左侧区间的元素值较小,相等元素放在左区间,保证稳定性 arr[k] = aux[i - l]; i++; } else { // 右侧区间的元素值较小 arr[k] = aux[j - l]; j++; } } } //6.2归并排序的迭代写法 public static void mergeSortNonRecursion(int[] arr) { // 最外层循环表示每次合并的子数组的元素个数 for (int sz = 1; sz <= arr.length; sz += sz) { // 内层循环的变量i表示每次合并的开始索引 // i + sz 就是右区间的开始索引,i + sz < arr.length说明还存在右区间 for (int i = 0; i + sz < arr.length; i += sz + sz) { merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, arr.length - 1)); } } } //7.0快速排序 public static void quickSort(int[] arr) { quickSortInternal(arr, 0, arr.length - 1); } private static void quickSortInternal(int[] arr, int l, int r) { if (r - l <= 15) { insertionSort(arr, l, r); return; } // 先获取分区点 // 所谓的分区点就是经过分区函数后,某个元素落在了最终的位置 // 分区点左侧全都是小于该元素的区间,分区点右侧全都是 >= 该元素的区间 int p = partition(arr, l, r); // 重复在左区间和右区间上重复上述流程 quickSortInternal(arr, l, p - 1); quickSortInternal(arr, p + 1, r); } private static int partition(int[] arr, int l, int r) { // 随机在当前数组中选一个数 int randomIndex = random.nextInt(l, r); swap(arr, l, randomIndex); int v = arr[l]; // arr[l + 1..j] < v // arr[j + 1..i) >= v // i表示当前正在扫描的元素 int j = l; for (int i = l + 1; i <= r; i++) { if (arr[i] < v) { swap(arr, j + 1, i); j++; } } // 将基准值和最后一个 < v的元素交换,基准值就落在了最终位置 swap(arr, l, j); return j; } //7.2快速排序 public static void quickSort2(int[] arr) { quickSortInternal2(arr, 0, arr.length - 1); } private static void quickSortInternal2(int[] arr, int l, int r) { if (r - l <= 15) { insertionSort(arr, l, r); return; } int p = partition2(arr, l, r); quickSortInternal2(arr, l, p - 1); quickSortInternal2(arr, p + 1, r); } private static int partition2(int[] arr, int l, int r) { int randomIndex = random.nextInt(l, r); swap(arr, l, randomIndex); int v = arr[l]; // arr[l + 1..i) <= v // [l + 1..l + 1) = 0 int i = l + 1; // arr(j..r] >= v // (r...r] = 0 int j = r; while (true) { // i从前向后扫描,碰到第一个 >= v的元素停止 while (i <= j && arr[i] < v) { i++; } // j从后向前扫描,碰到第一个 <= v的元素停止 while (i <= j && arr[j] > v) { j--; } if (i >= j) { break; } swap(arr, i, j); i++; j--; } // j落在最后一个 <= v的元素身上 swap(arr, l, j); return j; } //7.3 三路快排 public static void quickSort3(int[] arr) { quickSortInternal3(arr, 0, arr.length - 1); } private static void quickSortInternal3(int[] arr, int l, int r) { if (r - l <= 15) { insertionSort(arr, l, r); return; } int randomIndex = random.nextInt(l, r); swap(arr, l, randomIndex); int v = arr[l]; // arr[l + 1..lt] < v // lt是指向最后一个<v的元素 int lt = l; // arr[lt + 1..i) == v // i - 1是最后一个 = v的元素 int i = lt + 1; // arr[gt..r] > v // gt是第一个 > v的元素 int gt = r + 1; // i从前向后扫描和gt重合时,所有元素就处理完毕 while (i < gt) { if (arr[i] < v) { // arr[l + 1..lt] < v // arr[lt + 1..i) == v swap(arr, i, lt + 1); i++; lt++; } else if (arr[i] > v) { // 交换到gt - 1 swap(arr, i, gt - 1); gt--; // 此处i不++,交换来的gt - 1还没有处理 } else { // 此时arr[i] = v i++; } } // lt落在最后一个 < v的索引处 swap(arr, l, lt); // arr[l..lt - 1] < v quickSortInternal3(arr, l, lt - 1); // arr[gt..r] > v quickSortInternal3(arr, gt, r); } //7.4 借助栈来实现非递归分治快排 public static void quickSortNonRecursion(int[] arr) { Deque<Integer> stack = new ArrayDeque<>(); // 栈中保存当前集合的开始位置和终止位置 int l = 0; int r = arr.length - 1; stack.push(r); stack.push(l); while (!stack.isEmpty()) { // 栈不为空时,说明子区间还没有处理完毕 int left = stack.pop(); int right = stack.pop(); if (left >= right) { // 区间只有一个元素 continue; } int p = partition(arr, left, right); // 依次将右区间的开始和结束位置入栈 stack.push(right); stack.push(p + 1); // 再将左侧区间的开始和结束位置入栈 stack.push(p - 1); stack.push(left); } } //7.5 挖坑法 public static void quickSortDigPit(int[] arr) { quickSortDigPitInternal(arr, 0, arr.length - 1); } private static int quickSortDigPitInternal(int[] array, int left, int right) { int i = left; int j = right; int pivot = array[left]; while (i < j) { while (i < j && array[j] >= pivot) { j--; } array[i] = array[j]; while (i < j && array[i] <= pivot) { i++; } array[j] = array[i]; } array[i] = pivot; return i; } //交换三连 private static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }