深入解析十大经典排序算法原理与实现

排序算法示例说明文档

概述

本文档详细说明了排序算法示例的实现原理、性能特点和使用方法。

功能概要:提供各种排序算法的完整实现,包括基础排序算法和高级排序算法,帮助理解算法原理和性能特点

排序算法分类

1. 基础排序算法 (Basic Sorting Algorithms)

1.1 冒泡排序 (Bubble Sort)
  • 算法思想:比较相邻的两个元素,如果第一个比第二个大,则交换它们
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 适用场景:小规模数据排序,教学演示

算法步骤:

  1. 比较相邻的两个元素,如果第一个比第二个大,则交换它们
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对
  3. 重复以上步骤,直到没有任何一对数字需要比较

优化点:

  • 添加swapped标志,如果一轮比较中没有发生交换,说明数组已经有序,可以提前退出
java 复制代码
/**
 * 冒泡排序算法
 * 
 * 算法思想:通过相邻元素比较和交换,将最大的元素逐步"冒泡"到数组末尾
 * 具体逻辑:
 * 1. 外层循环控制排序轮数,每轮将当前范围内最大的元素放到末尾
 * 2. 内层循环比较相邻元素,如果前一个大于后一个则交换
 * 3. 使用swapped标志优化:如果一轮中没有发生交换,说明数组已有序,可以提前退出
 * 
 * 时间复杂度:O(n²) - 最坏情况下需要n-1轮,每轮比较n-i-1次
 * 空间复杂度:O(1) - 只使用常数个额外变量
 * 稳定性:稳定 - 相等元素不会改变相对位置
 * 
 * @param arr 待排序的整数数组
 */
public static void bubbleSort(int[] arr) {
    // 边界条件检查:空数组或长度为1的数组无需排序
    if (arr == null || arr.length <= 1) {
        return;
    }
    
    int n = arr.length;
    // 优化标志:如果一轮中没有发生交换,说明数组已经有序
    boolean swapped;
    
    // 外层循环:控制排序轮数,每轮将当前范围内最大的元素放到末尾
    for (int i = 0; i < n - 1; i++) {
        // 初始化交换标志为false
        swapped = false;
        // 内层循环:比较相邻元素,范围是[0, n-1-i)
        // 每轮结束后,最后i个元素已经有序,无需再比较
        for (int j = 0; j < n - 1 - i; j++) {
            // 如果前一个元素大于后一个元素,则交换它们
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
                // 标记发生了交换
                swapped = true;
            }
        }
        // 优化:如果一轮中没有发生交换,说明数组已经有序,可以提前退出
        if (!swapped) {
            break;
        }
    }
}
1.2 选择排序 (Selection Sort)
  • 算法思想:在未排序序列中找到最小元素,存放到排序序列的起始位置
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 适用场景:小规模数据排序,交换次数少

算法步骤:

  1. 在未排序序列中找到最小元素,存放到排序序列的起始位置
  2. 再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾
  3. 重复第二步,直到所有元素均排序完毕
java 复制代码
  /**
   * 选择排序算法
   * 
   * 算法思想:每次从未排序区间选择最小的元素,放到已排序区间的末尾
   * 具体逻辑:
   * 1. 外层循环控制已排序区间的边界,初始时已排序区间为空
   * 2. 内层循环在未排序区间中寻找最小元素的下标
   * 3. 将找到的最小元素与未排序区间的第一个元素交换,扩大已排序区间
   * 4. 重复上述过程,直到所有元素都进入已排序区间
   * 
   * 时间复杂度:O(n²) - 需要n-1轮选择,每轮在n-i个元素中找最小值
   * 空间复杂度:O(1) - 只使用常数个额外变量
   * 稳定性:不稳定 - 相等元素可能改变相对位置(如[3,3,1]排序后变成[1,3,3])
   * 
   * @param arr 待排序的整数数组
   */
  public static void selectionSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }

      int n = arr.length;
      // 外层循环:控制已排序区间的边界,初始时已排序区间为空
      for (int i = 0; i < n - 1; i++) {
          // 假设当前位置i的元素是最小的
          int minIndex = i;
          // 内层循环:在未排序区间[i+1, n)中寻找真正的最小元素
          for (int j = i + 1; j < n; j++) {
              // 如果发现更小的元素,更新最小元素的下标
              if (arr[j] < arr[minIndex]) {
                  minIndex = j;
              }
          }
          // 如果找到的最小元素不在位置i,则交换它们
          // 这样就将最小元素放到了已排序区间的末尾
          if (minIndex != i) {
              swap(arr, i, minIndex);
          }
      }
  }
