一、冒泡排序(Bubble Sort)
public static void bubbleSort(int[] arr) {
int n = arr.length;
// 外层循环控制排序轮数
for (int i = 0; i < n - 1; i++) {
// 内层循环控制每轮比较次数
for (int j = 0; j < n - i - 1; j++) {
// 如果前一个元素比后一个大,则交换
if (arr[j] > arr[j + 1]) {
// 交换arr[j]和arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//测试样例
int[] arr = {64, 34, 25, 12, 22, 11, 90};
bubbleSort(arr);
选择排序 (Selection Sort)
public static void selectionSort(int[] arr) {
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[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
三、插入排序 (Insertion Sort)
public static void insertionSort(int[] arr) {
int n = arr.length;
// 从第二个元素开始(索引1)
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;
}
}
四、快速排序 (Quick Sort)
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 找到分区点
int pi = partition(arr, low, high);
// 递归排序分区点左边的子数组
quickSort(arr, low, pi - 1);
// 递归排序分区点右边的子数组
quickSort(arr, pi + 1, high);
}
}
// 分区方法
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high]; // 选择最右边的元素作为基准
int i = low - 1; // 小于基准的元素的索引
for (int j = low; j < high; j++) {
// 如果当前元素小于基准
if (arr[j] < pivot) {
i++;
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 交换arr[i+1]和基准
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1; // 返回分区点索引
}
//测试样例
int[] arr = {10, 7, 8, 9, 1, 5};
int n = arr.length;
quickSort(arr, 0, n - 1);
五、归并排序 (Merge Sort)
// 主排序方法
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
// 找到中间点
int mid = left + (right - left) / 2;
// 递归排序左半部分
mergeSort(arr, left, mid);
// 递归排序右半部分
mergeSort(arr, mid + 1, right);
// 合并已排序的两部分
merge(arr, left, mid, right);
}
}
// 合并两个子数组的方法
private static void merge(int[] arr, int left, int mid, int right) {
// 计算两个子数组的大小
int n1 = mid - left + 1;
int n2 = right - mid;
// 创建临时数组
int[] L = new int[n1];
int[] R = new int[n2];
// 拷贝数据到临时数组
for (int i = 0; i < n1; i++) {
L[i] = arr[left + i];
}
for (int j = 0; j < n2; j++) {
R[j] = arr[mid + 1 + j];
}
// 合并临时数组
int i = 0, j = 0; // 初始索引
int k = left; // 初始合并索引
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// 拷贝L[]剩余元素
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
// 拷贝R[]剩余元素
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
//测试样例
int[] arr = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.length - 1);
六、堆排序 (Heap Sort)
public static void heapSort(int[] arr) {
int n = arr.length;
// 构建最大堆(重新排列数组)
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// 一个一个从堆顶取出元素
for (int i = n - 1; i > 0; i--) {
// 将当前根(最大值)移动到数组末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 在减小的堆上调用heapify
heapify(arr, i, 0);
}
}
// 堆化子树的方法
private static void heapify(int[] arr, int n, int i) {
int largest = i; // 初始化最大值为根
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
// 如果左子节点大于根
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// 如果右子节点大于当前最大值
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// 如果最大值不是根
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// 递归堆化受影响的子树
heapify(arr, n, largest);
}
}
//测试样例
int[] arr = {12, 11, 13, 5, 6, 7};
heapSort(arr);
七、Java内置排序方法
import java.util.Arrays;
public class JavaBuiltInSort {
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
// 使用Arrays.sort()方法
Arrays.sort(arr);
System.out.println("排序后数组:");
for (int num : arr) {
System.out.print(num + " ");
}
// 并行排序(Java 8+)
int[] largeArr = new int[1000000];
// ...填充数组...
Arrays.parallelSort(largeArr);
}
}
实际开发中,对于小型数组或几乎已排序的数组,插入排序可能表现更好;对于大型数组,通常使用快速排序、归并排序或堆排序;而Java内置的Arrays.sort()方法已经针对不同情况做了优化,是大多数情况下的最佳选择。
八、各排序算法比较
算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 |
选择排序 | O(n²) | O(n²) | O(1) | 不稳定 |
插入排序 | O(n²) | O(n²) | O(1) | 稳定 |
快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 |
归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 |
堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 |
Arrays.sort() | O(n log n) | O(n log n) | O(n) | 稳定 |
复杂度从小到大排序:
- O(1) - 常数复杂度
- 示例:数组随机访问、哈希表查找
- O(log n) - 对数复杂度
- 示例:二分查找、平衡二叉搜索树操作
- O(n) - 线性复杂度
- 示例:遍历数组、链表查找
- O(n log n) - 线性对数复杂度
- 示例:快速排序、归并排序、堆排序
- O(n²) - 平方复杂度
- 示例:冒泡排序、选择排序、插入排序
- O(n³) - 立方复杂度
- 示例:朴素矩阵乘法
- O(2ⁿ) - 指数复杂度
- 示例:汉诺塔问题、暴力破解算法
- O(n!) - 阶乘复杂度
- 示例:旅行商问题的暴力解法
1 < log n < √n < n < n log n < n² < n³ < 2ⁿ < n!