排序算法示例说明文档

概述

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

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

排序算法分类

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

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

算法步骤

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

优化点

  • 添加swapped标志,如果一轮比较中没有发生交换,说明数组已经有序,可以提前退出
/*** 冒泡排序算法* * 算法思想:通过相邻元素比较和交换,将最大的元素逐步"冒泡"到数组末尾* 具体逻辑:* 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++) {// 初始化交换标志为falseswapped = 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. 重复第二步,直到所有元素均排序完毕
  /*** 选择排序算法* * 算法思想:每次从未排序区间选择最小的元素,放到已排序区间的末尾* 具体逻辑:* 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
  /*** 插入排序算法* * 算法思想:将数组分为已排序和未排序两部分,逐个将未排序元素插入到已排序部分的正确位置* 具体逻辑:* 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时,完成排序
  /*** 希尔排序算法(改进的插入排序)* * 算法思想:通过设置不同的间隔序列,对数组进行分组插入排序,逐步减小间隔直到为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开始,逐步减小到1for (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时,排序完成

优化点

  • 选择基准元素的方法(三数取中、随机选择等)
  • 处理重复元素的优化
  • 小数组使用插入排序
  /*** 快速排序算法* * 算法思想:使用分治策略,选择一个基准元素(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-1int i = low - 1;// 遍历数组,将小于等于pivot的元素放到左边区域for (int j = low; j < high; j++) {// 如果当前元素小于等于pivotif (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时,开始合并

特点

  • 时间复杂度稳定,不受数据分布影响
  • 需要额外的空间复杂度
  • 适合外部排序
  /*** 归并排序算法* * 算法思想:使用分治策略,将数组递归地分成两半,分别排序后再合并* 具体逻辑:* 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个最大/最小元素
  /*** 堆排序算法* * 算法思想:利用堆这种数据结构进行排序,先将数组构建成最大堆,* 然后逐个取出堆顶元素(最大值),放到数组末尾,重复此过程直到堆为空* * 具体逻辑:* 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 - 1for (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而不是nheapify(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. 直接调用静态方法

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. 通过策略模式使用

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

3. 运行示例程序

# 运行基础排序算法演示
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. 空间限制:在内存受限的环境下,优先选择原地排序算法

相关资源

  • 算法导论
  • 数据结构与算法分析
  • 可视化排序算法

公共代码

工具方法
    /*** 交换数组中两个元素的位置* * @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++) {// 如果发现逆序,返回falseif (arr[i] < arr[i - 1]) {return false;}}// 所有元素都满足升序条件return true;}/*** 复制数组,创建数组的深拷贝* * @param arr 要复制的原数组* @return 新的数组副本,如果原数组为null则返回null*/public static int[] copyArray(int[] arr) {// 如果原数组为null,直接返回nullif (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;}
性能测试
    /*** 测试所有排序算法的性能* * 测试流程:* 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) ? "成功" : "失败"));}
主方法
    /*** 主方法:演示所有排序算法的功能和性能* * 演示内容:* 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("所有排序算法演示完成!");}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94683.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94683.shtml
英文地址,请注明出处:http://en.pswp.cn/web/94683.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务-26.网关登录校验-OpenFeign传递用户信息

一.OpenFeign传递用户信息前端发起的请求都会经过网关再到微服务&#xff0c;由于我们之前编写的过滤器和拦截器功能&#xff0c;微服务可以轻松获取登录用户信息。但有些业务是比较复杂的&#xff0c;请求到达微服务后还需要调用其它多个微服务。比如下单业务&#xff0c;流程…

Java:IO流——增强篇

目录 前言 一、缓冲流——让数据传输飞起来 &#x1f680; 1、缓冲思想 2、缓冲字节流 3、缓冲字符流 二、标准流——程序三大通道&#x1f6a6; 1、标准输入流&#xff08;System.in&#xff09; 2、标准输出流&#xff08;System.out&#xff09; 3、标准错误流&#xff08;S…

指针 (六):sizeof和strlen细节强化之“做题篇”

目录 1. sizeof和strlen的对比 1.1 sizeof 1.2 strlen 1.3 sizeof 和 strlen的对比 2. 数组和指针笔试题解析 2.1 ⼀维数组 2.2 字符数组 代码1&#xff1a; 代码2&#xff1a; 代码3&#xff1a; 代码4&#xff1a; 代码5&#xff1a; 代码6&#xff1a; 2.3 二维数组 3. 指针…

java中的数据类型

1 概述 Java 是一门面向对象的编程语言&#xff0c;其核心原则之一是一切皆对象。然而&#xff0c;基本数据类型&#xff08;如 int、double、char 等&#xff09;并非对象&#xff0c;不具备对象的特性&#xff0c;例如不能调用方法、不能参与继承体系等。而包装类&#xff08…

【系统分析师】高分论文:论信息系统开发方法及应用

【摘要】 本文以某国有企业的 B2B 商品棉交易平台的电子商务门户网站系统&#xff08;以下简称“门户网站”&#xff09;建设为例&#xff0c;讨论信息系统开发方法及应用。本文作者认为项目实施中选择合适的开发方法&#xff0c;既能满足用户需求&#xff0c;又能提高整个项目…

开源 C++ QT Widget 开发(七)线程--多线程及通讯

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源 C…

CPU-IO-网络-内核参数的调优

CPU-IO-网络-内核参数的调优CPU-IO-网络-内核参数的调优一、CPU 资源调优1.1 调整进程优先级&#xff08;nice 值&#xff09;1.2 设置 CPU 亲和力&#xff08;taskset&#xff09;1.3 cpu命令描述1.4 使用 vmstat 分析系统瓶颈二、磁盘 I/O 调优2.1 ulimit 资源限制2.2 测试磁…

JavaScript 实战进阶:工程化、性能与未来展望

一、JavaScript 工程化实践 随着前端项目规模的扩大&#xff0c;“工程化”成为提升开发效率、保证代码质量的核心手段。它涵盖模块化设计、构建工具链、代码规范与测试等多个维度。 &#xff08;一&#xff09;模块化开发 模块化是将复杂代码拆分为可复用、可维护的独立单元的…

破局与增长:全球电商的业财一体化战略与数字化未来

一、全球电商的数字化转型背景在瞬息万变的全球电商市场中&#xff0c;数字化转型已经成为企业保持竞争力的必由之路。近年来&#xff0c;国内品牌出海企业快速扩张&#xff0c;业务范围覆盖数十个国家和平台。然而&#xff0c;随着规模的几何级增长&#xff0c;行业普遍面临以…

Excel怎么换行?3种单元格内换行方法?【图文详解】Excel自动换行?Alt+Enter?

一、问题背景 在日常使用 Excel 处理数据时&#xff0c;很多人会遇到这样的困扰&#xff1a;输入长文本&#xff08;比如产品说明、多行备注、地址信息等&#xff09;时&#xff0c;文字会一直横向延伸&#xff0c;不仅导致单元格变宽、表格排版混乱&#xff0c;还可能遮挡相邻…

【生产实践】局域网多服务器多用户SSH登录批量测试(附完整shell脚本)

在企业运维场景中&#xff0c;局域网内多台服务器的SSH登录凭据&#xff08;用户名/密码&#xff09;验证是高频需求——无论是新服务器部署后的凭据校验&#xff0c;还是定期安全巡检中的凭据有效性检查&#xff0c;手动逐台逐用户测试不仅效率低下&#xff0c;还容易出错。 本…

专题:2025人工智能2.0智能体驱动ERP、生成式AI经济现状落地报告|附400+份报告PDF、原数据表汇总下载

原文链接&#xff1a;https://tecdat.cn/?p43713 2025年&#xff0c;人工智能正从技术概念快速渗透到产业实操层面——大模型推理能力的突破让复杂任务自动化成为可能&#xff0c;AI代理的规模化应用重构企业效率边界&#xff0c;而AI企业“天生全球化”的特性更是打破了传统创…

机器学习--支持向量机

目录 一、为什么需要 SVM&#xff1f;先解决 “怎么分才好” 的问题 二、SVM 的核心&#xff1a;什么是 “最好的超平面”&#xff1f;用 “间隔” 说话 1. 先搞懂两个关键概念 2. 目标&#xff1a;把 “间隔” 拉到最大 三、从 “想要最大间隔” 到 “解数学问题”&#…

Multi-output Classification and Multi-label Classification|多输出分类和多标签分类

----------------------------------------------------------------------------------------------- 这是我在我的网站中截取的文章&#xff0c;有更多的文章欢迎来访问我自己的博客网站rn.berlinlian.cn&#xff0c;这里还有很多有关计算机的知识&#xff0c;欢迎进行留言或…

【目标检测】论文阅读5

Small-object detection based on YOLOv5 in autonomous driving systems 发表期刊&#xff1a;Pattern Recognition Letters&#xff1b;发表时间&#xff1a;2023年 论文地址 摘要 随着自动驾驶领域的快速发展&#xff0c;对更快、更准确的目标检测框架的需求已经成为必要。…

Playwright进阶指南 (6) | 自动化测试实战

2025企业级测试解决方案&#xff1a;从单测到千级并发&#xff0c;打造高可用测试体系一、为什么传统自动化测试难以落地&#xff1f;根据2025年最新行业调研&#xff0c;测试项目失败的三大核心原因&#xff1a;失败原因占比典型表现维护成本过高45%选择器频繁失效&#xff0c…

uv 简单使用

二进制安装 powershell -ExecutionPolicy Bypass -c "irm https://ghproxy.cn/https://github.com/astral-sh/uv/releases/download/0.8.13/uv-installer.ps1 | iex"版本号&#xff1a;0.8.13&#xff0c;自行更改github加速前缀&#xff1a;https://ghproxy.cn/ 配置…

Linux程序管理

目录 一、Linux程序与进程 1、程序,进程,线程的概念 2、程序和进程的区别 3、进程和线程的区别 二、Linux进程基础(生命周期) 1、进程生命周期 2、父子进程的关系 三、程序管理 1、课程目标 2、常见的软件包类型 3、安装方法 使用独立的rpm包安装 rpm包的命名方法…

Linux-进程替换exec

文章目录进程替换exec 函数族使用说明查看命令的路径 which测试 execl测试 execlp测试 execv测试 execvp进程替换 概述 在 Windows 平台下&#xff0c;我们可以通过双击运行可执行程序&#xff0c;让这个可执行程序成为一个进程&#xff1b;而在 Linux 平台&#xff0c;我们可…

Seaborn数据可视化实战:Seaborn数据可视化实战入门

Seaborn数据可视化实战&#xff1a;从数据到图表的完整旅程 学习目标 通过本课程的学习&#xff0c;你将能够掌握使用Seaborn进行数据可视化的完整流程&#xff0c;从数据准备到图表设计&#xff0c;再到最终的图表呈现。本课程将通过一个具体的项目案例&#xff0c;帮助你全面…