1.3 插入排序 (Insertion Sort)
  • 算法思想:将第一个元素看做已排序序列,取出下一个元素,在已排序序列中从后向前扫描
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 适用场景:小规模数据排序,基本有序的数据

算法步骤:

  1. 将第一个元素看做已排序序列
  2. 取出下一个元素,在已排序序列中从后向前扫描
  3. 如果该元素大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5
java 复制代码
  /**
   * 插入排序算法
   * 
   * 算法思想:将数组分为已排序和未排序两部分,逐个将未排序元素插入到已排序部分的正确位置
   * 具体逻辑:
   * 1. 初始时,第一个元素视为已排序,其余元素为未排序
   * 2. 从未排序区间取第一个元素,在已排序区间中寻找合适的插入位置
   * 3. 插入过程中,将大于该元素的已排序元素向后移动,为插入腾出空间
   * 4. 将元素插入到正确位置,扩大已排序区间
   * 5. 重复上述过程,直到所有元素都进入已排序区间
   * 
   * 时间复杂度:O(n²) - 最坏情况下需要移动大量元素
   * 空间复杂度:O(1) - 只使用常数个额外变量
   * 稳定性:稳定 - 相等元素不会改变相对位置
   * 特点:对于接近有序的数组,插入排序效率很高,接近O(n)
   * 
   * @param arr 待排序的整数数组
   */
  public static void insertionSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }

      int n = arr.length;
      // 从第二个元素开始,逐个将未排序元素插入到已排序部分
      for (int i = 1; i < n; i++) {
          // 保存当前要插入的元素
          int key = arr[i];
          // j指向已排序区间的最后一个元素
          int j = i - 1;

          // 在已排序区间中寻找key的插入位置
          // 将大于key的元素向后移动,为key腾出插入空间
          while (j >= 0 && arr[j] > key) {
              // 将大于key的元素向后移动一位
              arr[j + 1] = arr[j];
              // 继续向前查找
              j--;
          }

          // 找到插入位置,将key插入到arr[j+1]位置
          // 此时arr[j] <= key,所以key应该放在arr[j+1]
          arr[j + 1] = key;
      }
  }
1.4 希尔排序 (Shell Sort)
  • 算法思想:插入排序的改进版,通过设置不同的增量序列来提高效率
  • 时间复杂度:O(n^1.3) 到 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 适用场景:中等规模数据排序

算法步骤:

  1. 选择一个初始的增量序列(通常为n/2, n/4, n/8...)
  2. 按照增量序列对数组进行分组
  3. 对每组使用插入排序
  4. 逐步减小增量,重复步骤2-3
  5. 当增量为1时,完成排序
java 复制代码
  /**
   * 希尔排序算法(改进的插入排序)
   * 
   * 算法思想:通过设置不同的间隔序列,对数组进行分组插入排序,逐步减小间隔直到为1
   * 具体逻辑:
   * 1. 选择一个初始间隔gap(通常为n/2),将数组分成gap个子序列
   * 2. 对每个子序列分别进行插入排序,但使用gap作为步长
   * 3. 减小间隔(通常为gap/2),重复上述过程
   * 4. 当间隔为1时,进行最后一次插入排序,此时数组已经基本有序
   * 5. 由于数组基本有序,最后的插入排序效率很高
   * 
   * 时间复杂度:O(n^1.3) - 比插入排序快,但具体复杂度取决于间隔序列的选择
   * 空间复杂度:O(1) - 只使用常数个额外变量
   * 稳定性:不稳定 - 相等元素可能改变相对位置
   * 特点:希尔排序是插入排序的改进版本,对于中等大小的数组效率较高
   * 
   * @param arr 待排序的整数数组
   */
  public static void shellSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }

      int n = arr.length;
      // 外层循环:控制间隔序列,从n/2开始,逐步减小到1
      for (int gap = n / 2; gap > 0; gap /= 2) {
          // 对每个子序列进行插入排序
          // 从第gap个元素开始,因为前gap个元素分别作为各个子序列的第一个元素
          for (int i = gap; i < n; i++) {
              // 保存当前要插入的元素
              int key = arr[i];
              // j指向当前元素在子序列中的前一个位置
              int j = i;

              // 在子序列中寻找key的插入位置
              // 将大于key的元素向后移动gap个位置
              while (j >= gap && arr[j - gap] > key) {
                  // 将大于key的元素向后移动gap个位置
                  arr[j] = arr[j - gap];
                  // 在子序列中向前查找
                  j -= gap;
              }

              // 找到插入位置,将key插入到arr[j]位置
              arr[j] = key;
          }
      }
  }

