【JavaGuide】十大经典排序算法总结

冒泡排序

算法步骤

从前往后不断的两两比较,这样当前最大的元素总是会排在最后面。所以称为冒泡。

图解算法

代码实现

java 复制代码
public static int[] bubbleSort(int[] arr) {
	// i是排好了几个数
   for (int i = 1; i < arr.length; i++) {
	   // flag标记当前循环是否调整了顺序,如果没有调整,说明排序完成
       boolean flag = true;
       // arr.length - i控制数组尾巴
       for (int j = 0; j < arr.length - i; j++) {
           if (arr[j] > arr[j + 1]) {
               int tmp = arr[j];
               arr[j] = arr[j + 1];
               arr[j + 1] = tmp;
               flag = false;
           }
       }
       if (flag) {
           break;
       }
   }
   return arr;
}

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n ) O(n) O(n) ,最差: O ( n 2 ) O(n^2) O(n2), 平均: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

排序方式:内部排序

选择排序

算法步骤

从前往后不断地选择最小/最大的元素和当前未排序序列的头进行交换

图解算法

代码实现

java 复制代码
public static int[] selectionSort(int[] arr) {
   // 找到的元素放到第i个,未排序序列头
   for (int i = 0; i < arr.length - 1; i++) {
       // minIndex记录当前未排序的最小元素的索引
       int minIndex = i;
       for (int j = i + 1; j < arr.length; j++) {
           if (arr[j] < arr[minIndex]) {
               minIndex = j;
           }
       }
       // 交换
       if (minIndex != i) {
           int tmp = arr[i];
           arr[i] = arr[minIndex];
           arr[minIndex] = tmp;
       }
   }
   return arr;
}

算法分析

稳定性:不稳定

时间复杂度:最佳: O ( n 2 ) O(n^2) O(n2) ,最差: O ( n 2 ) O(n^2) O(n2), 平均: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

排序方式:内部排序

插入排序

算法步骤

就是扑克牌理牌。读取未排列序列的元素,拿到新元素后从后往前遍历已排序序列找到合适的位置插入。

图解算法

代码实现

java 复制代码
public static int[] insertionSort(int[] arr) {
   for (int i = 1; i < arr.length; i++) {
   	  // preindex记录已排序序列的尾
       int preIndex = i - 1;
       // current是当前要插入的元素
       int current = arr[i];
       while (preIndex >= 0 && current < arr[preIndex]) {
       	  // 往后移
           arr[preIndex + 1] = arr[preIndex];
           preIndex -= 1;
       }
       arr[preIndex + 1] = current;
   }
   return arr;
}

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n ) O(n) O(n) ,最差: O ( n 2 ) O(n^2) O(n2), 平均: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

排序方式:内部排序

希尔排序

算法步骤

不断的按照增量来分出子数组的数量,子数组内部进行插入排序,然后缩小增量,减少分子数组的数量,然后接着插入排序,直到增量为1之后再进行一次插入排序即可。

算法图解

代码实现

java 复制代码
public static int[] shellSort(int[] arr) {
   int n = arr.length;
   int gap = n / 2;
   while (gap > 0) {
       for (int i = gap; i < n; i++) {
           int current = arr[i];
           int preIndex = i - gap;
           // 插入排序
           while (preIndex >= 0 && arr[preIndex] > current) {
               arr[preIndex + gap] = arr[preIndex];
               preIndex -= gap;
           }
           arr[preIndex + gap] = current;
       }
       gap /= 2;
   }
   return arr;
}

算法分析

稳定性:不稳定

时间复杂度:最佳: O ( n l o g n ) O(nlogn) O(nlogn), 最差: O ( n 2 ) O(n^2) O(n2) 平均: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( 1 ) O(1) O(1)

排序方式:内部排序

归并排序

算法步骤

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

就是让子数列内部有序,然后让两个子序列段间有序,不断重复直到整个序列有序。

图解算法

代码实现

java 复制代码
public static int[] mergeSort(int[] arr) {
   if (arr.length <= 1) {
       return arr;
   }
   int middle = arr.length / 2;
   int[] arr_1 = Arrays.copyOfRange(arr, 0, middle);
   int[] arr_2 = Arrays.copyOfRange(arr, middle, arr.length);
   return merge(mergeSort(arr_1), mergeSort(arr_2));
}

