当前位置: 首页 > news >正文

八大排序算法与 Java 代码实现

排序算法是计算机科学中最基础的算法之一,也是面试和笔试的常客。本文将详细介绍八大经典排序算法的核心思想、时间复杂度、稳定性,并给出完整的 Java 代码实现。无论你是初学者还是准备面试的开发者,相信本文都能帮你理清思路,加深理解。

排序算法分类

按照是否使用比较操作,排序算法可分为两类:

  • 比较排序:通过比较元素大小决定顺序,如冒泡、选择、插入、希尔、归并、快速、堆排序。时间复杂度下界为 O(n log n)。

  • 非比较排序:不通过比较,而是利用元素本身的特征排序,如计数排序、基数排序、桶排序。在特定条件下可以达到 O(n) 的时间复杂度。

按照稳定性,排序算法又可分为稳定排序和不稳定排序:稳定排序能保持相等元素的原始相对顺序。

1. 冒泡排序(Bubble Sort)

算法思想:重复遍历数组,依次比较相邻元素,若顺序错误则交换。每遍历一次,最大(或最小)的元素就会“浮”到末尾。

时间复杂度:O(n²)
空间复杂度:O(1)
稳定性:稳定

public class BubbleSort { public static void bubbleSort(int[] arr) { 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. 选择排序(Selection Sort)

算法思想:每次从未排序部分选出最小(或最大)元素,放到已排序部分的末尾。

时间复杂度:O(n²)
空间复杂度:O(1)
稳定性:不稳定(例如 [5, 5, 2] 第一个 5 会和 2 交换,导致两个 5 的相对顺序改变)

public class SelectionSort { public static void selectionSort(int[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) { minIdx = j; } } // 交换 int temp = arr[i]; arr[i] = arr[minIdx]; arr[minIdx] = temp; } } }

3. 插入排序(Insertion Sort)

算法思想:将数组看作已排序和未排序两部分,每次从未排序部分取出第一个元素,插入到已排序部分的正确位置。

时间复杂度:O(n²)(最好情况 O(n))
空间复杂度:O(1)
稳定性:稳定

public class InsertionSort { public static void insertionSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int key = arr[i]; int j = i - 1; while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } } }

4. 希尔排序(Shell Sort)

算法思想:插入排序的改进版,通过将数组按一定间隔分组,对每组进行插入排序,然后逐步缩小间隔,最终间隔为 1 时完成排序。

时间复杂度:依赖于步长序列,平均 O(n log n) ~ O(n²)
空间复杂度:O(1)
稳定性:不稳定(分组插入可能打乱相等元素顺序)

public class ShellSort { public static void shellSort(int[] arr) { int n = arr.length; for (int gap = n / 2; gap > 0; gap /= 2) { for (int i = gap; i < n; i++) { int temp = arr[i]; int j = i; while (j >= gap && arr[j - gap] > temp) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } } } }

5. 归并排序(Merge Sort)

算法思想:采用分治法,将数组递归分成两半分别排序,再将两个有序子数组合并。

时间复杂度:O(n log n)
空间复杂度:O(n)(需要额外数组存储合并结果)
稳定性:稳定

public class MergeSort { 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[] temp = new int[right - left + 1]; int i = left, j = mid + 1, k = 0; 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 (i = 0; i < temp.length; i++) { arr[left + i] = temp[i]; } } }

6. 快速排序(Quick Sort)

算法思想:选择一个基准(pivot),将小于基准的元素放左边,大于基准的放右边,然后递归地对左右子数组排序。

时间复杂度:平均 O(n log n),最坏 O(n²)
空间复杂度:O(log n)(递归栈空间)
稳定性:不稳定

下面是简洁易懂的递归版本(使用额外数组)和原地分区版本。这里给出经典的原地分区实现。

public class QuickSort { 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; } } // 将基准放到正确位置 int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp; return i + 1; } }

7. 堆排序(Heap Sort)

算法思想:利用堆这种数据结构。首先将数组构建成最大堆,然后反复将堆顶(最大元素)与末尾交换,并调整堆。