2. 高级排序算法 (Advanced Sorting Algorithms)

2.1 快速排序 (Quick Sort)
  • 算法思想:选择一个基准元素,将小于基准的元素放在左边,大于基准的元素放在右边
  • 时间复杂度:平均O(n log n),最坏O(n²)
  • 空间复杂度:O(log n)
  • 稳定性:不稳定
  • 适用场景:大规模数据排序,实际应用中最常用的排序算法

算法步骤:

  1. 选择一个基准元素(pivot)
  2. 将小于基准的元素放在左边,大于基准的元素放在右边
  3. 对左右两个子序列递归执行快速排序
  4. 当子序列长度为1或0时,排序完成

优化点:

  • 选择基准元素的方法(三数取中、随机选择等)
  • 处理重复元素的优化
  • 小数组使用插入排序
java 复制代码
  /**
   * 快速排序算法
   * 
   * 算法思想:使用分治策略,选择一个基准元素(pivot),将数组分为两部分,
   * 左边部分都小于等于基准元素,右边部分都大于基准元素,然后递归排序两部分
   * 
   * 具体逻辑:
   * 1. 选择基准元素(这里选择最后一个元素作为pivot)
   * 2. 通过partition方法将数组分为两部分
   * 3. 递归对左半部分和右半部分进行快速排序
   * 4. 当子数组长度小于等于1时,递归结束
   * 
   * 时间复杂度:平均O(n log n),最坏O(n²)(当数组已经有序时)
   * 空间复杂度:O(log n) - 递归调用栈的深度
   * 稳定性:不稳定 - 相等元素可能改变相对位置
   * 特点:实际应用中效率很高,是很多编程语言内置排序算法的选择
   * 
   * @param arr 待排序的整数数组
   */
  public static void quickSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }
      // 调用递归版本的快速排序,初始范围为整个数组
      quickSort(arr, 0, arr.length - 1);
  }

  /**
   * 快速排序的递归实现
   * 
   * @param arr 待排序的数组
   * @param low 当前排序范围的起始索引
   * @param high 当前排序范围的结束索引
   */
  private static void quickSort(int[] arr, int low, int high) {
      // 递归终止条件:当子数组长度小于等于1时
      if (low < high) {
          // 选择基准元素并分区,返回基准元素的最终位置
          int pivotIndex = partition(arr, low, high);
          // 递归排序左半部分(小于基准元素的部分)
          quickSort(arr, low, pivotIndex - 1);
          // 递归排序右半部分(大于基准元素的部分)
          quickSort(arr, pivotIndex + 1, high);
      }
  }

  /**
   * 分区方法:将数组分为两部分,左边都小于等于pivot,右边都大于pivot
   * 
   * 具体逻辑:
   * 1. 选择最后一个元素作为基准元素(pivot)
   * 2. 使用双指针技术:i指向小于等于pivot的区域的最后一个位置
   * 3. 遍历数组,将小于等于pivot的元素放到左边区域
   * 4. 最后将pivot放到正确位置,返回pivot的索引
   * 
   * @param arr 待分区的数组
   * @param low 分区范围的起始索引
   * @param high 分区范围的结束索引(pivot的位置)
   * @return 基准元素的最终位置
   */
  private static int partition(int[] arr, int low, int high) {
      // 选择最后一个元素作为基准元素
      int pivot = arr[high];
      // i指向小于等于pivot的区域的最后一个位置,初始为low-1
      int i = low - 1;

      // 遍历数组,将小于等于pivot的元素放到左边区域
      for (int j = low; j < high; j++) {
          // 如果当前元素小于等于pivot
          if (arr[j] <= pivot) {
              // 扩展左边区域
              i++;
              // 将当前元素交换到左边区域
              swap(arr, i, j);
          }
      }

      // 将pivot放到正确位置(左边区域的后面)
      swap(arr, i + 1, high);
      // 返回pivot的最终位置
      return i + 1;
  }
