上篇文章详细的描述了四种简单的排序算法及其优化的一些方案,其实比起基本的排序算法,我觉得学习者更应该掌握优化后的排序算法甚至希望可以在评论区上看到更多不同的解法,只要是自己去深入研究的,都可以放到评论区一起探讨甚至给博主纠正。下面就是要详细刨析另外四种不常见的排序算法,性能更高,但是其实真正的使用场景偏少。
特性/算法 | 冒泡排序 (Bubble Sort) | 选择排序 (Selection Sort) | 插入排序 (Insertion Sort) | 快速排序 (Quick Sort) | 归并排序 (Merge Sort) | 堆排序 (Heap Sort) | 计数排序 (Counting Sort) | 基数排序 (Radix Sort) |
---|---|---|---|---|---|---|---|---|
稳定性 | 稳定 | 不稳定 | 稳定 | 不稳定 | 稳定 | 不稳定 | 稳定 | 稳定 |
时间复杂度 | O(n²) | O(n²) | O(n²) | 平均O(n log n) | O(n log n) | O(n log n) | O(n + k) | O(n * k) |
空间复杂度 | O(1) | O(1) | O(1) | O(log n) | O(n) | O(1) | O(k) | O(n + k) |
适用场景 | 小规模数据集 | 小规模数据集 | 小规模或部分有序数据集 | 大规模数据集 | 大规模数据集 | 大规模数据集 | 小范围整数数据集 | 小范围整数数据集 |
主要操作 | 比较与交换相邻元素 | 找最小值并交换 | 插入到正确位置 | 分治法 | 分治法 | 构建与维护堆 | 统计频率 | 按位排序 |
递归/迭代 | 迭代 | 迭代 | 迭代 | 递归 | 递归 | 迭代 | 迭代 | 迭代 |
优点 | 实现简单 | 实现简单 | 对部分有序数据效果好 | 高效的平均性能 | 稳定且高效 | 原地排序 | 线性时间复杂度 | 线性时间复杂度 |
缺点 | 效率低 | 效率低 | 对大规模数据效率低 | 最坏情况O(n²) | 需要额外空间 | 不稳定 | 只适用于小范围整数 | 只适用于小范围整数 |
口诀:么(冒)选(选)叉(插),快(快)乖(归)嘚(堆),gg(计基)。接近方言话选衣服的时候不要选了,男生催女生不要选啦,快一点乖乖,不然要gg了。有猜到是哪里的方言嘛?评论区留言,猜对有奖。
归并排序代码展示:
public class Main {
// 归并排序方法
public static void mergeSort(int[] arr, int low, int high) {
if (low < high) {
// 找到中间点
int mid = (low + high) / 2;
// 递归排序左半部分
mergeSort(arr, low, mid);
// 递归排序右半部分
mergeSort(arr, mid + 1, high);
// 合并两个已排序的部分
merge(arr, low, mid, high);
}
}
// 合并方法
private static void merge(int[] arr, int low, int mid, int high) {
// 创建临时数组存储合并后的结果
int[] tempArray = new int[high - low + 1];
int leftIndex = low;
int rightIndex = mid + 1;
int tempIndex = 0;
// 比较左右两部分的元素,按顺序填入临时数组
while (leftIndex <= mid && rightIndex <= high) {
if (arr[leftIndex] <= arr[rightIndex]) {
tempArray[tempIndex++] = arr[leftIndex++];
} else {
tempArray[tempIndex++] = arr[rightIndex++];
}
}
// 如果左边剩余,则直接添加到结果中
while (leftIndex <= mid) {
tempArray[tempIndex++] = arr[leftIndex++];
}
// 如果右边剩余,则直接添加到结果中
while (rightIndex <= high) {
tempArray[tempIndex++] = arr[rightIndex++];
}
// 将临时数组中的内容拷贝回原数组
for (int i = 0; i < tempArray.length; i++) {
arr[low + i] = tempArray[i];
}
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
printArray(arr);
mergeSort(arr, 0, arr.length - 1);
System.out.println("排序后的数组:");
printArray(arr);
}
}
代码运行结果展示:
原始数组:
64 34 25 12 22 11 90
排序后的数组:
11 12 22 25 34 64 90
public class Main {
private static final int INSERTION_SORT_THRESHOLD = 10;
public static void mergeSort(int[] arr, int low, int high) {
if (high - low < INSERTION_SORT_THRESHOLD) {
insertionSort(arr, low, high);
return;
}
if (low < high) {
int mid = (low + high) / 2;
mergeSort(arr, low, mid);
mergeSort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
}
private static void insertionSort(int[] arr, int low, int high) {
for (int i = low + 1; i <= high; i++) {
int key = arr[i];
int j = i - 1;
while (j >= low && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] tempArray = new int[high - low + 1];
int leftIndex = low;
int rightIndex = mid + 1;
int tempIndex = 0;
while (leftIndex <= mid && rightIndex <= high) {
if (arr[leftIndex] <= arr[rightIndex]) {
tempArray[tempIndex++] = arr[leftIndex++];
} else {
tempArray[tempIndex++] = arr[rightIndex++];
}
}
while (leftIndex <= mid) {
tempArray[tempIndex++] = arr[leftIndex++];
}
while (rightIndex <= high) {
tempArray[tempIndex++] = arr[rightIndex++];
}
for (int i = 0; i < tempArray.length; i++) {
arr[low + i] = tempArray[i];
}
}
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
printArray(arr);
mergeSort(arr, 0, arr.length - 1);
System.out.println("排序后的数组:");
printArray(arr);
}
}
可以自己尝试一下编写,不会的话私信博主,提供最易懂的代码。
堆排序代码展示:
public class Main {
// 堆排序方法
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--) {
// 将当前最大的元素放到数组末尾
swap(arr, 0, i);
// 调整堆大小并重建堆
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) {
swap(arr, i, largest);
heapify(arr, n, largest);
}
}
// 交换数组中的两个元素
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
printArray(arr);
heapSort(arr);
System.out.println("排序后的数组:");
printArray(arr);
}
}
代码运行结果展示:
原始数组:
64 34 25 12 22 11 90
排序后的数组:
11 12 22 25 34 64 90
参考代码:
public static void heapSortWithSentinel(int[] arr) {
int n = arr.length;
int[] extendedArr = new int[n + 1];
// 插入哨兵节点
System.arraycopy(arr, 0, extendedArr, 1, n);
// 构建最大堆
for (int i = n / 2; i >= 1; i--) {
heapifyWithSentinel(extendedArr, n, i);
}
// 逐一提取堆顶元素并重建堆
for (int i = n; i > 1; i--) {
// 将当前最大的元素放到数组末尾
swap(extendedArr, 1, i);
// 调整堆大小并重建堆
heapifyWithSentinel(extendedArr, i - 1, 1);
}
// 复制回原数组
System.arraycopy(extendedArr, 1, arr, 0, n);
}
private static void heapifyWithSentinel(int[] arr, int n, int i) {
int largest = i;
int left = 2 * i;
int right = 2 * i + 1;
if (left <= n && arr[left] > arr[largest]) {
largest = left;
}
if (right <= n && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapifyWithSentinel(arr, n, largest);
}
}
代码参考:
public static void bottomUpHeapSort(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--) {
// 将当前最大的元素放到数组末尾
swap(arr, 0, i);
// 调整堆大小并重建堆
heapify(arr, i, 0);
}
}
代码展示:
public class Main {
public static void countingSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 找到数组中的最小值和最大值
int min = arr[0];
int max = arr[0];
for (int num : arr) {
if (num < min) {
min = num;
}
if (num > max) {
max = num;
}
}
// 创建计数数组
int range = max - min + 1;
int[] countArray = new int[range];
// 统计每个元素出现的次数
for (int num : arr) {
countArray[num - min]++;
}
// 累积计数
for (int i = 1; i < range; i++) {
countArray[i] += countArray[i - 1];
}
// 构建输出数组
int[] outputArray = new int[arr.length];
for (int i = arr.length - 1; i >= 0; i--) {
int num = arr[i];
outputArray[countArray[num - min] - 1] = num;
countArray[num - min]--;
}
// 将结果复制回原数组
System.arraycopy(outputArray, 0, arr, 0, arr.length);
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
printArray(arr);
countingSort(arr);
System.out.println("排序后的数组:");
printArray(arr);
}
}
代码运行结果展示:
原始数组:
64 34 25 12 22 11 90
排序后的数组:
11 12 22 25 34 64 90
参考代码:
public class OptimizedCountingSort {
public static void optimizedCountingSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 找到数组中的最小值和最大值
int min = arr[0];
int max = arr[0];
for (int num : arr) {
if (num < min) {
min = num;
}
if (num > max) {
max = num;
}
}
// 创建计数数组
int range = max - min + 1;
int[] countArray = new int[range];
// 统计每个元素出现的次数
for (int num : arr) {
countArray[num - min]++;
}
// 累积计数
for (int i = 1; i < range; i++) {
countArray[i] += countArray[i - 1];
}
// 构建输出数组
int[] outputArray = new int[arr.length];
for (int i = arr.length - 1; i >= 0; i--) {
int num = arr[i];
outputArray[countArray[num - min] - 1] = num;
countArray[num - min]--;
}
// 将结果复制回原数组
System.arraycopy(outputArray, 0, arr, 0, arr.length);
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90, -1, -10, 0};
System.out.println("原始数组:");
printArray(arr);
optimizedCountingSort(arr);
System.out.println("排序后的数组:");
printArray(arr);
}
}
其实还可以针对动态调整一下数组大小,希望可以在评论区看到。
代码展示:
public class Main {
// 基数排序方法
public static void radixSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 找到数组中的最大值,以确定需要处理的最大位数
int max = getMax(arr);
// 对每一位进行排序
for (int exp = 1; max / exp > 0; exp *= 10) {
countingSortByDigit(arr, exp);
}
}
// 获取数组中的最大值
private static int getMax(int[] arr) {
int max = arr[0];
for (int num : arr) {
if (num > max) {
max = num;
}
}
return max;
}
// 使用计数排序按位排序
private static void countingSortByDigit(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n]; // 输出数组
int[] count = new int[10]; // 计数数组
// 统计每个数字出现的次数
for (int i = 0; i < n; i++) {
int digit = (arr[i] / exp) % 10;
count[digit]++;
}
// 累积计数
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 构建输出数组
for (int i = n - 1; i >= 0; i--) { // 从右向左遍历以保持稳定性
int digit = (arr[i] / exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
// 将结果复制回原数组
System.arraycopy(output, 0, arr, 0, n);
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
System.out.println("原始数组:");
printArray(arr);
radixSort(arr);
System.out.println("排序后的数组:");
printArray(arr);
}
}
代码运行结果展示:
原始数组:
64 34 25 12 22 11 90
排序后的数组:
11 12 22 25 34 64 90
参考代码:
public class OptimizedRadixSort {
public static void optimizedRadixSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
// 分离正数和负数
int positiveCount = 0;
int negativeCount = 0;
for (int num : arr) {
if (num >= 0) {
positiveCount++;
} else {
negativeCount++;
}
}
int[] positiveArr = new int[positiveCount];
int[] negativeArr = new int[negativeCount];
int posIndex = 0;
int negIndex = 0;
for (int num : arr) {
if (num >= 0) {
positiveArr[posIndex++] = num;
} else {
negativeArr[negIndex++] = -num; // 转换为正数
}
}
// 对正数和负数分别进行基数排序
radixSort(positiveArr);
radixSort(negativeArr);
// 合并结果
negIndex = negativeArr.length - 1;
posIndex = 0;
int index = 0;
// 先插入负数(转换回负数)
while (negIndex >= 0) {
arr[index++] = -negativeArr[negIndex--];
}
// 再插入正数
while (posIndex < positiveArr.length) {
arr[index++] = positiveArr[posIndex++];
}
}
// 基数排序方法
private static void radixSort(int[] arr) {
int max = getMax(arr);
// 对每一位进行排序
for (int exp = 1; max / exp > 0; exp *= 10) {
countingSortByDigit(arr, exp);
}
}
// 获取数组中的最大值
private static int getMax(int[] arr) {
int max = arr[0];
for (int num : arr) {
if (num > max) {
max = num;
}
}
return max;
}
// 使用计数排序按位排序
private static void countingSortByDigit(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n]; // 输出数组
int[] count = new int[10]; // 计数数组
// 统计每个数字出现的次数
for (int i = 0; i < n; i++) {
int digit = (arr[i] / exp) % 10;
count[digit]++;
}
// 累积计数
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 构建输出数组
for (int i = n - 1; i >= 0; i--) { // 从右向左遍历以保持稳定性
int digit = (arr[i] / exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
// 将结果复制回原数组
System.arraycopy(output, 0, arr, 0, n);
}
// 打印数组的方法
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
// 主方法用于测试
public static void main(String[] args) {
int[] arr = {170, 45, 75, 90, 802, 24, 2, 66, -1, -10, 0};
System.out.println("原始数组:");
printArray(arr);
optimizedRadixSort(arr);
System.out.println("排序后的数组:");
printArray(arr);
}
}