STL排序算法详解

目录

前言

[1. sort 算法](#1. sort 算法)

[1.1 算法原理](#1.1 算法原理)

[1.2 函数签名](#1.2 函数签名)

[1.3 特点](#1.3 特点)

[1.4 应用举例](#1.4 应用举例)

[2. partial_sort 算法](#2. partial_sort 算法)

[2.1 算法原理](#2.1 算法原理)

[2.2 函数签名](#2.2 函数签名)

[2.3 特点](#2.3 特点)

[2.4 应用举例](#2.4 应用举例)

[3. stable_sort 算法](#3. stable_sort 算法)

[3.1 算法原理](#3.1 算法原理)

[3.2 函数签名](#3.2 函数签名)

[3.3 特点](#3.3 特点)

[3.4 应用举例](#3.4 应用举例)

[4. 三种排序算法的性能比较](#4. 三种排序算法的性能比较)

[4.1 测试环境](#4.1 测试环境)

[4.2 测试代码](#4.2 测试代码)

[4.3 测试结果](#4.3 测试结果)

[4.4 性能分析](#4.4 性能分析)

[5. 适用场景总结](#5. 适用场景总结)

[6. 代码优化建议](#6. 代码优化建议)

[7. 结论](#7. 结论)


前言

本文详细解析了STL中的三种排序算法:sort(混合排序,不稳定,O(nlogn))、partial_sort(堆排序思想,获取前N个元素,O(nlogk))和stable_sort(归并排序,稳定但需额外空间)。通过性能测试对比了它们的效率差异,并提供了具体应用场景建议:sort适用于通用排序,partial_sort适合Top-K问题,stable_sort用于需要保持元素顺序的情况。


1. sort 算法

1.1 算法原理

sort 算法是STL中最常用的排序算法,它实现了一种混合排序算法,通常是** introsort**(内省排序),结合了快速排序、堆排序和插入排序的优点:

  1. 快速排序:用于处理大部分情况,平均时间复杂度为 O(n log n)
  2. 堆排序:当快速排序的递归深度超过阈值时使用,保证最坏情况时间复杂度为 O(n log n)
  3. 插入排序:当数据量较小时使用,对于小规模数据效率更高

1.2 函数签名

cpp 复制代码
template<typename RandomIt>
void sort(RandomIt first, RandomIt last);

template<typename RandomIt, typename Compare>
void sort(RandomIt first, RandomIt last, Compare comp);

1.3 特点

  • 不稳定排序:相等元素的相对顺序可能会改变
  • 原地排序:不需要额外的存储空间
  • 时间复杂度:平均 O(n log n),最坏 O(n log n)
  • 空间复杂度:O(log n),用于递归调用栈

1.4 应用举例

例1:基本用法

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {5, 2, 9, 1, 5, 6};
    std::sort(v.begin(), v.end());
    
    std::cout << "Sorted: ";
    for (int num : v) {
        std::cout << num << " "; // 输出: 1 2 5 5 6 9
    }
    std::cout << std::endl;
    return 0;
}

例2:使用自定义比较函数

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {5, 2, 9, 1, 5, 6};
    std::sort(v.begin(), v.end(), [](int a, int b) {
        return a > b; // 降序排序
    });
    
    std::cout << "Sorted in descending order: ";
    for (int num : v) {
        std::cout << num << " "; // 输出: 9 6 5 5 2 1
    }
    std::cout << std::endl;
    return 0;
}

2. partial_sort 算法

2.1 算法原理

partial_sort 算法用于对序列的前 N 个元素进行排序,而不是对整个序列排序。它使用堆排序的思想:

  1. 首先构建一个大小为 N 的最大堆
  2. 然后遍历剩余元素,将每个元素与堆顶元素比较
  3. 如果当前元素小于堆顶元素,则替换堆顶并重新调整堆
  4. 最后对堆中的元素进行排序

2.2 函数签名

cpp 复制代码
template<typename RandomIt>
void partial_sort(RandomIt first, RandomIt middle, RandomIt last);

template<typename RandomIt, typename Compare>
void partial_sort(RandomIt first, RandomIt middle, RandomIt last, Compare comp);

2.3 特点

  • 不稳定排序:相等元素的相对顺序可能会改变
  • 原地排序:不需要额外的存储空间
  • 时间复杂度:O(n log k),其中 k 是要排序的元素个数(即 middle - first)
  • 空间复杂度:O(1),不需要额外空间

2.4 应用举例

例1:获取前N个最小元素

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {5, 2, 9, 1, 5, 6, 3, 8, 7, 4};
    
    // 排序前5个元素
    std::partial_sort(v.begin(), v.begin() + 5, v.end());
    
    std::cout << "First 5 sorted elements: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << v[i] << " "; // 输出: 1 2 3 4 5
    }
    std::cout << std::endl;
    
    std::cout << "Remaining elements: ";
    for (int i = 5; i < v.size(); ++i) {
        std::cout << v[i] << " "; // 输出: 9 6 8 7 5
    }
    std::cout << std::endl;
    return 0;
}

例2:获取前N个最大元素

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {5, 2, 9, 1, 5, 6, 3, 8, 7, 4};
    
    // 排序前5个最大元素
    std::partial_sort(v.begin(), v.begin() + 5, v.end(), std::greater<int>());
    
    std::cout << "First 5 largest elements: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << v[i] << " "; // 输出: 9 8 7 6 5
    }
    std::cout << std::endl;
    return 0;
}

3. stable_sort 算法

3.1 算法原理

stable_sort 算法是一种稳定的排序算法,它保证相等元素的相对顺序不变。通常实现为归并排序

  1. 将序列递归地分成两半
  2. 对每一半进行排序
  3. 将排序后的两半合并成一个有序序列

3.2 函数签名

cpp 复制代码
template<typename RandomIt>
void stable_sort(RandomIt first, RandomIt last);

template<typename RandomIt, typename Compare>
void stable_sort(RandomIt first, RandomIt last, Compare comp);

3.3 特点

  • 稳定排序:相等元素的相对顺序保持不变
  • 非原地排序:需要额外的存储空间
  • 时间复杂度:平均 O(n log n),最坏 O(n log n)
  • 空间复杂度:O(n),需要额外的存储空间用于归并

3.4 应用举例

例1:基本用法

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> v = {5, 2, 9, 1, 5, 6};
    std::stable_sort(v.begin(), v.end());
    
    std::cout << "Stably sorted: ";
    for (int num : v) {
        std::cout << num << " "; // 输出: 1 2 5 5 6 9
    }
    std::cout << std::endl;
    return 0;
}

例2:稳定排序的重要性

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

struct Person {
    std::string name;
    int age;
    
    Person(const std::string& n, int a) : name(n), age(a) {}
};

int main() {
    std::vector<Person> people = {
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 25},
        {"David", 30}
    };
    
    // 先按年龄排序
    std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
        return a.age < b.age;
    });
    
    std::cout << "After sort (unstable):" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << ")" << std::endl;
    }
    
    // 重新初始化
    people = {
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 25},
        {"David", 30}
    };
    
    // 用stable_sort按年龄排序
    std::stable_sort(people.begin(), people.end(), [](const Person& a, const Person& b) {
        return a.age < b.age;
    });
    
    std::cout << "\nAfter stable_sort:" << std::endl;
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << ")" << std::endl;
    }
    
    return 0;
}

4. 三种排序算法的性能比较

4.1 测试环境

  • 硬件:Intel Core i5 处理器,8GB 内存
  • 软件:Visual Studio 2022,C++17
  • 数据规模:100,000 个随机整数

4.2 测试代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>

void test_sort() {
    const int size = 100000;
    std::vector<int> v(size);
    
    // 生成随机数据
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist(1, 1000000);
    
    for (int i = 0; i < size; ++i) {
        v[i] = dist(gen);
    }
    
    // 测试 sort
    std::vector<int> v1 = v;
    auto start = std::chrono::high_resolution_clock::now();
    std::sort(v1.begin(), v1.end());
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "sort: " << duration.count() << " ms" << std::endl;
    
    // 测试 partial_sort (前50000个元素)
    std::vector<int> v2 = v;
    start = std::chrono::high_resolution_clock::now();
    std::partial_sort(v2.begin(), v2.begin() + 50000, v2.end());
    end = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "partial_sort: " << duration.count() << " ms" << std::endl;
    
    // 测试 stable_sort
    std::vector<int> v3 = v;
    start = std::chrono::high_resolution_clock::now();
    std::stable_sort(v3.begin(), v3.end());
    end = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "stable_sort: " << duration.count() << " ms" << std::endl;
}

int main() {
    std::cout << "Testing sorting algorithms with 100,000 elements..." << std::endl;
    test_sort();
    return 0;
}

4.3 测试结果

排序算法 平均运行时间 (ms) 特点
sort 15-25 不稳定,原地排序,平均 O(n log n)
partial_sort (前50%) 10-18 不稳定,原地排序,O(n log k)
stable_sort 20-30 稳定,需要额外空间,O(n log n)

4.4 性能分析

  1. sort

    • 最快的通用排序算法
    • 适用于需要完整排序的场景
    • 空间开销小
  2. partial_sort

    • 当只需要前 N 个元素有序时,比完整排序更快
    • 特别适用于 Top-K 问题
    • 时间复杂度与 K 有关,K 越小,性能越好
  3. stable_sort

    • 当需要保持相等元素的相对顺序时使用
    • 由于需要额外的存储空间,通常比 sort 慢
    • 适用于对包含多个字段的对象进行排序的场景

5. 适用场景总结

算法 适用场景 不适用场景
sort 大多数排序场景,对稳定性无要求 需要保持元素相对顺序的场景
partial_sort Top-K 问题,只需要部分元素有序 需要完整排序的场景
stable_sort 需要保持相等元素相对顺序的场景 对空间使用有严格限制的场景

6. 代码优化建议

  1. 选择合适的排序算法

    • 如果只需要前 N 个元素有序,使用 partial_sort
    • 如果需要保持元素相对顺序,使用 stable_sort
    • 其他情况,使用 sort
  2. 优化比较函数

    • 尽量使用简单的比较函数
    • 对于复杂对象,考虑使用成员函数或 lambda 表达式
  3. 数据预处理

    • 对于大规模数据,可以考虑先进行数据清洗或预处理
    • 对于几乎有序的数据,stable_sort 可能表现更好
  4. 内存管理

    • 对于 stable_sort,确保有足够的内存空间
    • 对于大规模数据,可以考虑分块处理

7. 结论

STL 提供了多种排序算法,每种算法都有其特定的适用场景:

  • sort 是最通用、最高效的排序算法,适用于大多数场景
  • partial_sort 适用于只需要部分元素有序的场景,如 Top-K 问题
  • stable_sort 适用于需要保持相等元素相对顺序的场景

选择合适的排序算法可以显著提高程序的性能和正确性,根据具体的应用场景和需求选择最合适的排序方法是每个 C++ 开发者应该掌握的技能。

相关推荐
美狐美颜sdk2 小时前
美颜SDK是什么?直播/短视频美颜SDK技术详解
人工智能·算法·美颜sdk·直播美颜sdk·美颜api
DowneyJoy2 小时前
【Unity3D补充知识点】常用数据结构分析-集合(List<T>)
数据结构·unity·c#·list
华农DrLai2 小时前
什么是远程监督?怎么自动生成训练数据?
人工智能·算法·llm·prompt·知识图谱
计算机安禾2 小时前
【数据结构与算法】第16篇:串(String)的定长顺序存储与朴素模式匹配
c语言·数据结构·c++·学习·算法·visual studio code·visual studio
2401_827499992 小时前
python核心语法01-数据存储与运算
java·数据结构·python
AI科技星2 小时前
基于v≡c公设的理论优化方案
c语言·开发语言·算法·机器学习·数据挖掘
江不清丶2 小时前
垃圾收集算法深度解析:从标记-清除到分代收集的演进之路
java·jvm·算法
副露のmagic2 小时前
链表章节 leetcode 思路&实现
数据结构·leetcode·链表
Dr.F.Arthur3 小时前
我的算法学习笔记——链表篇
数据结构·笔记·学习·链表