2.2 归并排序 (Merge Sort)
  • 算法思想:将数组分成两个相等的子数组,递归地对两个子数组进行归并排序,然后合并
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(n)
  • 稳定性:稳定
  • 适用场景:大规模数据排序,需要稳定排序的场景

算法步骤:

  1. 将数组分成两个相等的子数组
  2. 递归地对两个子数组进行归并排序
  3. 将两个已排序的子数组合并成一个有序数组
  4. 当子数组长度为1时,开始合并

特点:

  • 时间复杂度稳定,不受数据分布影响
  • 需要额外的空间复杂度
  • 适合外部排序
java 复制代码
  /**
   * 归并排序算法
   * 
   * 算法思想:使用分治策略,将数组递归地分成两半,分别排序后再合并
   * 具体逻辑:
   * 1. 将数组分成两个大致相等的子数组
   * 2. 递归地对两个子数组进行归并排序
   * 3. 将两个已排序的子数组合并成一个有序数组
   * 4. 当子数组长度为1时,递归结束
   * 
   * 时间复杂度:O(n log n) - 稳定,不受数据分布影响
   * 空间复杂度:O(n) - 需要额外的临时数组来存储合并结果
   * 稳定性:稳定 - 相等元素不会改变相对位置
   * 特点:时间复杂度稳定,但需要额外空间,适合外部排序
   * 
   * @param arr 待排序的整数数组
   */
  public static void mergeSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }

      int n = arr.length;
      // 创建临时数组,用于存储合并过程中的结果
      int[] temp = new int[n];
      // 调用递归版本的归并排序,初始范围为整个数组
      mergeSort(arr, 0, n - 1, temp);
  }

  /**
   * 归并排序的递归实现
   * 
   * @param arr 待排序的数组
   * @param left 当前排序范围的左边界
   * @param right 当前排序范围的右边界
   * @param temp 临时数组,用于存储合并结果
   */
  private static void mergeSort(int[] arr, int left, int right, int[] temp) {
      // 递归终止条件:当子数组长度小于等于1时
      if (left < right) {
          // 计算中间位置,将数组分成两半
          int mid = (left + right) / 2;
          // 递归排序左半部分
          mergeSort(arr, left, mid, temp);
          // 递归排序右半部分
          mergeSort(arr, mid + 1, right, temp);
          // 合并两个已排序的子数组
          merge(arr, left, mid, right, temp);
      }
  }

  /**
   * 合并两个已排序的子数组
   * 
   * 具体逻辑:
   * 1. 使用三个指针:i指向左半部分,j指向右半部分,k指向临时数组
   * 2. 比较两个子数组的元素,将较小的放入临时数组
   * 3. 当一个子数组遍历完后,将另一个子数组的剩余元素全部放入临时数组
   * 4. 最后将临时数组的结果复制回原数组
   * 
   * @param arr 原数组
   * @param left 左半部分的起始位置
   * @param mid 左半部分的结束位置
   * @param right 右半部分的结束位置
   * @param 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 (int m = left; m <= right; m++) {
          arr[m] = temp[m];
      }
  }
2.3 堆排序 (Heap Sort)
  • 算法思想:利用堆这种数据结构所设计的排序算法
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 适用场景:大规模数据排序,原地排序

算法步骤:

  1. 构建最大堆(或最小堆)
  2. 将堆顶元素(最大值)与末尾元素交换
  3. 调整堆结构,使其重新成为最大堆
  4. 重复步骤2-3,直到堆中只有一个元素

特点:

  • 原地排序,不需要额外空间
  • 时间复杂度稳定
  • 适合找出前k个最大/最小元素
java 复制代码
  /**
   * 堆排序算法
   * 
   * 算法思想:利用堆这种数据结构进行排序,先将数组构建成最大堆,
   * 然后逐个取出堆顶元素(最大值),放到数组末尾,重复此过程直到堆为空
   * 
   * 具体逻辑:
   * 1. 构建最大堆:从最后一个非叶子节点开始,自底向上调整每个节点
   * 2. 堆排序:重复执行以下操作直到堆为空
   *    a. 将堆顶元素(最大值)与堆的最后一个元素交换
   *    b. 堆的大小减1
   *    c. 对新的堆顶元素进行下沉操作,重新构建最大堆
   * 3. 最终得到升序排列的数组
   * 
   * 时间复杂度:O(n log n) - 构建堆O(n),每次调整O(log n),共n-1次
   * 空间复杂度:O(1) - 原地排序,只使用常数个额外变量
   * 稳定性:不稳定 - 相等元素可能改变相对位置
   * 特点:原地排序,空间效率高,适合大数据量排序
   * 
   * @param arr 待排序的整数数组
   */
  public static void heapSort(int[] arr) {
      // 边界条件检查:空数组或长度为1的数组无需排序
      if (arr == null || arr.length <= 1) {
          return;
      }

      int n = arr.length;

      // 第一步:构建最大堆
      // 从最后一个非叶子节点开始,自底向上调整每个节点
      // 最后一个非叶子节点的索引是 n/2 - 1
      for (int i = n / 2 - 1; i >= 0; i--) {
          heapify(arr, n, i);
      }

      // 第二步:逐个从堆中取出元素
      // 从数组末尾开始,每次将堆顶元素(最大值)放到当前位置
      for (int i = n - 1; i > 0; i--) {
          // 将堆顶元素(最大值)与堆的最后一个元素交换
          swap(arr, 0, i);
          // 对新的堆顶元素进行下沉操作,重新构建最大堆
          // 注意:此时堆的大小已经减1,所以传入i而不是n
          heapify(arr, i, 0);
      }
  }

  /**
   * 堆化操作:将以节点i为根的子树调整为最大堆
   * 
   * 具体逻辑:
   * 1. 假设节点i是最大的,比较节点i与其左右子节点
   * 2. 如果左子节点或右子节点更大,则更新largest指针
   * 3. 如果largest不等于i,说明需要交换,然后递归调整被交换的子树
   * 4. 这个过程称为"下沉"操作,确保父节点总是大于等于其子节点
   * 
   * @param arr 数组(表示堆)
   * @param n 堆的当前大小
   * @param i 要调整的节点索引
   */
  private static void heapify(int[] arr, int n, int i) {
      // 假设当前节点i是最大的
      int largest = i;
      // 计算左子节点的索引
      int left = 2 * i + 1;
      // 计算右子节点的索引
      int right = 2 * i + 2;

      // 如果左子节点存在且大于当前节点,更新largest指针
      if (left < n && arr[left] > arr[largest]) {
          largest = left;
      }

      // 如果右子节点存在且大于当前最大值,更新largest指针
      if (right < n && arr[right] > arr[largest]) {
          largest = right;
      }

      // 如果largest不等于i,说明需要交换
      if (largest != i) {
          // 交换当前节点与最大子节点
          swap(arr, i, largest);
          // 递归调整被交换的子树,确保整个子树都满足最大堆性质
          heapify(arr, n, largest);
      }
  }