public static int[] merge(int[] arr_1, int[] arr_2) {
   int[] sorted_arr = new int[arr_1.length + arr_2.length];
   int idx = 0, idx_1 = 0, idx_2 = 0;
   while (idx_1 < arr_1.length && idx_2 < arr_2.length) {
       if (arr_1[idx_1] < arr_2[idx_2]) {
           sorted_arr[idx] = arr_1[idx_1];
           idx_1 += 1;
       } else {
           sorted_arr[idx] = arr_2[idx_2];
           idx_2 += 1;
       }
       idx += 1;
   }
   if (idx_1 < arr_1.length) {
       while (idx_1 < arr_1.length) {
           sorted_arr[idx] = arr_1[idx_1];
           idx_1 += 1;
           idx += 1;
       }
   } else {
       while (idx_2 < arr_2.length) {
           sorted_arr[idx] = arr_2[idx_2];
           idx_2 += 1;
           idx += 1;
       }
   }
   return sorted_arr;
}

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n l o g n ) O(nlogn) O(nlogn), 最差: O ( n l o g n ) O(nlogn) O(nlogn), 平均: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( n ) O(n) O(n)

排序方式:外部排序

快速排序

算法步骤

从序列中随机挑出一个元素,做为 基准;通过一趟排序将待排序列分隔成独立的两部分,比基准小的在左边,比基准大的在右边,则可分别对这两部分子序列继续进行排序,以达到整个序列有序。

图解算法

代码实现

java 复制代码
public static int partition(int[] array, int low, int high) {
   int pivot = array[high];
   int pointer = low;
   for (int i = low; i < high; i++) {
       if (array[i] <= pivot) {
           int temp = array[i];
           array[i] = array[pointer];
           array[pointer] = temp;
           pointer++;
       }
       System.out.println(Arrays.toString(array));
   }
   int temp = array[pointer];
   array[pointer] = array[high];
   array[high] = temp;
   return pointer;
}
public static void quickSort(int[] array, int low, int high) {
   if (low < high) {
       int position = partition(array, low, high);
       quickSort(array, low, position - 1);
       quickSort(array, position + 1, high);
   }
}

算法分析

稳定性:不稳定

时间复杂度:最佳: O ( n l o g n ) O(nlogn) O(nlogn), 最差: O ( n 2 ) O(n^2) O(n2),平均: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( l o g n ) O(logn) O(logn)

排序方式:内部排序

堆排序

算法步骤

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的值总是小于(或者大于)它的父节点。

图解算法

算法分析

稳定性:不稳定

时间复杂度:最佳: O ( n l o g n ) O(nlogn) O(nlogn), 最差: O ( n l o g n ) O(nlogn) O(nlogn), 平均: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( 1 ) O(1) O(1)

排序方式:内部排序

计数排序

算法步骤

  1. 使用一个额外的数组 C,其中第 i 个元素是待排序数组 A 中值等于 i 的元素的个数。然后根据数组 C 来将 A 中的元素排到正确的位置。
  2. 对 C 数组变形,新元素的值是该元素与前一个元素值的和,即当 i>1 时 C[i] = C[i] + C[i-1]:目的是为了确定每个元素在排序后数组中的正确位置。

图解算法

代码实现

java 复制代码
// 获取最大最小值
private static int[] getMinAndMax(int[] arr) {
   int maxValue = arr[0];
   int minValue = arr[0];
   for (int i = 0; i < arr.length; i++) {
       if (arr[i] > maxValue) {
           maxValue = arr[i];
       } else if (arr[i] < minValue) {
           minValue = arr[i];
       }
   }
   return new int[] { minValue, maxValue };
}
public static int[] countingSort(int[] arr) {
   if (arr.length < 2) {
       return arr;
   }
   int[] extremum = getMinAndMax(arr);
   int minValue = extremum[0];
   int maxValue = extremum[1];
   int[] countArr = new int[maxValue - minValue + 1];
   int[] result = new int[arr.length];

   for (int i = 0; i < arr.length; i++) {
       countArr[arr[i] - minValue] += 1;
   }
   for (int i = 1; i < countArr.length; i++) {
       countArr[i] += countArr[i - 1];
   }
   for (int i = arr.length - 1; i >= 0; i--) {
       int idx = countArr[arr[i] - minValue] - 1;
       result[idx] = arr[i];
       countArr[arr[i] - minValue] -= 1;
   }
   return result;
}

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n + k ) O(n+k) O(n+k) 最差: O ( n + k ) O(n+k) O(n+k) 平均: O ( n + k ) O(n+k) O(n+k)

