Java常见排序算法实现
以下是Java中几种常见排序算法的实现,包括冒泡排序、选择排序、插入排序、快速排序和归并排序。
各排序算法特点说明:
-
冒泡排序:
- 原理:重复比较相邻元素,将大的元素逐步"冒泡"到数组末尾
- 特点:稳定排序,适合小规模数据,可优化(加入交换标记提前退出)
- 空间复杂度:O(1)(原地排序)
-
选择排序:
- 原理:每次从剩余元素中找到最小元素,放到已排序区间末尾
- 特点:不稳定(可能改变相等元素的相对顺序),交换次数少
- 空间复杂度:O(1)(原地排序)
-
插入排序:
- 原理:将元素逐个插入到前面已排序的区间中合适的位置
- 特点:稳定排序,对近乎有序的数据效率很高(接近O(n))
- 空间复杂度:O(1)(原地排序)
-
快速排序:
- 原理:通过分区操作将数组分为两部分,递归排序子数组
- 特点:实际应用中最快的排序算法之一,不稳定,最坏情况性能差
- 空间复杂度:O(log n)(递归栈空间)
-
归并排序:
- 原理:将数组分成两半分别排序,再合并两个有序子数组
- 特点:稳定排序,性能稳定(始终O(n log n)),需要额外空间
- 空间复杂度:O(n)(需要临时数组)
import java.util.Arrays;public class SortingAlgorithms {// 1. 冒泡排序(稳定,O(n²))public static void bubbleSort(int[] arr) {if (arr == null || arr.length <= 1) return;int n = arr.length;// 外层循环控制需要进行多少轮比较for (int i = 0; i < n - 1; i++) {boolean swapped = false;// 内层循环每轮将最大元素"冒泡"到末尾for (int j = 0; j < n - 1 - i; j++) {if (arr[j] > arr[j + 1]) {// 交换元素int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = true;}}// 如果本轮没有交换,说明数组已有序,提前退出if (!swapped) break;}}// 2. 选择排序(不稳定,O(n²))public static void selectionSort(int[] arr) {if (arr == null || arr.length <= 1) return;int n = arr.length;// 外层循环控制未排序区间的起点for (int i = 0; i < n - 1; i++) {// 找到未排序区间中最小元素的索引int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}// 将最小元素与未排序区间的第一个元素交换int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}// 3. 插入排序(稳定,O(n²),适合近乎有序的数据)public static void insertionSort(int[] arr) {if (arr == null || arr.length <= 1) return;int n = arr.length;// 从第二个元素开始,将其插入到前面的有序区间for (int i = 1; i < n; i++) {int key = arr[i]; // 当前要插入的元素int j = i - 1;// 移动有序区间中大于key的元素while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}// 将key插入到正确位置arr[j + 1] = key;}}// 4. 快速排序(不稳定,平均O(n log n),最坏O(n²))public static void quickSort(int[] arr) {if (arr == null || arr.length <= 1) return;quickSort(arr, 0, arr.length - 1);}private static void quickSort(int[] arr, int left, int right) {if (left < right) {// 分区操作,返回基准元素的正确位置int pivotIndex = partition(arr, left, right);// 递归排序基准元素左右两侧的子数组quickSort(arr, left, pivotIndex - 1);quickSort(arr, pivotIndex + 1, right);}}private static int partition(int[] arr, int left, int right) {int pivot = arr[right]; // 以最右侧元素作为基准int i = left - 1; // i指向小于基准的元素的最后位置// 遍历数组,将小于基准的元素放到左侧for (int j = left; j < right; j++) {if (arr[j] <= pivot) {i++;// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 将基准元素放到正确位置int temp = arr[i + 1];arr[i + 1] = arr[right];arr[right] = temp;return i + 1; // 返回基准元素的索引}// 5. 归并排序(稳定,O(n log n),需要额外空间)public static void mergeSort(int[] arr) {if (arr == null || arr.length <= 1) return;mergeSort(arr, 0, arr.length - 1, new int[arr.length]);}private static void mergeSort(int[] arr, int left, int right, int[] temp) {if (left < right) {int mid = left + (right - left) / 2; // 避免溢出// 递归排序左右两半mergeSort(arr, left, mid, temp);mergeSort(arr, mid + 1, right, temp);// 合并两个有序子数组merge(arr, left, mid, right, temp);}}private static void merge(int[] arr, int left, int mid, int right, int[] temp) {int i = left; // 左子数组的起始索引int j = mid + 1; // 右子数组的起始索引int k = left; // 临时数组的起始索引// 合并两个子数组到临时数组while (i <= mid && j <= right) {if (arr[i] <= arr[j]) {temp[k++] = arr[i++];} else {temp[k++] = arr[j++];}}// 复制左子数组剩余元素while (i <= mid) {temp[k++] = arr[i++];}// 复制右子数组剩余元素while (j <= right) {temp[k++] = arr[j++];}// 将临时数组中的元素复制回原数组for (k = left; k <= right; k++) {arr[k] = temp[k];}}// 测试public static void main(String[] args) {int[] arr = {64, 34, 25, 12, 22, 11, 90};int[] copy;copy = Arrays.copyOf(arr, arr.length);bubbleSort(copy);System.out.println("冒泡排序: " + Arrays.toString(copy));copy = Arrays.copyOf(arr, arr.length);selectionSort(copy);System.out.println("选择排序: " + Arrays.toString(copy));copy = Arrays.copyOf(arr, arr.length);insertionSort(copy);System.out.println("插入排序: " + Arrays.toString(copy));copy = Arrays.copyOf(arr, arr.length);quickSort(copy);System.out.println("快速排序: " + Arrays.toString(copy));copy = Arrays.copyOf(arr, arr.length);mergeSort(copy);System.out.println("归并排序: " + Arrays.toString(copy));}
}