- 插入排序
插入排序的核心思想是:将数组分为已排序和未排序两部分,依次将未排序部分的元素插入到已排序部分的合适位置。
public static void main(String[] args) {
int[] arr = {101, 34, 119, 1};
insertionSort(arr); // 调用插入排序
System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
}
// 插入排序核心方法
public static void insertionSort(int[] arr) {
// 从第2个元素开始(索引1),默认第一个元素已排序
for (int i = 1; i < arr.length; i++) {
int key = arr[i]; // 暂存当前待插入元素
int j = i - 1; // 指向已排序区间最后一个元素
// 向前遍历已排序区间,找到插入位置(元素大于key则后移)
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]; // 元素后移,腾出插入位置
j--;
}
// 若位置变化,将key插入对应位置
if (j + 1 != i) {
arr[j + 1] = key;
}
}
}
}
- 希尔排序
希尔排序是插入排序的改进版:先将数组按「步长」分组,对每组进行插入排序;逐步缩小步长,最终步长为 1 时,数组已基本有序,再做一次插入排序。
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
shellSort(arr);
System.out.println(java.util.Arrays.toString(arr));
}
public static void shellSort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int temp = arr[i];
int j = i - gap;
while (j >= 0 && temp < arr[j]) {
arr[j + gap] = arr[j];
j -= gap;
}
if (j + gap != i) arr[j + gap] = temp;
}
}
}
}
- 快速排序
快速排序是分治思想:选择一个「基准值」,将数组分为 “小于基准值” 和 “大于基准值” 两部分,再递归处理子数组。
public static void main(String[] args) {
int[] arr = {5, 2, 9, 3, 8, 4, 1, 7, 6};
quickSort(arr); // 调用快速排序
for (int num : arr) { // 输出排序结果
System.out.print(num + " ");
}
}
// 快速排序入口(封装)
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) return; // 空/长度<2无需排序
quickSort2(arr, 0, arr.length - 1); // 递归处理整个数组
}
// 递归分区排序
public static void quickSort2(int[] arr, int left, int right) {
if (left >= right) return; // 递归终止:区间只有1个元素
int pivotIndex = partition(arr, left, right); // 分区,返回基准值索引
quickSort2(arr, left, pivotIndex - 1); // 处理基准值左区间
quickSort2(arr, pivotIndex + 1, right); // 处理基准值右区间
}
// 分区核心:以右边界为基准值,划分<=基准值和>基准值区间
public static int partition(int[] arr, int left, int right) {
int pivot = arr[right]; // 基准值(右边界元素)
int index = left; // 记录<=基准值区间的右边界
// 遍历区间,将<=基准值的元素移到左侧
for (int i = left; i < right; i++) {
if (arr[i] <= pivot) {
swap(arr, index++, i); // 交换元素,扩大<=基准值区间
}
}
swap(arr, index, right); // 将基准值放到最终位置
return index; // 返回基准值索引
}
// 交换数组两个元素
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
- 选择排序
选择排序的核心:每一轮从未排序区间找到最小元素,与未排序区间的第一个元素交换,逐步扩大已排序区间。
public static void main(String[] args) {
int[] arr = {101, 34, 119, 1};
selectionSort(arr); // 调用选择排序
System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
}
// 选择排序核心方法
public static void selectionSort(int[] arr) {
// 外层循环:控制未排序区间起始位置
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i; // 初始化最小值索引为未排序区间第一个元素
// 内层循环:找未排序区间的最小值索引
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) minIndex = j;
}
// 最小值不在起始位置则交换
if (i != minIndex) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
}
- 归并排序
归并排序基于「分治思想」:先将数组递归拆分为最小单元,再将拆分后的有序子数组合并为一个有序数组。
public static void main(String[] args) {
int[] arr = {8, 4, 5, 7, 1, 4, 6, 2};
mergeSort(arr, 0, arr.length - 1); // 调用归并排序(左0,右最后一位)
System.out.println(java.util.Arrays.toString(arr)); // 输出排序结果
}
// 递归拆分数组
public static void mergeSort(int[] arr, int left, int right) {
if (left >= right) return; // 递归终止:区间只有1个元素
int mid = (left + right) / 2; // 计算中间索引,拆分左右区间
mergeSort(arr, left, mid); // 递归处理左区间
mergeSort(arr, mid + 1, right); // 递归处理右区间
merge(arr, left, mid, right); // 合并两个有序子区间
}
// 合并两个有序子数组
public static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1]; // 临时数组存合并结果
int i = left, j = mid + 1, k = 0; // i=左区间指针,j=右区间指针,k=临时数组指针
// 比较左右区间元素,将较小值放入临时数组
while (i <= mid && j <= right) {
temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
}
// 左区间剩余元素放入临时数组
while (i <= mid) temp[k++] = arr[i++];
// 右区间剩余元素放入临时数组
while (j <= right) temp[k++] = arr[j++];
// 临时数组结果复制回原数组
for (k = 0; k < temp.length; k++) {
arr[left + k] = temp[k];
}
}
}