性能对比

时间复杂度对比

算法 最好情况 平均情况 最坏情况
冒泡排序 O(n) O(n²) O(n²)
选择排序 O(n²) O(n²) O(n²)
插入排序 O(n) O(n²) O(n²)
希尔排序 O(n log n) O(n^1.3) O(n²)
快速排序 O(n log n) O(n log n) O(n²)
归并排序 O(n log n) O(n log n) O(n log n)
堆排序 O(n log n) O(n log n) O(n log n)

空间复杂度对比

算法 空间复杂度 是否原地排序
冒泡排序 O(1) 是
选择排序 O(1) 是
插入排序 O(1) 是
希尔排序 O(1) 是
快速排序 O(log n) 是
归并排序 O(n) 否
堆排序 O(1) 是

稳定性对比

算法 稳定性
冒泡排序 稳定
选择排序 不稳定
插入排序 稳定
希尔排序 不稳定
快速排序 不稳定
归并排序 稳定
堆排序 不稳定

使用方式

1. 直接调用静态方法

java 复制代码
int[] arr = {64, 34, 25, 12, 22, 11, 90};

// 使用基础排序算法
BasicSortingAlgorithms.bubbleSort(arr);
BasicSortingAlgorithms.selectionSort(arr);
BasicSortingAlgorithms.insertionSort(arr);
BasicSortingAlgorithms.shellSort(arr);

