java 的排序算法

一、排序算法分类

先明确两个核心概念,帮你理解不同算法的特性:

  • 稳定排序:排序后,相等元素的相对位置不变(如归并、冒泡、插入)。
  • 不稳定排序:相等元素的相对位置可能改变(如快速、选择)。
  • 时间复杂度:算法执行效率的核心指标(O (n²) 为简单算法,O (n log n) 为高效算法)。

一、冒泡排序(Bubble Sort)

1. 核心原理

冒泡排序是最基础的交换类排序,核心思想是:

  • 重复遍历待排序数组,两两比较相邻元素,如果顺序错误就交换它们;

  • 每一轮遍历都会将当前未排序部分的最大元素 "冒泡" 到末尾;

  • 优化点:如果某一轮遍历中没有发生任何交换,说明数组已经有序,可提前终止排序。

    public class BubbleSort {
    // 整数数组升序排序
    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; // 无交换,说明已有序
              }
          }
      }
    
      public static void main(String[] args) {
          int[] arr = {5, 2, 9, 1, 5, 6};
          bubbleSort(arr);
          System.out.println("冒泡排序结果:" + Arrays.toString(arr)); // [1, 2, 5, 5, 6, 9]
      }

    }

核心特点

  • 时间复杂度:
    • 最坏情况(完全逆序):O(n2)(需要 n-1 轮遍历,每轮 n-i 次比较);
    • 最好情况(已排序):O(n)(优化后只需 1 轮遍历,无交换直接终止);
    • 平均情况:O(n2)。
  • 空间复杂度:O(1)(原地排序,仅使用临时变量交换元素)。
  • 稳定性:稳定排序(相邻元素相等时不交换,相对位置不变)。
  • 适用场景:仅适合学习排序原理或极小规模 / 近乎有序的数组,实际开发中几乎不使用(效率太低)。

插入排序(简单、适合小规模 / 近乎有序数据)

原理:像整理扑克牌一样,将每个元素插入到前面已排序序列的正确位置。

复制代码
import java.util.Arrays;

public class InsertionSort {
    // 升序排序
    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;

        // 从第2个元素开始(第1个默认已排序)
        for (int i = 1; i < arr.length; i++) {
            int current = arr[i]; // 待插入的元素
            int j = i - 1;        // 已排序序列的末尾指针

            // 向前找插入位置:比current大的元素都后移
            while (j >= 0 && arr[j] > current) {
                arr[j + 1] = arr[j]; // 元素后移
                j--;
            }
            arr[j + 1] = current; // 插入到正确位置
        }
    }

    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6};
        insertionSort(arr);
        System.out.println("插入排序结果:" + Arrays.toString(arr)); // [1, 2, 5, 5, 6, 9]
    }
}

核心特点:

  • 时间复杂度:O (n²)(最坏)、O (n)(近乎有序时)。
  • 空间复杂度:O (1)(原地排序)。
  • 稳定排序,适合小数据量场景。

选择排序(简单、不稳定)

原理:每次从剩余未排序部分找到最小(大)元素,放到已排序部分的末尾。

复制代码
import java.util.Arrays;

public class SelectionSort {
    public static void selectionSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;

        // 外层循环:确定已排序部分的末尾位置
        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 (minIndex != i) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6};
        selectionSort(arr);
        System.out.println("选择排序结果:" + Arrays.toString(arr)); // [1, 2, 5, 5, 6, 9]
    }
}

核心特点:

  • 时间复杂度:O (n²)(无论数据是否有序,都要遍历找最小值)。
  • 空间复杂度:O (1)(原地排序)。
  • 不稳定排序(例如 [2, 2, 1] 排序时,第一个 2 会和 1 交换,破坏相对位置)。

归并排序(高效、稳定、分治思想)

原理:采用 "分治" 策略,先将数组拆分成最小单元,再两两合并成有序数组。

复制代码
import java.util.Arrays;

public class MergeSort {
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        // 临时数组:避免频繁创建数组,优化性能
        int[] temp = new int[arr.length];
        mergeSort(arr, 0, arr.length - 1, temp);
    }

    // 递归拆分
    private static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left >= right) return;
        int mid = left + (right - left) / 2; // 避免溢出,等价于 (left+right)/2

        // 拆分成左、右两部分
        mergeSort(arr, left, mid, temp);
        mergeSort(arr, mid + 1, right, temp);

        // 合并有序的左、右部分
        merge(arr, left, mid, right, temp);
    }

    // 合并逻辑:将 [left,mid] 和 [mid+1,right] 合并为有序数组
    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 = {5, 2, 9, 1, 5, 6};
        mergeSort(arr);
        System.out.println("归并排序结果:" + Arrays.toString(arr)); // [1, 2, 5, 5, 6, 9]
    }
}

