排序算法解析

在编程和数据处理中,排序就像 "整理房间"------ 把杂乱无章的数据按规则梳理有序,后续的查找、统计、分析才能高效推进。无论是电商商品按价格排序,还是后台数据按时间归档,排序算法都是底层核心能力。

一、排序的基本概念

在学具体算法前,得先明确几个关键定义 ------ 这些是判断算法优劣的 "标尺"。

1. 排序的本质

排序是将一组记录,按照某个(或某些)关键字(比如价格、成绩、时间)的大小,按递增或递减顺序排列的操作。比如把[3,1,4,2]按 "数值递增" 排序后得到[1,2,3,4]

2. 稳定性:排序的 "隐性要求"

假设待排序数据中有多个关键字相同的元素(比如两个价格都是 50 的商品),排序后如果它们的相对位置不变,就称这个算法是 "稳定的";反之则不稳定。

举个例子:原序列[5a, 3, 5b](a 和 b 代表两个不同商品,仅价格相同),稳定排序后还是[3, 5a, 5b];若变成[3, 5b, 5a],就是不稳定的。为什么在乎稳定性? 比如电商排序 ------ 先按价格排序,再按销量排序时,同价格商品的销量排序需要保留之前的相对顺序,这就需要稳定算法。

3. 内部排序 vs 外部排序

  • 内部排序:所有数据都能放进内存,排序过程只在内存中进行(比如给一个 1000 个元素的数组排序)。我们常用的七大排序算法都属于内部排序。
  • 外部排序:数据量太大,内存放不下(比如 100G 数据,内存只有 1G),需要在磁盘和内存之间频繁读写数据才能排序(归并排序是外部排序的核心算法)。

二、七大基于比较的排序算法:原理与特性

所谓 "基于比较",就是通过比较元素大小来决定位置 ------ 这是最通用的排序思路,覆盖了我们日常开发中 90% 以上的场景。下面逐个拆解核心逻辑和优缺点。

1. 插入排序:从 "玩扑克牌" 学起

插入排序的核心是 "逐个插入有序序列",分两种:直接插入排序和希尔排序。

(1)直接插入排序:简单但 "看数据"

基本思想 :像玩扑克牌时 "摸牌插牌"------ 摸一张牌,就把它插入到手里已排好序的牌中,直到摸完所有牌。具体步骤:假设要排序数组arr,当插入第i个元素时,arr[0]~arr[i-1]已经有序;我们用arr[i]从后往前对比arr[i-1]arr[i-2]... 找到合适位置插入,同时把后面的元素往后挪。

复制代码
public class SortAlgorithms {
    //直接插入排序
    public static void insertSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return; //数组为空或长度为1,无需排序
        }
        //从第2个元素(索引1)开始,逐个插入前面的有序序列
        for (int i = 1; i < arr.length; i++) {
            int temp = arr[i]; //保存当前待插入元素
            int j = i - 1;
            //从后往前比较已排序序列,找到待插入位置
            while (j >= 0 && arr[j] > temp) {
                arr[j + 1] = arr[j]; //元素后移
                j--;
            }
            arr[j + 1] = temp; //插入当前元素到正确位置
        }
    }
}

特性总结

  • 数据越接近有序,效率越高(最好情况时间复杂度O(n),比如已经排好序的数组);
  • 平均 / 最坏时间复杂度O(n²)(数据完全无序时,要频繁挪元素);
  • 空间复杂度O(1)(只需要临时变量存插入元素);
  • 稳定排序(相同元素不会因为插入而换位置)。
(2)希尔排序:直接插入的 "优化版"

直接插入排序在数据量大且无序时效率太低,希尔排序通过 "分组预排序" 解决这个问题,也叫 "缩小增量排序"。

基本思想 :先选一个 "增量gap"(比如gap = 数组长度/2),把数组分成gap组(比如gap=3时,第 1、4、7 个元素为一组,第 2、5、8 个为一组...),每组内部用直接插入排序;然后缩小gap(比如gap=gap/2),重复分组和排序;直到gap=1,此时数组已经接近有序,最后做一次直接插入排序即可。