// 使用高级排序算法
AdvancedSortingAlgorithms.quickSort(arr);
AdvancedSortingAlgorithms.mergeSort(arr);
AdvancedSortingAlgorithms.heapSort(arr);

2. 通过策略模式使用

java 复制代码
SortContext context = new SortContext();

// 设置不同的排序策略
context.setStrategy(new QuickSortStrategy());
int[] result1 = context.executeSort(testArray);

context.setStrategy(new MergeSortStrategy());
int[] result2 = context.executeSort(testArray);

3. 运行示例程序

bash 复制代码
# 运行基础排序算法演示
java org.example.interview.algorithm.sorting.BasicSortingAlgorithms

# 运行高级排序算法演示
java org.example.interview.algorithm.sorting.AdvancedSortingAlgorithms

# 运行策略模式演示
java org.example.interview.algorithm.sorting.StrategyPattern

算法选择建议

小规模数据 (n < 50)

  • 推荐:插入排序
  • 原因:实现简单,对于小数据效率较高

中等规模数据 (50 ≤ n < 1000)

  • 推荐:希尔排序
  • 原因:比插入排序效率高,实现相对简单

大规模数据 (n ≥ 1000)

  • 推荐:快速排序
  • 原因:平均情况下效率最高,实际应用中最常用

特殊需求

  • 需要稳定排序:归并排序
  • 原地排序:堆排序
  • 外部排序:归并排序

扩展建议

  1. 添加更多排序算法 :
    • 计数排序、基数排序、桶排序等线性时间排序算法
    • 外部排序算法
    • 并行排序算法
  2. 性能优化 :
    • 混合排序算法(如Timsort)
    • 针对特定数据分布的优化
    • 多线程并行排序
  3. 可视化展示 :
    • 排序过程的可视化
    • 性能对比图表
    • 内存使用情况分析

注意事项

  1. 数据规模:选择合适的算法需要考虑数据规模
  2. 数据特征:考虑数据的分布特征(是否基本有序、重复元素等)
  3. 稳定性要求:某些应用场景要求排序算法是稳定的
  4. 空间限制:在内存受限的环境下,优先选择原地排序算法

相关资源


公共代码

工具方法
java 复制代码
    /**
     * 交换数组中两个元素的位置
     * 
     * @param arr 目标数组
     * @param i 第一个元素的索引
     * @param j 第二个元素的索引
     */
    private static void swap(int[] arr, int i, int j) {
        // 使用临时变量保存第一个元素
        int temp = arr[i];
        // 将第二个元素放到第一个位置
        arr[i] = arr[j];
        // 将第一个元素放到第二个位置
        arr[j] = temp;
    }

    /**
     * 打印数组内容,用于调试和演示
     * 
     * @param arr 要打印的数组
     * @param message 打印前的说明信息
     */
    public static void printArray(int[] arr, String message) {
        // 输出消息和数组开始符号
        System.out.print(message + ": [");
        // 遍历数组的每个元素
        for (int i = 0; i < arr.length; i++) {
            // 输出当前元素
            System.out.print(arr[i]);
            // 如果不是最后一个元素,添加分隔符
            if (i < arr.length - 1) {
                System.out.print(", ");
            }
        }
        // 输出数组结束符号并换行
        System.out.println("]");
    }

    /**
     * 检查数组是否已经排序(升序)
     * 
     * @param arr 要检查的数组
     * @return true表示数组已排序,false表示数组未排序
     */
    public static boolean isSorted(int[] arr) {
        // 空数组或长度为1的数组认为是有序的
        if (arr == null || arr.length <= 1) {
            return true;
        }
        
        // 检查每个元素是否大于等于前一个元素
        for (int i = 1; i < arr.length; i++) {
            // 如果发现逆序,返回false
            if (arr[i] < arr[i - 1]) {
                return false;
            }
        }
        // 所有元素都满足升序条件
        return true;
    }

    /**
     * 复制数组,创建数组的深拷贝
     * 
     * @param arr 要复制的原数组
     * @return 新的数组副本,如果原数组为null则返回null
     */
    public static int[] copyArray(int[] arr) {
        // 如果原数组为null,直接返回null
        if (arr == null) {
            return null;
        }
        // 创建新数组,长度与原数组相同
        int[] copy = new int[arr.length];
        // 使用系统方法复制数组内容,效率最高
        System.arraycopy(arr, 0, copy, 0, arr.length);
        return copy;
    }

    /**
     * 生成指定大小的随机数组,用于测试排序算法
     * 
     * @param size 数组大小
     * @param max 随机数的最大值(不包含)
     * @return 包含随机整数的数组
     */
    public static int[] generateRandomArray(int size, int max) {
        // 创建指定大小的数组
        int[] arr = new int[size];
        // 为每个位置生成随机数
        for (int i = 0; i < size; i++) {
            // 生成[0, max)范围内的随机整数
            arr[i] = (int) (Math.random() * max);
        }
        return arr;
    }
