排序算法详细介绍对比及备考建议

发布于:2025-04-12 ⋅ 阅读:(31) ⋅ 点赞:(0)

本文将介绍常见排序算法,包括算法思想、代码实现(C++和Python)、时间复杂度对比及不同排序间的比较。


排序算法对比

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 稳定性 适用场景 是否原地
冒泡排序 O(n²) O(n) O(n²) O(1) 稳定 小数据或基本有序
选择排序 O(n²) O(n²) O(n²) O(1) 不稳定 小数据,交换次数少
插入排序 O(n²) O(n) O(n²) O(1) 稳定 小数据或基本有序
希尔排序 O(n log n) O(n log²n) O(n²) O(1) 不稳定 中等数据量
归并排序 O(n log n) O(n log n) O(n log n) O(n) 稳定 大数据量,稳定排序
快速排序 O(n log n) O(n log n) O(n²) O(log n) 不稳定 通用排序,平均最快
堆排序 O(n log n) O(n log n) O(n log n) O(1) 不稳定 原地排序,避免最坏情况
计数排序 O(n +k) O(n +k) O(n +k) O(k) 稳定 整数,范围小
桶排序 O(n +k) O(n +k) O(n²) O(n +k) 稳定 均匀分布数据
基数排序 O(d(n +k)) O(d(n +k)) O(d(n +k)) O(n +k) 稳定 多位数整数

算法逐一介绍

1. 冒泡排序(Bubble Sort)

  • 思想:相邻元素比较交换,将最大元素逐步“冒泡”到末尾。
  • 时间复杂度
    • 平均/最坏:O(n²)
    • 最好(已有序):O(n)
  • 空间复杂度:O(1)
  • 稳定性:稳定

C++代码

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        bool swapped = false;
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                swap(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

Python代码

def bubble_sort(arr):
    n = len(arr)
    for i in range(n-1):
        swapped = False
        for j in range(n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr

2. 选择排序(Selection Sort)

  • 思想:每次选择最小元素放到已排序序列末尾。
  • 时间复杂度
    • 平均/最好/最坏:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

C++代码

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        int min_idx = i;
        for (int j = i+1; j < n; j++) {
            if (arr[j] < arr[min_idx]) min_idx = j;
        }
        swap(arr[i], arr[min_idx]);
    }
}

Python代码

def selection_sort(arr):
    n = len(arr)
    for i in range(n-1):
        min_idx = i
        for j in range(i+1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]
    return arr

3. 插入排序(Insertion Sort)

  • 思想:逐个将元素插入到已排序序列的正确位置。
  • 时间复杂度
    • 平均/最坏:O(n²)
    • 最好(已有序):O(n)
  • 空间复杂度:O(1)
  • 稳定性:稳定

C++代码

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i], j = i-1;
        while (j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = key;
    }
}

Python代码

def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i-1
        while j >=0 and key < arr[j]:
            arr[j+1] = arr[j]
            j -=1
        arr[j+1] = key
    return arr

4. 希尔排序(Shell Sort)

  • 思想:分组插入排序,逐步缩小间隔。
  • 时间复杂度:O(n log n) ~ O(n²)(取决于间隔序列)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

C++代码

void shellSort(int arr[], int n) {
    for (int gap = n/2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int temp = arr[i], j;
            for (j = i; j >= gap && arr[j-gap] > temp; j -= gap)
                arr[j] = arr[j-gap];
            arr[j] = temp;
        }
    }
}

Python代码

def shell_sort(arr):
    n = len(arr)
    gap = n // 2
    while gap > 0:
        for i in range(gap, n):
            temp = arr[i]
            j = i
            while j >= gap and arr[j-gap] > temp:
                arr[j] = arr[j-gap]
                j -= gap
            arr[j] = temp
        gap //= 2
    return arr

5. 归并排序(Merge Sort)

  • 思想:分治法,递归排序后合并。
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(n)
  • 稳定性:稳定

C++代码

