## Sorting(排序算法)

Sorting(排序算法)

排序是算法基础中的基础。PDF 里按考察频率把排序分成了三类:

  • 常考的:Merge Sort、Quick Sort(以及 Quick Select)、Bucket Sort
  • 偶尔考的:Counting Sort、Heap Sort(在 Heap 部分讲)、Pancake Sort
  • 不考的:Bubble Sort、Selection Sort、Insertion Sort、Shell Sort、Radix Sort(了解即可)

本专题重点讲前三个常考的,它们的时间复杂度都是 O(n log n),并且都蕴含了重要的算法思想。


1. 归并排序(Merge Sort)

核心思想

归并排序是**分治法(Divide and Conquer)**的教科书式实现:

  1. :将数组不断从中间二分,直到子数组只剩一个元素
  2. :将两个有序的子数组合并成一个有序数组

特点是稳定排序 ,且时间复杂度在任何情况下都是 O(n log n)。空间复杂度 O(n)(需要临时数组)。

代码模板(两份:上浮法 + 下沉法)

java 复制代码
// 主函数:递归分治
public int[] mergeSort(int[] nums) {
    if (nums == null || nums.length <= 1) return nums;
    int[] temp = new int[nums.length];               // 全局临时数组,避免反复创建
    mergeSort(nums, 0, nums.length - 1, temp);
    return nums;
}

private void mergeSort(int[] nums, int left, int right, int[] temp) {
    if (left >= right) return;                       // 递归终止:只剩一个元素
    int mid = left + (right - left) / 2;
    mergeSort(nums, left, mid, temp);                // 分:排好左边
    mergeSort(nums, mid + 1, right, temp);           // 分:排好右边
    merge(nums, left, mid, right, temp);             // 治:合并两个有序数组
}

// 合并两个有序数组 [left, mid] 和 [mid+1, right]
private void merge(int[] nums, int left, int mid, int right, int[] temp) {
    int i = left, j = mid + 1, k = left;
    // 两个指针比较,把较小的放入 temp
    while (i <= mid && j <= right) {
        temp[k++] = nums[i] <= nums[j] ? nums[i++] : nums[j++];
    }
    // 把剩余元素补上
    while (i <= mid) temp[k++] = nums[i++];
    while (j <= right) temp[k++] = nums[j++];
    // 拷回原数组
    for (int p = left; p <= right; p++) {
        nums[p] = temp[p];
    }
}

重要应用

  • 链表排序(LeetCode 148. Sort List)最适合用归并排序
  • 求逆序对(LeetCode 493)------ 在 merge 过程中统计 nums[i] > nums[j] 的情况

2. 快速排序(Quick Sort)与快速选择(Quick Select)

核心思想

快速排序也是分治法,但和归并排序思路相反:

  1. 选枢轴(pivot):选一个基准元素
  2. 分区(partition):把小于 pivot 的放左边,大于 pivot 的放右边,pivot 到了它最终的正确位置
  3. 递归:对 pivot 的左边和右边分别做同样的事

平均 O(n log n) ,排序不需要额外空间(但递归栈 O(log n))。最差 O(n²),可以用随机化 pivotshuffle 来避免。


分区模板(两种写法)

写法一:墙法(更直观)

java 复制代码
private int partition(int[] nums, int left, int right) {
    int pivot = nums[right];                // 选最右边为 pivot
    int wall = left;                        // 墙的左边全比 pivot 小
    for (int i = left; i < right; i++) {
        if (nums[i] < pivot) {
            swap(nums, i, wall);            // 把小于 pivot 的数放到墙的左边
            wall++;
        }
    }
    swap(nums, wall, right);                // pivot 放到墙的位置(最终位置)
    return wall;                            // 返回 pivot 的位置
}

写法二:双指针法

java 复制代码
private int partition2(int[] nums, int left, int right) {
    int pivot = nums[right];
    int start = left, end = right - 1;
    while (start <= end) {
        if (nums[start] <= pivot) start++;
        else if (nums[end] > pivot) end--;
        else swap(nums, start++, end--);
    }
    swap(nums, start, right);               // start 就是 pivot 最终位置
    return start;
}

完整快排代码

java 复制代码
public int[] quickSort(int[] nums) {
    quickSort(nums, 0, nums.length - 1);
    return nums;
}

private void quickSort(int[] nums, int left, int right) {
    if (left >= right) return;
    int pivotIndex = partition(nums, left, right);
    quickSort(nums, left, pivotIndex - 1);
    quickSort(nums, pivotIndex + 1, right);
}

快速选择(Quick Select)------ 找第 K 大元素