核心特点:

  • 时间复杂度:O (n log n)(拆分是 log n 层,每层合并是 O (n))。
  • 空间复杂度:O (n)(需要临时数组存储合并结果)。
  • 稳定排序,适合大数据量、要求稳定排序的场景(如对象排序)。

快速排序(高效、原地、分治思想)

复制代码
import java.util.Arrays;

public class QuickSortOptimized {
    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) return;

        // 优化1:小规模数据改用插入排序(减少递归开销)
        if (right - left + 1 <= 10) {
            insertionSort(arr, left, right);
            return;
        }

        // 优化2:三数取中法选基准(避免最坏情况)
        int pivotIndex = medianOfThree(arr, left, right);
        swap(arr, left, pivotIndex); // 基准放到左边界

        // 分区
        int pivot = arr[left];
        int i = left, j = right;
        while (i < j) {
            while (i < j && arr[j] >= pivot) j--;
            while (i < j && arr[i] <= pivot) i++;
            if (i < j) swap(arr, i, j);
        }
        swap(arr, left, i); // 基准归位

        // 递归处理左右
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
    }

    // 三数取中法:选左、中、右三个数的中位数作为基准
    private static int medianOfThree(int[] arr, int left, int right) {
        int mid = left + (right - left) / 2;
        if (arr[left] > arr[mid]) swap(arr, left, mid);
        if (arr[left] > arr[right]) swap(arr, left, right);
        if (arr[mid] > arr[right]) swap(arr, mid, right);
        return mid; // 返回中位数索引
    }

    // 局部插入排序(仅排序 [left, right] 范围)
    private static void insertionSort(int[] arr, int left, int right) {
        for (int i = left + 1; i <= right; i++) {
            int current = arr[i];
            int j = i - 1;
            while (j >= left && arr[j] > current) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = current;
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6, 3, 7};
        quickSort(arr);
        System.out.println("优化版快速排序结果:" + Arrays.toString(arr)); // [1, 2, 3, 5, 5, 6, 7, 9]
    }
}

核心特点:

  • 时间复杂度:O (n log n)(平均)、O (n²)(最坏,优化后可避免)。
  • 空间复杂度:O (log n)(递归栈空间,原地排序)。
  • 不稳定排序,是实际开发中最常用的高效排序算法(JDK 的 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) 稳定 大数据量、要求稳定排序

总结

  • 简单算法(冒泡 / 插入 / 选择):适合学习排序原理,仅用于小规模数据,实际开发中极少直接使用。
  • 高效算法(快速 / 归并):是核心重点,快速排序性能最优(原地 + 低常数),归并排序胜在稳定。
  • JDK 内置排序:底层结合了快速排序、归并排序、插入排序的优点(基本类型用双轴快排,对象用归并排序),日常开发优先使用 Arrays.sort()/Collections.sort(),无需手动实现。
相关推荐
gechunlian882 小时前
SpringCloud系列教程:微服务的未来(十四)网关登录校验、自定义过滤器GlobalFilter、GatawayFilter
java·spring cloud·微服务
2401_853576502 小时前
并行算法在STL中的应用
开发语言·c++·算法
晓纪同学2 小时前
ROS2 -06-动作
java·数据库·python·算法·机器人·ros·ros2
无限进步_2 小时前
【C++】字符串中的字母反转算法详解
开发语言·c++·ide·git·算法·github·visual studio
qyzm2 小时前
Codeforces Round 927 (Div. 3)
数据结构·python·算法
重庆兔巴哥2 小时前
如何在Windows上配置Java环境变量?
java·开发语言·windows
2401_891482172 小时前
C++中的状态模式实战
开发语言·c++·算法
小江的记录本2 小时前
【PageHelper】 【Spring Boot + MyBatis + PageHelper】 完整项目示例+PageHelper核心原理深度解析
java·前端·spring boot·后端·sql·spring·mybatis
weixin_704266052 小时前
Spring AOP事务控制实战指南
java·后端·spring