空间复杂度: O ( k ) O(k) O(k)

排序方式:外部排序

桶排序

算法步骤

遍历输入数据,并且把数据依次映射到对应的桶里去。对每个非空的桶进行排序。从非空桶里把排好序的数据拼接起来。

图解算法

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n + k ) O(n+k) O(n+k) 最差: O ( n 2 ) O(n^2) O(n2) 平均: O ( n + k ) O(n+k) O(n+k)

空间复杂度: O ( n + k ) O(n+k) O(n+k)

排序方式:外部排序

基数排序

算法步骤

  1. 对元素中的每一位数字进行排序,从最低位开始排序。按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。
  2. A 为原始数组,从最低位开始取每个位组成 radix 数组;对 radix 进行计数排序(利用计数排序适用于小范围数的特点)。

图解算法

算法实现

java 复制代码
public static int[] radixSort(int[] arr) {
   if (arr.length < 2) {
       return arr;
   }
   int N = 1;
   int maxValue = arr[0];
   for (int element : arr) {
       if (element > maxValue) {
           maxValue = element;
       }
   }
   while (maxValue / 10 != 0) {
       maxValue = maxValue / 10;
       N += 1;
   }
   for (int i = 0; i < N; i++) {
       List<List<Integer>> radix = new ArrayList<>();
       for (int k = 0; k < 10; k++) {
           radix.add(new ArrayList<Integer>());
       }
       for (int element : arr) {
           int idx = (element / (int) Math.pow(10, i)) % 10;
           radix.get(idx).add(element);
       }
       int idx = 0;
       for (List<Integer> l : radix) {
           for (int n : l) {
               arr[idx++] = n;
           }
       }
   }
   return arr;
}

算法分析

稳定性:稳定

时间复杂度:最佳: O ( n × k ) O(n×k) O(n×k) 最差: O ( n × k ) O(n×k) O(n×k) 平均: O ( n × k ) O(n×k) O(n×k)

空间复杂度: O ( n + k ) O(n+k) O(n+k)

排序方式:外部排序

计数排序 vs 桶排序 vs 基数排序

这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:

  • 计数排序:每个桶只存储单一键值(出现了几次)
  • 桶排序:每个桶存储一定范围的数值(桶内部排序)
  • 基数排序:根据键值的每位数字来分配桶(从数字的低位数到高位数排序)

排序算法总结


  • 比较类排序是通过比较来决定元素间的相对次序。比较类排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。
  • 非比较排序是要确定每个元素之前的已有的元素个数。非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。
相关推荐
曦月逸霜2 分钟前
蓝桥杯高频考点——高精度(含C++源码)
c++·算法·蓝桥杯
ゞ 正在缓冲99%…11 分钟前
leetcode152.乘积最大子数组
数据结构·算法·leetcode
Seven9728 分钟前
【Guava】IO工具
java
独行soc33 分钟前
2025年渗透测试面试题总结-某腾某讯-技术安全实习生升级(题目+回答)
java·python·安全·web安全·面试·职场和发展·红蓝攻防
闯闯爱编程1 小时前
数组与特殊压缩矩阵
数据结构·算法·矩阵
用键盘当武器的秋刀鱼1 小时前
springBoot统一响应类型3.5.3版本
java·spring boot·spring
qq_431510161 小时前
tomcat组件概览
java·tomcat
秋风战士1 小时前
通信算法之255:无人机频谱探测设备技术详解
算法·无人机
栗筝i1 小时前
Spring 核心技术解析【纯干货版】- XVII:Spring 网络模块 Spring-WebFlux 模块精讲
java·网络·spring
工业互联网专业1 小时前
基于springcloud微服务架构的巡游出租管理平台
java·vue.js·spring cloud·微服务·毕业设计·源码·课程设计