复制代码
public class SortAlgorithms {
    //希尔排序(Knuth增量:gap = gap/3 + 1)
    public static void shellSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        int gap = arr.length;
        //逐步缩小增量,直到gap=1
        while (gap > 1) {
            gap = gap / 3 + 1; //符合文档中Knuth的增量取法
            //按当前gap分组,每组内执行直接插入排序
            for (int i = gap; i < arr.length; i++) {
                int temp = arr[i];
                int j = i - gap;
                while (j >= 0 && arr[j] > temp) {
                    arr[j + gap] = arr[j]; //组内元素后移
                    j -= gap;
                }
                arr[j + gap] = temp; //插入当前元素
            }
        }
    }
}

特性总结

  • 本质是对直接插入排序的优化,通过预排序让数组 "接近有序",最终提升效率;
  • 时间复杂度不好计算(依赖gap的取值),常用O(n^1.25)~O(1.6n^1.25)(Knuth 增量的统计结果);
  • 空间复杂度O(1),但不稳定(分组排序时可能打乱相同元素的相对位置)。

2. 选择排序:"挑最小的放前面"

选择排序的思路很直白:每次从剩下的元素里挑最小(或最大)的,放到已排序序列的末尾,直到选完所有元素。分直接选择排序和堆排序。

(1)直接选择排序:简单但 "低效"

基本思想 :比如排升序 ------ 第一次从arr[0]~arr[n-1]挑最小的,和arr[0]交换;第二次从arr[1]~arr[n-1]挑最小的,和arr[1]交换;以此类推,直到只剩一个元素。

复制代码
public class SortAlgorithms {
    //直接选择排序(选最小元素放起始位置)
    public static void selectSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        //每次确定第i个位置的元素(0~n-2,最后一个元素自动有序)
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i; // 记录最小元素的索引
            //遍历剩余序列,找到最小元素
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            //交换最小元素与当前起始位置元素
            if (minIndex != i) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }
}

特性总结

  • 逻辑简单,容易实现,但效率低(无论数据是否有序,都要遍历找最小元素,时间复杂度始终O(n²));
  • 空间复杂度O(1)不稳定 (比如[3, 2, 2],第一次选最小的 2 和 3 交换,会变成[2, 3, 2],两个 2 的相对位置变了)。
(2)堆排序:选择排序的 "性能王者"

直接选择排序找最小元素要遍历整个数组,堆排序用 "堆" 这种数据结构,能快速找到最大 / 最小元素,大幅提升效率。

基本思想:排升序要建 "大堆"(堆顶是最大元素),排降序建 "小堆"。步骤如下:

  1. 把无序数组建成大堆;

  2. 把堆顶(最大元素)和堆尾元素交换,此时最大元素放到了最终位置;

  3. 缩小堆的范围(排除已排好的堆尾),重新调整堆为大堆;

  4. 重复步骤 2~3,直到堆的范围为 1。

    public class SortAlgorithms {
    //堆排序(升序:建大堆)
    public static void heapSort(int[] arr) {
    if (arr == null || arr.length <= 1) {
    return;
    }
    int n = arr.length;
    //1. 构建大堆(从最后一个非叶子节点开始向下调整)
    for (int i = (n - 2) / 2; i >= 0; i--) {
    adjustMaxHeap(arr, i, n);
    }
    //2. 交换堆顶与堆尾,调整堆(重复直到堆范围为1)
    for (int i = n - 1; i > 0; i--) {
    //堆顶(最大元素)与堆尾交换
    int temp = arr[0];
    arr[0] = arr[i];
    arr[i] = temp;
    //缩小堆范围,调整剩余元素为大堆
    adjustMaxHeap(arr, 0, i);
    }
    }

    复制代码
     //向下调整为大堆(堆范围:0~heapSize-1)
     private static void adjustMaxHeap(int[] arr, int parent, int heapSize) {
         int temp = arr[parent]; //保存父节点值
         int child = 2 * parent + 1; //左孩子索引
         while (child < heapSize) {
             //若右孩子存在且比左孩子大,选择右孩子
             if (child + 1 < heapSize && arr[child + 1] > arr[child]) {
                 child++;
             }
             //父节点值大于孩子值,无需调整
             if (temp >= arr[child]) {
                 break;
             }
             //孩子值上移到父节点
             arr[parent] = arr[child];
             parent = child; //父节点指针下移
             child = 2 * parent + 1; //左孩子指针下移
         }
         arr[parent] = temp; //插入原父节点值到正确位置
     }

    }

