常见的排序算法:冒泡、快排、归并、计数

冒泡排序:

时间复杂度:O(n^2)

冒泡排序就像水里的气泡一样,轻的气泡会一点一点地浮到水面。在这个算法中,我们将待排序的元素比喻成气泡,通过比较相邻元素的值,让较大的元素慢慢“浮”到数组的末端。

具体步骤如下:

  1. 比较相邻的两个元素,如果前一个比后一个大(假设我们要从小到大排序),就交换它们的位置。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后已经排序好的元素。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 外层循环控制排序轮数,每轮将最大的元素移动到正确的位置
for (int i = 1; i < arr.length; i++) {
boolean swap = false;// 设置一个标志,用于判断本轮是否发生了交换
// 内层循环进行相邻元素的比较和交换
for (int j = 0; j < arr.length - i; j++) {
// 交换大小数
if (arr[j] > arr[j + 1]) {
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
swap = true;
}
}
// 如果一轮比较下来没有发生交换,说明数组已经有序,可以退出循环
if (!swap) {
break;
}
}
}

 

快速排序:

时间复杂度:O(n log n) - O(n^2)

快速排序的思想是找一个基准值(pivot),将数组分为两部分,左边都比基准值小,右边都比基准值大。然后对这两部分再递归地进行同样的操作。

具体步骤如下:

  1. 选择一个基准值,通常是数组的中部或最后一个元素。
  2. 将数组分为两部分,小于基准值的元素放到左边,大于基准值的元素放到右边。
  3. 对左右两个子数组重复步骤1和2,直到每个子数组的元素数量不超过1。
public static void quickSort(int[] arr, int start, int end) {
if (start >= end) {
return;
}
// 使用三数取中法选择基准值,避免最坏情况的发生
int mid = (start + end) >> 1;
int pivot = Math.max(Math.min(arr[start], arr[mid]), Math.min(Math.max(arr[start], arr[mid]), arr[end]));
int l = start - 1, r = end + 1;
// 双指针遍历,找到小于和大于基准值的元素并进行交换
do {
while (arr[++l] < pivot) ;// L向右遍历,找到大于或等于基准值的元素
while (arr[--r] > pivot) ;// R向左遍历,找到小于或等于基准值的元素
if (l < r) {
// L < R:交换大小数
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
} while (l < r);// R ≤ L:说明arr已完整遍历,跳出循环
// 对子数组进行递归排序
quickSort(arr, start, l - 1);
quickSort(arr, r + 1, end);
}

 

归并排序:

时间复杂度:O(n log n)

归并排序是采用分治法的一个非常典型的应用。它的思想是将数组分成若干个小数组,对每个小数组进行排序,然后将小数组合并成较大的数组,直到最后只有一个排序完成的大数组。

具体步骤如下:

  1. 把数组分成若干个小数组,直到每个小数组只有一个元素。
  2. 将相邻的小数组合并成较大的数组,合并时对数组进行排序。
  3. 重复步骤2,直到最后只有一个排序完成的大数组。
public static int[] mergeSort(int[] arr, int start, int end) {
if (start >= end) {
return new int[]{arr[start]};
}
// 计算左子数组的末尾位置
int endL = (start + end) >> 1;
// 对两个子数组进行递归排序
int[] lArr = mergeSort(arr, start, endL);
int[] rArr = mergeSort(arr, endL + 1, end);
int k = 0, i = 0, j = 0;
int[] newArr = new int[end - start + 1];
// 当两个子数组都还有元素时,比较并放入较小的元素到新数组
while (i < lArr.length && j < rArr.length) {
newArr[k++] = lArr[i] < rArr[j] ? lArr[i++] : rArr[j++];
}
// 如果子数组还有剩余元素,将其复制到新数组
System.arraycopy(lArr, i, newArr, k, lArr.length - i);
k += lArr.length - i;
System.arraycopy(rArr, j, newArr, k, rArr.length - j);
return newArr;
}

 

计数排序:

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

计数排序是一种简单的排序算法,它的工作原理是数一数每个元素在数组中出现的次数。然后,根据这些计数,将元素按照顺序重新排列到另一个数组中。这种方法特别适用于整数排序,尤其是当整数的范围不是很大时。

public static void countingSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 找出数组中的最大值和最小值
int min = arr[0], max = arr[0];
for (int i : arr) {
max = Math.max(max,i);
min = Math.min(min,i);
}
// 计算每个元素的个数,放入计数数组
int[] ints = new int[max - min + 1];
for (int i : arr) {
ints[i - min]++;
}
// 重建排序后的数组
for (int i = 0, j = 0; i < ints.length; i++) {
while (ints[i]-- > 0) {
arr[j++] = min + i;
}
}
}

 

总结:

每种排序算法各有特点,冒泡排序简单但效率较低,快速排序效率高但需要额外的存储空间,归并排序则是效率和稳定性都比较好的排序算法。

posted @   Yfeil  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
点击右上角即可分享
微信分享提示