性能测试
java 复制代码
    /**
     * 测试所有排序算法的性能
     * 
     * 测试流程:
     * 1. 分别测试基础排序算法和高级排序算法
     * 2. 每个算法使用相同的测试数据,确保公平比较
     * 3. 记录每个算法的执行时间
     * 4. 验证排序结果的正确性
     * 
     * @param arr 用于测试的原始数组
     */
    public static void testAllAlgorithms(int[] arr) {
        // 输出测试标题和基本信息
        System.out.println("\n=== 所有排序算法性能测试 ===");
        System.out.println("测试数组大小:" + arr.length);
        
        // 第一阶段:测试基础排序算法(时间复杂度O(n²))
        testBasicAlgorithms(arr);
        
        // 第二阶段:测试高级排序算法(时间复杂度O(n log n))
        testAdvancedAlgorithms(arr);
    }

    /**
     * 测试基础排序算法的性能
     * 
     * 测试算法:
     * - 冒泡排序:O(n²),稳定,适合小数据量
     * - 选择排序:O(n²),不稳定,交换次数少
     * - 插入排序:O(n²),稳定,对接近有序的数组效率高
     * - 希尔排序:O(n^1.3),不稳定,插入排序的改进版本
     * 
     * @param arr 用于测试的原始数组
     */
    private static void testBasicAlgorithms(int[] arr) {
        System.out.println("\n--- 基础排序算法 ---");
        
        // 测试冒泡排序
        int[] arr1 = copyArray(arr);
        long startTime = System.currentTimeMillis();
        bubbleSort(arr1);
        long endTime = System.currentTimeMillis();
        System.out.println("冒泡排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr1) ? "成功" : "失败"));
        
        // 测试选择排序
        int[] arr2 = copyArray(arr);
        startTime = System.currentTimeMillis();
        selectionSort(arr2);
        endTime = System.currentTimeMillis();
        System.out.println("选择排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr2) ? "成功" : "失败"));
        
        // 测试插入排序
        int[] arr3 = copyArray(arr);
        startTime = System.currentTimeMillis();
        insertionSort(arr3);
        endTime = System.currentTimeMillis();
        System.out.println("插入排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr3) ? "成功" : "失败"));
        
        // 测试希尔排序
        int[] arr4 = copyArray(arr);
        startTime = System.currentTimeMillis();
        shellSort(arr4);
        endTime = System.currentTimeMillis();
        System.out.println("希尔排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr4) ? "成功" : "失败"));
    }

    /**
     * 测试高级排序算法的性能
     * 
     * 测试算法:
     * - 快速排序:平均O(n log n),不稳定,实际应用中效率最高
     * - 归并排序:O(n log n),稳定,时间复杂度稳定但需要额外空间
     * - 堆排序:O(n log n),不稳定,原地排序空间效率高
     * 
     * 预期结果:这些算法在大数据量时应该比基础排序算法快很多
     * 
     * @param arr 用于测试的原始数组
     */
    private static void testAdvancedAlgorithms(int[] arr) {
        System.out.println("\n--- 高级排序算法 ---");
        
        // 测试快速排序
        int[] arr1 = copyArray(arr);
        long startTime = System.currentTimeMillis();
        quickSort(arr1);
        long endTime = System.currentTimeMillis();
        System.out.println("快速排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr1) ? "成功" : "失败"));
        
        // 测试归并排序
        int[] arr2 = copyArray(arr);
        startTime = System.currentTimeMillis();
        mergeSort(arr2);
        endTime = System.currentTimeMillis();
        System.out.println("归并排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr2) ? "成功" : "失败"));
        
        // 测试堆排序
        int[] arr3 = copyArray(arr);
        startTime = System.currentTimeMillis();
        heapSort(arr3);
        endTime = System.currentTimeMillis();
        System.out.println("堆排序:" + (endTime - startTime) + " 毫秒,结果:" + (isSorted(arr3) ? "成功" : "失败"));
    }
主方法
java 复制代码
    /**
     * 主方法:演示所有排序算法的功能和性能
     * 
     * 演示内容:
     * 1. 小规模数据排序:展示各种算法的排序过程和结果
     * 2. 大规模数据性能测试:比较不同算法的执行效率
     * 3. 验证排序结果的正确性
     * 
     * @param args 命令行参数(未使用)
     */
    public static void main(String[] args) {
        // 打印演示标题
        InterviewUtils.printTitle("所有排序算法演示");
        
        // 第一阶段:小规模数据排序演示
        // 使用固定的测试数据,便于观察排序过程
        int[] smallArray = {64, 34, 25, 12, 22, 11, 90, 88, 76, 54};
        System.out.println("小规模测试数据:");
        printArray(smallArray, "原始数组");
        
        // 依次测试各种排序算法,展示排序结果
        System.out.println("\n=== 小规模数据排序测试 ===");
        
        // 测试冒泡排序
        int[] arr1 = copyArray(smallArray);
        bubbleSort(arr1);
        printArray(arr1, "冒泡排序后");
        
        // 测试选择排序
        int[] arr2 = copyArray(smallArray);
        selectionSort(arr2);
        printArray(arr2, "选择排序后");
        
        // 测试插入排序
        int[] arr3 = copyArray(smallArray);
        insertionSort(arr3);
        printArray(arr3, "插入排序后");
        
        // 测试希尔排序
        int[] arr4 = copyArray(smallArray);
        shellSort(arr4);
        printArray(arr4, "希尔排序后");
        
        // 测试快速排序
        int[] arr5 = copyArray(smallArray);
        quickSort(arr5);
        printArray(arr5, "快速排序后");
        
        // 测试归并排序
        int[] arr6 = copyArray(smallArray);
        mergeSort(arr6);
        printArray(arr6, "归并排序后");
        
        // 测试堆排序
        int[] arr7 = copyArray(smallArray);
        heapSort(arr7);
        printArray(arr7, "堆排序后");
        
        // 第二阶段:大规模数据性能测试
        // 生成5000个随机数进行性能对比
        System.out.println("\n=== 大规模数据性能测试 ===");
        int[] largeArray = generateRandomArray(5000, 100000);
        testAllAlgorithms(largeArray);
        
        // 打印分隔线和完成信息
        InterviewUtils.printSeparator(50);
        System.out.println("所有排序算法演示完成!");
    }
相关推荐
0+11112 小时前
算法 --模拟
c++·算法·leetcode
青山是哪个青山12 小时前
LeetCode 300:最长递增子序列
算法
hetao173383712 小时前
2026-10-01~02 hetao1733837 的刷题记录
c++·算法
小虎牙^O^12 小时前
模糊数学综合评价:从模糊数学到建模评价类模型
人工智能·算法
随意起个昵称13 小时前
【单调队列优化DP】U301134 绿色通道
算法·动态规划
Nil20813 小时前
leetcode 739每日温度
算法·leetcode·职场和发展
lvwangshu13 小时前
势能分析(摊还分析)
数据结构·数学·算法
盘古开天166614 小时前
PPO算法代码实战(三):PyTorch从零实现PPO求解CartPole
人工智能·pytorch·算法
yyy(十一月限定版)14 小时前
CF2138A Cake Assignment
算法
盘古开天166614 小时前
PPO算法原理详解(下):Clip机制深入剖析与实践指南
人工智能·算法·机器学习