特性总结

  • 效率高,时间复杂度始终O(nlogn)(建堆O(n),调整堆每次O(logn),共n次);
  • 空间复杂度O(1)不稳定(调整堆时可能打乱相同元素的位置);
  • 适合数据量大的场景,不需要额外空间。

3. 交换排序:"大的往后挪,小的往前挤"

交换排序通过比较元素大小,交换位置来实现排序,核心是 "冒泡排序" 和 "快速排序"。

(1)冒泡排序:最易理解的 "入门级"

基本思想:像水里的泡泡往上冒 ------ 从数组开头开始,两两比较相邻元素,若前面比后面大(升序),就交换;每一轮都把最大的元素 "冒" 到数组末尾。

复制代码
public class SortAlgorithms {
    //冒泡排序(优化版:无交换则提前退出)
    public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        int n = arr.length;
        boolean swapped; //标记本轮是否发生交换
        //每轮将最大元素冒泡到尾部,轮次为n-1(最后一个元素自动有序)
        for (int i = 0; i < n - 1; i++) {
            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;
            }
        }
    }
}

特性总结

  • 逻辑极简,新手友好(比如[3,1,2],第一轮3和1交换[1,3,2],再3和2交换[1,2,3]);
  • 最好时间复杂度O(n)(数据有序时,加个 "是否交换" 的标记,可提前退出),平均 / 最坏O(n²)
  • 空间复杂度O(1)稳定(相同元素不交换,相对位置不变)。
(2)快速排序:综合性能 "天花板"

快速排序是 Hoare 在 1962 年提出的,基于 "分治法",是实际开发中用得最多的排序算法(比如 Java 的Arrays.sort()对基本类型数组就是用快排)。

基本思想:选一个 "基准值"(比如数组第一个元素),把数组分成两部分 ------ 左边全比基准值小,右边全比基准值大;然后对左右两部分重复这个过程(递归),直到每部分只有一个元素。

**核心步骤:如何划分数组?**常用三种划分方式,本质都是找到基准值的最终位置:

  1. Hoare 版:左右指针从两端向中间移动,左指针找比基准大的,右指针找比基准小的,交换两者;直到指针相遇,该位置就是基准的最终位置。
  2. 挖坑法:先把基准值存起来(形成 "坑"),右指针找比基准小的,填入坑中(右指针变新坑);左指针找比基准大的,填入新坑(左指针变新坑);直到指针相遇,把基准值填入最后一个坑。
  3. 前后指针法prev指针指向已排序区间末尾,cur指针遍历数组;若cur找到比基准小的元素,prev后移,交换prevcur的元素;遍历结束后,交换prev和基准值,prev就是基准的最终位置。