void merge(int arr[], int l, int m, int r) {
    int n1 = m - l + 1, n2 = r - m;
    int L[n1], R[n2];
    for (int i = 0; i < n1; i++) L[i] = arr[l+i];
    for (int j = 0; j < n2; j++) R[j] = arr[m+1+j];
    int i = 0, j = 0, k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) arr[k++] = L[i++];
        else arr[k++] = R[j++];
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSort(int arr[], int l, int r) {
    if (l >= r) return;
    int m = l + (r - l)/2;
    mergeSort(arr, l, m);
    mergeSort(arr, m+1, r);
    merge(arr, l, m, r);
}

Python代码

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        L, R = arr[:mid], arr[mid:]
        merge_sort(L)
        merge_sort(R)
        i = j = k = 0
        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i +=1
            else:
                arr[k] = R[j]
                j +=1
            k +=1
        while i < len(L):
            arr[k] = L[i]
            i +=1; k +=1
        while j < len(R):
            arr[k] = R[j]
            j +=1; k +=1
    return arr

6. 快速排序(Quick Sort)

  • 思想:分治法,选基准分区,递归排序。
  • 时间复杂度
    • 平均/最好:O(n log n)
    • 最坏(已有序):O(n²)
  • 空间复杂度:O(log n)(递归栈)
  • 稳定性:不稳定

C++代码

int partition(int arr[], int low, int high) {
    int pivot = arr[high], i = low-1;
    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i+1], arr[high]);
    return i+1;
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi-1);
        quickSort(arr, pi+1, high);
    }
}

Python代码

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[-1]
    left = [x for x in arr[:-1] if x <= pivot]
    right = [x for x in arr[:-1] if x > pivot]
    return quick_sort(left) + [pivot] + quick_sort(right)

7. 堆排序(Heap Sort)

  • 思想:构建最大堆,交换堆顶元素并调整堆。
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

C++代码