时间复杂度:O(n log n)
空间复杂度:O(1)
稳定性:不稳定

public class HeapSort { 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(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); } } }

8. 计数排序(Counting Sort)

算法思想:非比较排序,适用于数据范围不大的整数序列。统计每个元素出现的次数,然后根据次数依次输出。

时间复杂度:O(n + k),k 为数据范围
空间复杂度:O(k)
稳定性:稳定(若从后往前填充)

public class CountingSort { public static void countingSort(int[] arr) { if (arr.length == 0) return; // 找出最大值和最小值 int max = arr[0], min = arr[0]; for (int num : arr) { if (num > max) max = num; if (num < min) min = num; } int range = max - min + 1; int[] count = new int[range]; int[] output = new int[arr.length]; // 统计每个元素出现的次数 for (int num : arr) { count[num - min]++; } // 计算累积次数(用于稳定排序) for (int i = 1; i < range; i++) { count[i] += count[i - 1]; } // 从后往前填充,保证稳定性 for (int i = arr.length - 1; i >= 0; i--) { int num = arr[i]; output[count[num - min] - 1] = num; count[num - min]--; } // 将结果复制回原数组 System.arraycopy(output, 0, arr, 0, arr.length); } }

总结对比

排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性
冒泡排序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(n²)O(1)不稳定
归并排序O(n log n)O(n log n)O(n)稳定
快速排序O(n log n)O(n²)O(log n)不稳定
堆排序O(n log n)O(n log n)O(1)不稳定
计数排序O(n + k)O(n + k)O(k)稳定
http://www.jsqmd.com/news/477939/

相关文章:

  • 我用一台 Windows 笔记本,把 OpenClaw 跑起来了(小白可复现)
  • WVP-PRO流媒体服务:无人观看场景下的智能流生命周期管理
  • 研究flow3d模拟选区激光熔化Inconel 718制件内部缺陷的形成机理,优化工艺参数,从...
  • 150+数字人形象免费选!lite-avatar形象库快速部署与使用全攻略
  • Java String 类笔记
  • STM32F103+ESP8266 AP模式实战:TCP/UDP通信与网络调试全流程解析
  • 2.0 ARP欺骗攻击(基础版)
  • CosyVoice2-0.5B声音克隆效果展示:四川话/英文/日文多语种真实案例集
  • 【C++】STL详解(三)—vector使用手册:不看你会后悔
  • Hibernate与JPA方言配置:跨数据库开发的统一接口
  • 分布式事务解决方案全景指南:2PC、TCC、SAGA 与 Seata 实战
  • 【Windows】Dify + Ollama/Xinference/GPUStack:一站式AI开发环境搭建指南
  • 硬件设计之电源反接防护:从基础二极管到高效MOS管的选型实战
  • 跨微服务的“数据孤岛”解法:利用声明式 API 构建去中心化的数据联邦
  • SecGPT-14B步骤详解:Chainlit前端对接vLLM服务全流程
  • 从零到精通:UNIX BENCH性能基准测试全流程实战
  • 深入解析HDMI中的EDID与E-EDID:从基础结构到实际应用
  • StructBERT中文句子相似度WebUI实战手册:Websocket实时结果推送实验
  • 01-SA8155P 冷启动EDL模式硬件配置与常见问题解析
  • 泰山派嵌入式Linux驱动开发基础入门篇
  • L2-006 数的遍历(递归经典 ,图论 )
  • Phi-3-Mini-128K部署优化:bfloat16 vs float16显存与推理速度实测对比
  • Qwen3-TTS问题解决:常见部署错误排查,快速搞定语音合成
  • DAMO-YOLO快速体验:开箱即用的赛博朋克AI视觉工具
  • 从零构建认知:数据库系统核心概念与演进脉络深度解析
  • C++与区块链智能合约
  • 全面解读 Databricks:从架构、引擎到优化策略
  • java零碎知识(更新中)
  • Xiaojie雷达之路---毫米波雷达实战解析---相位差在速度测量中的关键作用
  • 基于SGL8022W的MOSS环形触摸灯硬件设计