快速排序的优化技巧

  • 三数取中法选基准:避免选到最大 / 最小元素(比如数据有序时,基准选第一个会导致划分失衡,时间复杂度退化为O(n²)),而是选 "左、中、右" 三个元素的中间值作为基准。

  • 小区间用插入排序:当递归到子数组长度很小时(比如小于 10),用直接插入排序(此时数据接近有序,插入排序效率更高)。

    public class SortAlgorithms {
    private static final int INSERT_SORT_THRESHOLD = 10; //小区间阈值(文档优化思想)

    复制代码
      //快速排序(前后指针法+三数取中+小区间插入排序)
      public static void quickSort(int[] arr) {
          if (arr == null || arr.length <= 1) {
              return;
          }
          quickSortHelper(arr, 0, arr.length - 1);
      }
    
      //递归辅助函数
      private static void quickSortHelper(int[] arr, int left, int right) {
          //小区间用直接插入排序(优化)
          if (right - left + 1 <= INSERT_SORT_THRESHOLD) {
              insertSortRange(arr, left, right);
              return;
          }
          //三数取中选基准(避免基准为极值)
          int pivotIndex = medianOfThree(arr, left, right);
          //基准值放到区间左端(方便前后指针法)
          swap(arr, pivotIndex, left);
          //划分区间,得到基准值最终位置
          int div = partitionByPrevCur(arr, left, right);
          //递归排序左区间(left~div-1)和右区间(div+1~right)
          quickSortHelper(arr, left, div - 1);
          quickSortHelper(arr, div + 1, right);
      }
    
      //三数取中:选择left、mid、right三者的中间值作为基准
      private static int medianOfThree(int[] arr, int left, int right) {
          int mid = left + (right - left) / 2;
          //比较left、mid、right,返回中间值索引
          if (arr[left] > arr[mid]) {
              swap(arr, left, mid);
          }
          if (arr[left] > arr[right]) {
              swap(arr, left, right);
          }
          if (arr[mid] > arr[right]) {
              swap(arr, mid, right);
          }
          return mid; // mid为中间值索引
      }
    
      //前后指针法划分区间
      private static int partitionByPrevCur(int[] arr, int left, int right) {
          int pivot = arr[left]; //基准值(已通过三数取中放到left)
          int prev = left; //已排序区间尾部指针
          int cur = left + 1; //遍历指针
    
          while (cur <= right) {
              //cur找到比基准小的元素,prev后移并交换
              if (arr[cur] < pivot && arr[++prev] != arr[cur]) {
                  swap(arr, prev, cur);
              }
              cur++;
          }
          //基准值放到最终位置(prev)
          swap(arr, left, prev);
          return prev; //返回基准值索引
      }
    
      //区间内的直接插入排序(用于快速排序小区间优化)
      private static void insertSortRange(int[] arr, int left, int right) {
          for (int i = left + 1; i <= right; i++) {
              int temp = arr[i];
              int j = i - 1;
              while (j >= left && arr[j] > temp) {
                  arr[j + 1] = arr[j];
                  j--;
              }
              arr[j + 1] = temp;
          }
      }
    
      //交换数组中两个元素
      private static void swap(int[] arr, int i, int j) {
          int temp = arr[i];
          arr[i] = arr[j];
          arr[j] = temp;
      }

    }

特性总结

  • 综合性能最好,平均时间复杂度O(nlogn),最坏O(n²)(数据有序且基准选得差);
  • 空间复杂度O(logn)~O(n)(递归栈的深度,优化后通常是O(logn));
  • 不稳定(划分时可能交换相同元素的位置);
  • 适合数据量大、无序的场景,是内部排序的首选。

4. 归并排序:"分而治之,合并有序"

归并排序是 "分治法" 的经典应用,核心优势是 "稳定" 且适合外部排序(海量数据)。

基本思想:先 "分" 后 "合"------

  1. 分:把数组从中间分成两部分,再把每部分继续分成两半,直到每部分只有一个元素(此时每部分都是有序的);
  2. 合:把两个有序的子数组合并成一个有序数组,比如[1,3][2,4]合并成[1,2,3,4];重复合并,直到整个数组有序。

海量数据排序的应用:如果内存只有 1G,要排序 100G 数据,归并排序是最佳选择:

  1. 把 100G 数据切成 200 个 512M 的块(内存能放下);

  2. 对每个 512M 的块单独排序(用快排、堆排都可以);

  3. 用 "二路归并" 同时处理 200 个有序块 ------ 每次从 200 个块中取最小元素,放入结果文件,直到所有块处理完。

    public class SortAlgorithms {
    //归并排序
    public static void mergeSort(int[] arr) {
    if (arr == null || arr.length <= 1) {
    return;
    }
    //临时数组(避免递归中频繁创建,降低开销)
    int[] temp = new int[arr.length];
    mergeSortHelper(arr, 0, arr.length - 1, temp);
    }

    复制代码
     //递归分割数组
     private static void mergeSortHelper(int[] arr, int left, int right, int[] temp) {
         if (left >= right) {
             return; //子数组长度为1,递归终止
         }
         int mid = left + (right - left) / 2; //中间位置(避免溢出)
         //递归分割左子数组(left~mid)和右子数组(mid+1~right)
         mergeSortHelper(arr, left, mid, temp);
         mergeSortHelper(arr, mid + 1, right, temp);
         //合并两个有序子数组
         merge(arr, left, mid, right, temp);
     }
    
     //合并left~mid和mid+1~right两个有序子数组
     private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
         int i = left; //左子数组指针
         int j = mid + 1; //右子数组指针
         int k = left; //临时数组指针
    
         //比较两个子数组元素,按序存入临时数组
         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++];
         }
    
         //临时数组元素复制回原数组(left~right区间)
         for (k = left; k <= right; k++) {
             arr[k] = temp[k];
         }
     }

    }