void heapify(int arr[], int n, int i) {
    int largest = i, l = 2*i+1, r = 2*i+2;
    if (l < n && arr[l] > arr[largest]) largest = l;
    if (r < n && arr[r] > arr[largest]) largest = r;
    if (largest != i) {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

void heapSort(int arr[], int n) {
    for (int i = n/2-1; i >=0; i--)
        heapify(arr, n, i);
    for (int i = n-1; i >=0; i--) {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

Python代码

def heapify(arr, n, i):
    largest = i
    l, r = 2*i+1, 2*i+2
    if l < n and arr[l] > arr[largest]:
        largest = l
    if r < n and arr[r] > arr[largest]:
        largest = r
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

def heap_sort(arr):
    n = len(arr)
    for i in range(n//2-1, -1, -1):
        heapify(arr, n, i)
    for i in range(n-1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)
    return arr

8. 计数排序(Counting Sort)

  • 思想:统计元素出现次数,按顺序输出。
  • 时间复杂度:O(n + k)(k为数据范围)
  • 空间复杂度:O(k)
  • 稳定性:稳定

C++代码

void countingSort(int arr[], int n, int max_val) {
    int output[n], count[max_val+1] = {0};
    for (int i = 0; i < n; i++) count[arr[i]]++;
    for (int i = 1; i <= max_val; i++) count[i] += count[i-1];
    for (int i = n-1; i >=0; i--) output[--count[arr[i]]] = arr[i];
    for (int i = 0; i < n; i++) arr[i] = output[i];
}

Python代码

def counting_sort(arr):
    max_val = max(arr)
    count = [0] * (max_val + 1)
    for num in arr:
        count[num] +=1
    i = 0
    for num in range(max_val +1):
        while count[num] >0:
            arr[i] = num
            i +=1
            count[num] -=1
    return arr

9. 桶排序(Bucket Sort)

  • 思想:分桶后分别排序,合并结果。
  • 时间复杂度
    • 平均:O(n + k)
    • 最坏:O(n²)
  • 空间复杂度:O(n + k)
  • 稳定性:稳定(取决于桶内排序算法)

C++代码

void bucketSort(float arr[], int n) {
    vector<float> buckets[n];
    for (int i = 0; i < n; i++) {
        int bi = n * arr[i];
        buckets[bi].push_back(arr[i]);
    }
    for (int i = 0; i < n; i++)
        sort(buckets[i].begin(), buckets[i].end());
    int index = 0;
    for (int i = 0; i < n; i++)
        for (float num : buckets[i])
            arr[index++] = num;
}

Python代码

def bucket_sort(arr):
    max_val, min_val = max(arr), min(arr)
    bucket_size = (max_val - min_val) / len(arr)
    buckets = [[] for _ in range(len(arr))]
    for num in arr:
        idx = int((num - min_val) // bucket_size)
        if idx == len(buckets):
            idx -=1
        buckets[idx].append(num)
    sorted_arr = []
    for bucket in buckets:
        sorted_arr.extend(sorted(bucket))
    return sorted_arr

10. 基数排序(Radix Sort)

  • 思想:按位排序,从低位到高位。
  • 时间复杂度:O(d(n + k))(d为位数)
  • 空间复杂度:O(n + k)
  • 稳定性:稳定

C++代码

void countingSortForRadix(int arr[], int n, int exp) {
    int output[n], count[10] = {0};
    for (int i = 0; i < n; i++) count[(arr[i]/exp)%10]++;
    for (int i = 1; i < 10; i++) count[i] += count[i-1];
    for (int i = n-1; i >=0; i--) {
        output[count[(arr[i]/exp)%10]-1] = arr[i];
        count[(arr[i]/exp)%10]--;
    }
    for (int i = 0; i < n; i++) arr[i] = output[i];
}

void radixSort(int arr[], int n) {
    int max_val = *max_element(arr, arr+n);
    for (int exp = 1; max_val/exp >0; exp *=10)
        countingSortForRadix(arr, n, exp);
}

Python代码

def radix_sort(arr):
    max_val = max(arr)
    exp = 1
    while max_val // exp >0:
        counting_sort_by_digit(arr, exp)
        exp *=10
    return arr

def counting_sort_by_digit(arr, exp):
    n = len(arr)
    output = [0]*n
    count = [0]*10
    for i in range(n):
        index = (arr[i] // exp) %10
        count[index] +=1
    for i in range(1,10):
        count[i] += count[i-1]
    for i in range(n-1, -1, -1):
        index = (arr[i] // exp) %10
        output[count[index]-1] = arr[i]
        count[index] -=1
    for i in range(n):
        arr[i] = output[i]

总结

  • 冒泡、选择、插入排序:适用于小规模数据。
  • 希尔排序:中等规模数据,优于插入排序。
  • 归并、快速、堆排序:大规模数据,归并稳定但需额外空间,快速排序平均最快,堆排序原地排序。
  • 计数、桶、基数排序:特定场景下高效(如整数范围小、均匀分布、多位数)。

非常好的问题!在数据结构与算法考试、面试(尤其是大厂)中,虽然排序算法很多,但常考的排序算法主要集中在下面这几种,因为它们代表了不同的思想和场景优化策略:


关于备考

✅ 必考 or 高频排序算法

排序算法 说明 / 考察点 为什么常考
快速排序 分治、原地、非稳定排序 最常用的高效通用排序,面试常要求手写
归并排序 分治、稳定排序、空间换时间 经常考在链表中排序(Leetcode经典)
堆排序 二叉堆结构、原地排序、优先队列基础 常作为Top K 问题基础
插入排序 稳定、简单,考察基本理解 代码简单,适合考察写基础排序
计数排序 线性时间排序代表 理解适用范围 + 稳定排序基础
冒泡排序 教学用、面试中考“你写一个最基础排序” 了解基本交换机制
桶/基数排序 高效但场景特定 高级笔试题中出现,用于浮点/大数据位排序

🎯 企业面试中高频考察点

  • 写一个快速排序(原地/非递归)
  • 链表使用归并排序排序 O(n log n)
  • 从数据流中找第K大(堆排序)
  • Top K 频率元素(堆 + 计数)
  • 计数排序 vs 比较类排序差别(适用场景)
  • 给定整数数组用基数排序排序

🧠 拓展建议

方向 建议练习题
📚 基础写法 快速排序、归并排序、堆排序手写
🔍 排序思想 比较类 vs 非比较类排序的适用场景
📈 排序优化 小数组用插入排序,大数组快速+归并
🧪 Leetcode #215, #912, #148, #347, #451

🎯 排序算法面试核心知识清单

✅ 高频要求手写的排序算法

算法 重点考察方向 稳定性 原地
快速排序 分治思想、原地实现、递归/非递归
归并排序 分治思想、链表排序实现
堆排序 大顶堆构建、Top K 使用
插入排序 简单直接、部分有序数组快

🔝 面试高频 Leetcode 题目(附关键词)

题目编号 名称 涉及排序类型 难度
#912 排序数组 快速/归并排序 🟠 中等
#215 数组第K大元素 快排/堆排序 🟠 中等
#347 前K高频元素 堆 + 计数排序思想 🟠 中等
#148 链表排序 归并排序 🔵 困难
#451 按频率排序 计数 + 自定义排序 🟠 中等

🧩 排序面试思维框架(你可以这样答)

🌟 经典问题:快速排序的时间复杂度?

  • 平均:O(n log n)
  • 最坏:O(n²)(已排序数组)
  • 优化:三路快排、随机pivot

🌟 高级问题:为什么说归并适合链表?

  • 不依赖随机访问
  • 可以轻松实现O(1)空间的链表归并
  • 稳定、时间复杂度始终 O(n log n)

🌟 扩展:非比较类排序的适用场景?

  • 计数排序:整数,范围小
  • 基数排序:整数、位数小(如手机号、身份证)
  • 桶排序:小数、浮点数、分布均匀

🧠 排序算法复习笔记(面试 & 考试专用)


一、排序算法分类

分类 算法 稳定性 是否原地 时间复杂度(平均) 时间复杂度(最坏) 适用场景
比较类排序 冒泡、插入、选择、归并、快速、堆 插入/冒泡/归并 ✅ 其余 ❌ 归并 ❌ 其余 ✅ O(n^2)/O(nlogn) O(n^2)/O(nlogn) 通用排序
非比较类排序 计数、桶、基数 O(n)/O(nk) O(n)/O(nk) 整数/位数小/范围有限的排序场景

二、常考排序算法精讲

1. 快速排序(Quick Sort)
  • 思想:分治 + 原地交换
  • 特点:高效,最常用,平均 O(nlogn),最坏 O(n^2)
  • 代码:C++/Python(略,见代码库)
  • 常考形式:手写实现;找第K大元素;排序 + 去重
2. 归并排序(Merge Sort)
  • 思想:分治 + 合并
  • 特点:稳定,适合链表,空间复杂度 O(n)
  • 常考形式:链表排序;大数据排序
3. 堆排序(Heap Sort)
  • 思想:最大堆构建 + 弹出堆顶
  • 特点:原地,不稳定,优先队列基础
  • 常考形式:Top K 问题,最大/最小K个数
4. 插入排序(Insertion Sort)
  • 思想:构建有序序列,将新元素插入
  • 特点:适合小数组、基本有序场景
5. 计数排序(Counting Sort)
  • 思想:统计频次 -> 回写数组
  • 特点:非比较排序,稳定,O(n+k),适合范围小的整数
6. 基数排序(Radix Sort)
  • 思想:按位排序(低位到高位)
  • 特点:适合大整数排序;与计数结合使用

三、面试高频题推荐

Leetcode 编号 题目 重点算法
912 Sort an Array 快排 / 归并
215 Kth Largest Element in Array 快排 / 堆排序
347 Top K Frequent Elements 堆 + 计数排序思想
148 Sort List 归并(链表版)
451 Sort Characters by Frequency 计数 + 自定义排序

四、应答框架 & 面试建议

1. 你知道哪些排序?哪种最好?

  • 分析场景再选:快排适合通用,归并适合链表,计数适合整数范围小

2. 快排最坏时间复杂度?怎么优化?

  • O(n^2),优化方法:随机化 pivot,三路快排

3. 为什么归并适合链表?

  • 链表不能随机访问,归并分治直接分中间节点,合并无需额外数组

4. 如何实现 Top K 问题?

  • 小顶堆维护前 K 个元素,堆排序或快速选择法

五、实战建议

  • 手写练习:快排(原地)、归并(链表)、堆构建
  • 使用库时:记得 sort() 默认用 IntroSort(快排+堆+插入)
  • 注意稳定性、是否原地、空间复杂度等细节

📌 建议每日刷 1-2 道经典题,注重思维过程而非背代码
📌 可搭配可视化工具(如 Visualgo.net)加深理解


网站公告

今日签到

点亮在社区的每一天
去签到