java 数组排序算法
数组排序是计算机科学中的基础问题,有多种经典算法。以下是主要的排序方法分类及代表算法:
一、比较排序算法(基于元素比较)
-
冒泡排序 (Bubble Sort)
- 原理:重复遍历数组,比较相邻元素并交换,将最大/小值"冒泡"到末尾。
- 时间复杂度:
- 平均 & 最坏:
O(n²)
- 最好(已有序):
O(n)
- 平均 & 最坏:
- 空间复杂度:
O(1)
(原地排序) - 稳定性:✅ 稳定
-
选择排序 (Selection Sort)
- 原理:每次从未排序部分选择最小(或最大)元素,与未排序部分首元素交换。
- 时间复杂度:
O(n²)
(任何情况) - 空间复杂度:
O(1)
- 稳定性:❌ 不稳定(交换可能破坏顺序)
-
插入排序 (Insertion Sort)
- 原理:将未排序元素逐个插入已排序部分的正确位置。
- 时间复杂度:
- 平均 & 最坏:
O(n²)
- 最好(已有序):
O(n)
- 平均 & 最坏:
- 空间复杂度:
O(1)
- 稳定性:✅ 稳定
-
希尔排序 (Shell Sort)
- 原理:插入排序的改进版,通过分组间隔比较减少元素移动次数。
- 时间复杂度:
O(n log n) ~ O(n²)
(取决于间隔序列) - 空间复杂度:
O(1)
- 稳定性:❌ 不稳定
-
快速排序 (Quick Sort)
- 原理:分治法。选取基准值,将数组分为小于基准和大于基准的两部分,递归排序。
- 时间复杂度:
- 平均:
O(n log n)
- 最坏(极端不平衡):
O(n²)
- 平均:
- 空间复杂度:
O(log n)
(递归栈) - 稳定性:❌ 不稳定
-
归并排序 (Merge Sort)
- 原理:分治法。将数组递归拆分为子数组排序,再合并有序子数组。
- 时间复杂度:
O(n log n)
(任何情况) - 空间复杂度:
O(n)
(合并需额外空间) - 稳定性:✅ 稳定
-
堆排序 (Heap Sort)
- 原理:将数组构建为最大堆,重复从堆顶取最大值放入已排序区。
- 时间复杂度:
O(n log n)
- 空间复杂度:
O(1)
- 稳定性:❌ 不稳定
二、非比较排序算法(基于数值特性)
-
计数排序 (Counting Sort)
- 原理:统计每个元素出现次数,直接计算元素在有序数组中的位置。
- 要求:元素为整数且范围
k
较小。 - 时间复杂度:
O(n + k)
- 空间复杂度:
O(n + k)
- 稳定性:✅ 稳定
-
桶排序 (Bucket Sort)
- 原理:将元素分到多个桶中,对每个桶单独排序后合并。
- 要求:数据均匀分布。
- 时间复杂度:
- 平均:
O(n + k)
(k
为桶数) - 最坏:
O(n²)
(所有元素集中在一个桶)
- 平均:
- 空间复杂度:
O(n + k)
- 稳定性:✅ 稳定(取决于桶内排序算法)
-
基数排序 (Radix Sort)
- 原理:按位数从低位到高位依次排序(通常用计数排序作为子过程)。
- 要求:元素为整数或固定位数的字符串。
- 时间复杂度:
O(d(n + k))
(d
为最大位数,k
为基数) - 空间复杂度:
O(n + k)
- 稳定性:✅ 稳定
三、总结对比
算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 | 适用场景 |
---|---|---|---|---|---|
冒泡排序 | 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) | ✅ | 链表排序、外部排序 |
堆排序 | O(n log n) | O(n log n) | O(1) | ❌ | 内存受限场景 |
计数排序 | O(n + k) | O(n + k) | O(n + k) | ✅ | 小范围整数排序 |
基数排序 | O(d(n + k)) | O(d(n + k)) | O(n + k) | ✅ | 多位数整数或字符串排序 |
关键结论:
- 高效通用排序:快速排序(平均最优)、归并排序(稳定且高效)、堆排序(避免最坏情况)。
- 小规模数据:插入排序(简单且对部分有序数据高效)。
- 非比较场景:计数排序、桶排序、基数排序(特定条件下可达线性时间复杂度)。
模板模式实现数组排序
1.抽象类(抽象排序)
public abstract class AbstractSort {final void abstractSort(Number[] numbers) {beforeSort(numbers);Number[] result = sort(numbers);afterSort(result);}private void beforeSort(Number[] numbers) {System.out.println("排序前的数组:");for (Number number : numbers) {System.out.print(" " + number);}System.out.println();}private void afterSort(Number[] numbers) {System.out.println("排序后的数组:");for (Number number : numbers) {System.out.print(" " + number);}System.out.println();}abstract Number[] sort(Number[] numbers);}
接下来就是各大排序算法:均继承抽象排序类
2.冒泡排序
public class BubbleSort extends AbstractSort {@Overridepublic Number[] sort(Number[] numbers) {for (int i = 0; i < numbers.length; i++) {for (int j = 0; j < numbers.length - 1 - i; j++) {if (numbers[j].doubleValue() > numbers[j + 1].doubleValue()) {Number temp = numbers[j];numbers[j] = numbers[j + 1];numbers[j + 1] = temp;}}}return numbers;}}
3.桶排序
public class BucketSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {// 分别定义最大、最小值Number min = numbers[0], max = numbers[0];for (int i = 0; i < numbers.length; i++) {if (numbers[i].doubleValue() > max.doubleValue()) {max = numbers[i];}if (numbers[i].doubleValue() < min.doubleValue()) {min = numbers[i];}}//桶初始化int bucketCount = numbers.length;ArrayList<LinkedList<Double>> list = new ArrayList<>(bucketCount);for (int i = 0; i < bucketCount; i++) {list.add(new LinkedList<>());}//将每个元素放入桶中double d = max.doubleValue() - min.doubleValue();for (Number number : numbers) {int num = (int) ((number.doubleValue() - min.doubleValue()) * (bucketCount - 1) / d);list.get(num).add(number.doubleValue());}//对每个桶内部进行排序for (LinkedList<Double> doubles : list) {Collections.sort(doubles);}//输出全部元素Number[] result = new Number[bucketCount];int index = 0;for (int i = 0; i < list.size(); i++) {for (int j = 0; j < list.get(i).size(); j++) {result[index++] = list.get(i).get(j);}}return result;}
}
4.计数排序
public class CountSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {sort(numbers, 1);return numbers;}void sort(Number[] numbers, Number offset) {int[] count = new int[numbers.length];for (Number number : numbers) {count[number.intValue() - offset.intValue()]++;}// 填充数组for (int i = 0, k = 0; i < count.length; i++) {for (int j = 0; j < count[i]; j++) {numbers[k++] = i + offset.intValue();}}}}
5.堆排序
public class HeapSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {// 把无序数组构建成最大堆for (int i = numbers.length / 2 - 1; i >= 0; i--) {adjustHeap(numbers, i, numbers.length);}// 调整堆结构+交换堆顶元素与末尾元素,调整堆产生新的堆顶for (int i = numbers.length - 1; i > 0; i--) {Number temp = numbers[i];numbers[i] = numbers[0];numbers[0] = temp;// "下沉"调整最大堆adjustHeap(numbers, 0, i);}return numbers;}void adjustHeap(Number[] numbers, int parentIndex, int length) {// temp 保存父节点值,用于最后的赋值Number temp = numbers[parentIndex];int childIndex = parentIndex * 2 + 1;while (childIndex < length) {// 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子if (childIndex + 1 < length && numbers[childIndex].doubleValue()< numbers[childIndex + 1].doubleValue()) {childIndex++;}// 如果父节点大于任何一个孩子的值,则直接跳出if (temp.doubleValue() >= numbers[childIndex].doubleValue()) {break;}// 无须真正交换,单向赋值即可numbers[parentIndex] = numbers[childIndex];parentIndex = childIndex;// 下一个左孩子childIndex = 2 * childIndex + 1;}numbers[parentIndex] = temp;}}
6.插入排序
public class InsertSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {for (int i = 1; i < numbers.length; i++) {// 当前待比较的元素Number temp = numbers[i];for (int j = i - 1; j >= 0; j--) {// 前面的元素比当前待比较的元素大if (numbers[j].doubleValue() > temp.doubleValue()) {// 前面的大元素右移一位numbers[j + 1] = numbers[j];// 若索引0的数还比temp大,那说明temp是最小元素,放在索引0的位置上if (j == 0) {numbers[j] = temp;}} else {// 遇到了比当前待比较的元素小,插在后一位numbers[j + 1] = temp;break;}}}return numbers;}}
7.归并排序
public class MergeSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {return mergeSort(numbers);}private Number[] mergeSort(Number[] numbers) {// 拆分到只剩下一个元素,返回if (numbers.length < 2) {return numbers;}int mid = numbers.length / 2;// 左数组Number[] left = Arrays.copyOfRange(numbers, 0, mid);left = mergeSort(left);// 右数组Number[] right = Arrays.copyOfRange(numbers, mid, numbers.length);right = mergeSort(right);return merge(left, right);}private Number[] merge(Number[] left, Number[] right) {Number[] result = new Number[left.length + right.length];int indexLeft = 0;int indexRight = 0;for (int i = 0; i < result.length; i++) {if (indexLeft < left.length && indexRight < right.length) {if (left[indexLeft].doubleValue() <= right[indexRight].doubleValue()) {result[i] = left[indexLeft++].doubleValue();} else {result[i] = right[indexRight++].doubleValue();}} else if (indexLeft >= left.length) {result[i] = right[indexRight++].doubleValue();} else {result[i] = left[indexLeft++].doubleValue();}}return result;}}
8.快速排序
public class QuickSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {quickSort(numbers, 0, numbers.length - 1);return numbers;}private void quickSort(Number[] numbers, int left, int right) {// 分别设置向右搜索i、向左搜索j指针int i = left;int j = right;// 基准Number temp = numbers[i];while (i < j) {//numbers[i]不小于基准,不用交换,继续向前搜索while (i < j && numbers[j].doubleValue() > temp.doubleValue()) {j--;}while (i < j && numbers[i].doubleValue() < temp.doubleValue()) {i++;}// i和j位置的元素互换Number tmp = numbers[i];numbers[i] = numbers[j];numbers[j] = tmp;}if (left < i - 1) {// 同样的方式处理左子区quickSort(numbers, left, i - 1);}if (right > i + 1) {quickSort(numbers, i + 1, right);}}}
9.基数排序
public class RadixSort<T> extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {Number max = numbers[0];for (int i = 1; i < numbers.length; i++) {if (numbers[i].intValue() > max.intValue()) {max = numbers[i];}}// 最大数是几位数int maxLength = (max.intValue() + "").length();int[][] bucket = new int[10][numbers.length];// 定义一个一维数组来记录每个桶中每次放入的元素个数int[] bucketElementCounts = new int[10];// 通过n取出每个元素上的位数for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {for (int j = 0; j < numbers.length; j++) {int digit = numbers[j].intValue() / n % 10;// 将元素放入对应的桶中bucket[digit][bucketElementCounts[digit]] = numbers[j].intValue();// 将桶中的元素个数++// 这样接下来的元素就可以排在前面的元素后面bucketElementCounts[digit]++;}// 按照桶里的元素取出并放回原数组int index = 0;for (int k = 0; k < bucket.length; k++) {if (bucketElementCounts[k] != 0) {for (int l = 0; l < bucketElementCounts[k]; l++) {numbers[index++] = bucket[k][l];}bucketElementCounts[k] = 0;}}}return numbers;}
}
10.选择排序
public class SelectSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {for (int i = 0; i < numbers.length - 1; i++) {// 记录当前元素位置int index = i;for (int j = i + 1; j < numbers.length; j++) {// 从下一个元素开始查找,找到最小的之后与i换位置if (numbers[j].doubleValue() < numbers[index].doubleValue()) {index = j;}}// numbers[i]与numbers[index]换位置Number temp = numbers[i];numbers[i] = numbers[index];numbers[index] = temp;}return numbers;}}
11.希尔排序
public class ShellSort extends AbstractSort {@OverrideNumber[] sort(Number[] numbers) {for (int gap = numbers.length / 2; gap > 0; gap /= 2) {for (int i = gap; i < numbers.length; i++) {for (int j = i - gap; j >= 0; j -= gap) {if (numbers[j].doubleValue() > numbers[j + gap].doubleValue()) {Number temp = numbers[j];numbers[j] = numbers[j + gap];numbers[j + gap] = temp;}}}}return numbers;}
}
12.测试方法
public class Main {public static void main(String[] args) {Integer[] integers = new Integer[]{30, 11, 6, 25, 40, 17, 12, 55, 36};new HeapSort().abstractSort(integers);}
}