特性总结

  • 时间复杂度始终O(nlogn)(分的过程O(logn),合的过程O(n));
  • 空间复杂度O(n)(需要额外数组存合并后的结果);
  • 稳定(合并时相同元素按原顺序放);
  • 适合外部排序、需要稳定排序的场景,但需要额外空间。

三、非基于比较的排序:特殊场景的 "快刀"

基于比较的排序有个理论上限 ------ 时间复杂度最低是O(nlogn),但非基于比较的排序不通过比较元素大小来排序,在特定场景下更快。这里重点讲最常用的 "计数排序"。

计数排序:"鸽巢原理" 的应用

基本思想:利用 "数据范围小" 的特点,统计每个元素出现的次数,再根据次数把元素放回数组。比如给学生成绩(0-100 分)排序,范围固定,计数排序比快排还快。

步骤举例 :排序[2,5,3,0,2,3,0,3]

  1. 找数据范围:0-5,建一个计数数组count,长度为 6;

  2. 统计次数:count[0]=2(0 出现 2 次),count[1]=0count[2]=2count[3]=3count[4]=0count[5]=1

  3. 计算 "小于等于当前值的元素个数":把count转化为前缀和,count[0]=2count[1]=2count[2]=4count[3]=7count[4]=7count[5]=8

  4. 倒序填结果:从原数组末尾开始,比如最后一个元素 3,count[3]是 7,所以 3 放在结果数组的第 6 位(索引 6),然后count[3]减1;依次填完所有元素,得到有序数组。

    public class SortAlgorithms {
    //计数排序(支持非负整数,可扩展至负数)
    public static void countSort(int[] arr) {
    if (arr == null || arr.length <= 1) {
    return;
    }
    //1. 找到数组中的最大值和最小值(确定计数数组范围)
    int min = arr[0];
    int max = arr[0];
    for (int num : arr) {
    if (num < min) {
    min = num;
    }
    if (num > max) {
    max = num;
    }
    }

    复制代码
         //2. 创建计数数组,统计每个元素出现次数
         int range = max - min + 1;
         int[] countArr = new int[range];
         for (int num : arr) {
             countArr[num - min]++; // 偏移量:解决min非0的情况
         }
    
         //3. 计算计数数组的前缀和(确定元素最终位置)
         for (int i = 1; i < countArr.length; i++) {
             countArr[i] += countArr[i - 1];
         }
    
         //4. 倒序遍历原数组,填入结果数组(保证稳定性)
         int[] result = new int[arr.length];
         for (int i = arr.length - 1; i >= 0; i--) {
             int num = arr[i];
             int index = countArr[num - min] - 1; // 元素在结果数组中的索引
             result[index] = num;
             countArr[num - min]--; // 计数减1(处理重复元素)
         }
    
         //5. 结果数组复制回原数组
         System.arraycopy(result, 0, arr, 0, arr.length);
     }
    
     // 测试所有排序算法
     public static void main(String[] args) {
         // 文档中示例数组(、等)
         int[] arr = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
         
         // 测试直接插入排序
         int[] insertArr = arr.clone();
         insertSort(insertArr);
         System.out.println("直接插入排序结果:" + Arrays.toString(insertArr));
    
         // 测试希尔排序
         int[] shellArr = arr.clone();
         shellSort(shellArr);
         System.out.println("希尔排序结果:" + Arrays.toString(shellArr));
    
         // 测试直接选择排序
         int[] selectArr = arr.clone();
         selectSort(selectArr);
         System.out.println("直接选择排序结果:" + Arrays.toString(selectArr));
    
         // 测试堆排序
         int[] heapArr = arr.clone();
         heapSort(heapArr);
         System.out.println("堆排序结果:" + Arrays.toString(heapArr));
    
         // 测试冒泡排序
         int[] bubbleArr = arr.clone();
         bubbleSort(bubbleArr);
         System.out.println("冒泡排序结果:" + Arrays.toString(bubbleArr));
    
         // 测试快速排序
         int[] quickArr = arr.clone();
         quickSort(quickArr);
         System.out.println("快速排序结果:" + Arrays.toString(quickArr));
    
         // 测试归并排序
         int[] mergeArr = arr.clone();
         mergeSort(mergeArr);
         System.out.println("归并排序结果:" + Arrays.toString(mergeArr));
    
         // 测试计数排序
         int[] countArr = arr.clone();
         countSort(countArr);
         System.out.println("计数排序结果:" + Arrays.toString(countArr));
     }

    }