核心 :不用把数组完全排序。每次 partition 后 pivot 到了正确位置 pos,如果 pos 正好是我们要找的索引,直接返回;否则根据 postarget 的关系,只递归一边。

时间复杂度 :平均 O(n),最坏 O(n²)

例题:Kth Largest Element in an Array(215, Medium)

java 复制代码
public int findKthLargest(int[] nums, int k) {
    // 第 K 大 = 第 (n - k) 小,对应索引 nums.length - k
    return quickSelect(nums, 0, nums.length - 1, nums.length - k);
}

private int quickSelect(int[] nums, int left, int right, int targetIdx) {
    if (left == right) return nums[left];               // 区间只有一个数了
    int pos = partition(nums, left, right);
    if (pos == targetIdx) return nums[pos];
    else if (pos < targetIdx) return quickSelect(nums, pos + 1, right, targetIdx);
    else return quickSelect(nums, left, pos - 1, targetIdx);
}

3. 桶排序(Bucket Sort)

核心思想

当数据分布均匀时,桶排序是最快的。

  1. 建立若干个(bucket)
  2. 根据某种映射把元素分配到对应的桶中
  3. 每个桶内各自排序(通常用插入排序)
  4. 按顺序合并所有桶

时间复杂度 :最好 O(n + k),其中 k 是桶的数量

典型例题:Top K Frequent Elements(347, Medium)

题目:给定一个数组,返回出现频率前 K 高的元素。

思路

  1. 用 HashMap 统计每个元素的频率
  2. 用一个 List<Integer>[] 数组作为桶,索引表示频率,桶内存放该频率的所有元素
  3. 从高频到低频遍历桶,收集 K 个元素

代码

java 复制代码
public int[] topKFrequent(int[] nums, int k) {
    // 1. 统计频率
    Map<Integer, Integer> freqMap = new HashMap<>();
    for (int num : nums) {
        freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
    }
    
    // 2. 建立桶,桶索引 = 频率,最多可能有 nums.length 次出现
    List<Integer>[] bucket = new List[nums.length + 1];
    for (int key : freqMap.keySet()) {
        int freq = freqMap.get(key);
        if (bucket[freq] == null) {
            bucket[freq] = new ArrayList<>();
        }
        bucket[freq].add(key);
    }
    
    // 3. 从高频率桶向低频率桶遍历,收集结果
    List<Integer> res = new ArrayList<>();
    for (int i = bucket.length - 1; i >= 0 && res.size() < k; i--) {
        if (bucket[i] != null) {
            res.addAll(bucket[i]);
        }
    }
    
    // 转成 int[]
    return res.stream().mapToInt(i -> i).toArray(); // 或者直接 List<Integer> 也行
}

计数排序(Counting Sort)

是桶排序的特例,当元素范围确定(如 0-255)时可以直接用计数数组。后文堆排序将在 Heap 部分专门讲解。


总结:排序算法速查

排序算法 时间(平均) 时间(最坏) 空间 稳定 核心考点
Merge Sort O(n log n) O(n log n) O(n) ✅ 稳定 分治思想、合并两个有序数组、链表排序
Quick Sort O(n log n) O(n²) O(log n) ❌ 不稳定 partition 模板、随机 pivot 避免最坏
Quick Select O(n) O(n²) O(log n) 第K大/小元素、只递归一边
Bucket Sort O(n + k) O(n²) O(n + k) 取决于桶内 频率桶(Top K Frequent)
Heap Sort O(n log n) O(n log n) O(1) heapify 建堆、详细见 Heap 专题

与二分、分治的关系

  • Quick Select + Binary Search:在有序性可以帮助缩小范围时两者结合(比如在一个排序数组中找某个条件的位置)
  • Sorting 是分治法的子领域,Merge Sort 和 Quick Sort 都是典型分而治之
相关推荐
ydmy1 小时前
注意力机制(个人理解)
pytorch·python·深度学习
wuweijianlove2 小时前
算法的平均复杂度建模与性能回归分析的技术7
算法·数据挖掘·回归
子琦啊2 小时前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
iwhitney2 小时前
【次方量化】3分钟搞懂什么是量化策略
python
高洁013 小时前
大模型部署资源不足?轻量化部署解决方案
python·深度学习·机器学习·数据挖掘·transformer
阿里云大数据AI技术3 小时前
MaxFrame 视频帧智能分析:从视频到语义向量的端到端分布式处理
人工智能·python
淘矿人3 小时前
从0到1:用Claude启动你的第一个项目
开发语言·人工智能·git·python·github·php·pygame
code_pgf3 小时前
Octo 算法详解-开源通用机器人策略模型技术报告
算法·机器人·开源
嘻嘻哈哈樱桃3 小时前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划