特性总结

  • 时间复杂度O(n + 范围)(n 是元素个数,范围是数据的最大值 - 最小值 + 1);
  • 空间复杂度O(范围)(计数数组的长度);
  • 稳定(倒序填结果能保留相同元素的相对位置);
  • 只适合数据范围小的场景(比如成绩、年龄),范围大时(比如 1-100 万)不适用。

四、一张表理清所有排序算法的性能

排序算法 最好时间复杂度 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性 核心适用场景
直接插入排序 O(n) O(n²) O(n²) O(1) 稳定 数据接近有序、小规模数据
希尔排序 O(n) O(n^1.25) O(n²) O(1) 不稳定 中等规模数据
直接选择排序 O(n²) O(n²) O(n²) O(1) 不稳定 小规模数据、不在乎效率
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定 大规模数据、省空间
冒泡排序 O(n) O(n²) O(n²) O(1) 稳定 教学演示、小规模有序数据
快速排序 O(nlogn) O(nlogn) O(n²) O(logn) 不稳定 大规模无序数据、内部排序首选
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定 外部排序、需要稳定排序
计数排序 O (n + 范围) O (n + 范围) O (n + 范围) O (范围) 稳定 数据范围小(如成绩、ID)

五、经典例题:避坑指南

  1. **快速排序基于什么思想?**答案:A(分治法)。快排的核心是 "分"(划分数组)和 "治"(递归排序子数组),属于分治法;递归是实现方式,不是核心思想。

  2. 直接插入排序插入 45 时,需要比较几次? 原序列[54,38,96,23,15,72,60,45],插入第 8 个元素 45 时,前 7 个元素已排序为[15,23,38,54,60,72,96]。从后往前比:96>45(1)、72>45(2)、60>45(3)、54>45(4)、38<45(5),共 5 次。答案:C。

  3. **哪个排序需要 O (n) 辅助空间?**答案:D(归并排序)。简单排序(插入、冒泡、选择)和堆排序都是 O (1),快排是 O (logn),归并需要额外数组存合并结果,是 O (n)。

六、总结:没有 "最好",只有 "最合适"

排序算法没有绝对的优劣,选择时要结合三个维度:

  1. 数据规模:小规模用插入 / 冒泡,大规模用快排 / 堆排 / 归并;
  2. 数据有序性:接近有序用直接插入,完全无序用快排;
  3. 稳定性需求:需要稳定排序(如多关键字排序)用归并 / 计数,不需要则用快排 / 堆排。

理解每个算法的核心逻辑和适用场景,才能在实际开发中 "对症下药"------ 这也是学习排序算法的最终目的。

相关推荐
周杰伦_Jay4 小时前
【计算机网络核心】TCP/IP模型与网页解析全流程详解
网络·网络协议·tcp/ip·计算机网络·算法·架构·1024程序员节
额呃呃4 小时前
对信号的理解
linux·运维·算法
OKkankan4 小时前
模板的进阶
开发语言·数据结构·c++·算法
拾光Ծ4 小时前
【高阶数据结构】哈希表
数据结构·c++·哈希算法·散列表
我不会插花弄玉4 小时前
c语言实现栈【由浅入深-数据结构】
c语言·数据结构
RTC老炮4 小时前
webrtc弱网-PccBitrateController类源码分析与算法原理
网络·算法·webrtc
和芯星通unicore4 小时前
扩展RTCM消息
人工智能·算法
草莓熊Lotso4 小时前
《算法闯关指南:优选算法--前缀和》--25.【模板】前缀和,26.【模板】二维前缀和
开发语言·c++·算法
hetao17338374 小时前
[CSP-S 2024] 超速检测
c++·算法