乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

排序与二分算法

定义与作用

排序算法将元素按指定顺序排列,二分算法在有序序列中高效查找。STL 提供了丰富的排序和二分查找功能,覆盖不同场景的性能需求。

#include <algorithm>

std::vector<int> v{3, 1, 4, 1, 5, 9, 2, 6};
std::sort(v.begin(), v.end());              // 全排序 O(n log n)
std::binary_search(v.begin(), v.end(), 5);  // 二分查找 O(log n)

核心原理

排序算法族谱

lower_bound / upper_bound / equal_range

完整示例

示例一:飞翔科技绩效排名

场景说明:大翔用排序算法为飞翔科技年度绩效排名。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>

struct Employee {
    std::string name;
    std::string dept;
    int score;
    double revenue;  // 创收(万元)
};

void printRanking(const std::string& title,
                  const std::vector<Employee>& team) {
    std::cout << title << ":\n";
    int rank = 1;
    for (const auto& e : team)
        std::cout << "  #" << rank++ << " " << e.name << " ("
                  << e.dept << ") 绩效:" << e.score
                  << " 创收:¥" << std::fixed << std::setprecision(1)
                  << e.revenue << "万\n";
}

int main() {
    std::vector<Employee> team = {
        {"大翔", "技术", 98, 1888.8},
        {"白歌", "技术", 95, 1666.6},
        {"小崔", "技术", 92, 1234.5},
        {"黄俪", "技术", 90, 888.8},
        {"李眉", "技术", 88, 666.6},
        {"孔蓝", "产品", 87, 2320.0},
        {"赵鸣", "运营", 85, 1888.8},
        {"高英", "运营", 82, 1666.6},
        {"孙鹤", "产品", 80, 520.0},
        {"杨英", "运营", 78, 1234.5},
        {"朱璐", "运营", 75, 888.8},
        {"林鸥", "设计", 72, 666.6},
    };

    // 1. 全排序:按绩效分数降序
    std::sort(team.begin(), team.end(),
        [](const Employee& a, const Employee& b) {
            return a.score > b.score;  // 降序
        });
    printRanking("=== 绩效排名(全排序)===", team);

    // 2. 稳定排序:按部门排序,同部门内保持原有顺序
    std::stable_sort(team.begin(), team.end(),
        [](const Employee& a, const Employee& b) {
            return a.dept < b.dept;
        });
    std::cout << "\n=== 部门分组(稳定排序)===\n";
    for (const auto& e : team)
        std::cout << "  " << e.dept << " | " << e.name << "\n";

    // 3. 部分排序:Top 3 创收明星
    std::partial_sort(team.begin(), team.begin() + 3, team.end(),
        [](const Employee& a, const Employee& b) {
            return a.revenue > b.revenue;
        });
    std::cout << "\n=== Top 3 创收明星 ===\n";
    for (int i = 0; i < 3; ++i)
        std::cout << "  #" << i + 1 << " " << team[i].name
                  << ": ¥" << team[i].revenue << "万\n";

    // 4. nth_element:中位数创收
    auto mid = team.begin() + team.size() / 2;
    std::nth_element(team.begin(), mid, team.end(),
        [](const Employee& a, const Employee& b) {
            return a.revenue < b.revenue;
        });
    std::cout << "\n创收中位数: ¥" << mid->revenue << "万 ("
              << mid->name << ")\n";

    // 5. 二分查找:查找创收 ¥888.8 万的员工
    std::sort(team.begin(), team.end(),
        [](const Employee& a, const Employee& b) {
            return a.revenue < b.revenue;
        });

    double target = 888.8;
    auto low = std::lower_bound(team.begin(), team.end(), target,
        [](const Employee& e, double val) { return e.revenue < val; });
    auto up  = std::upper_bound(team.begin(), team.end(), target,
        [](double val, const Employee& e) { return val < e.revenue; });

    std::cout << "\n=== 创收 ¥888.8 万的员工 ===\n";
    for (auto it = low; it != up; ++it)
        std::cout << "  " << it->name << ": ¥" << it->revenue << "万\n";

    std::cout << "(共 " << std::distance(low, up) << " 人)\n";
}

预期输出:

=== 绩效排名(全排序)===
  #1 大翔 (技术) 绩效:98 创收:¥1888.8万
  #2 白歌 (技术) 绩效:95 创收:¥1666.6万
  ...

=== 部门分组(稳定排序)===
  产品 | 孔蓝
  产品 | 孙鹤
  技术 | 大翔
  技术 | 白歌
  ...

创收中位数: ¥XXXX万 (XXX)

=== 创收 ¥888.8 万的员工 ===
  黄俪: ¥888.8万
  朱璐: ¥888.8万
(共 2 人)

逐段分析:

  • sort 底层通常是 introsort(快速排序 + 堆排序混合),O(n log n)
  • stable_sort 保持相等元素的相对顺序,通常用归并排序
  • partial_sort 只保证前 k 个有序,其余无序,适合 Top-K 场景
  • nth_element 只保证第 n 位置正确,前半部分 ≤ 第 n ≤ 后半部分
  • lower_bound / upper_bound 的组合等价于 equal_range

示例二:is_sorted 与 merge 日志合并

场景说明:李眉用合并算法整合多台服务器的有序日志。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main() {
    std::vector<int> serverA{101, 203, 305, 407};
    std::vector<int> serverB{102, 204, 306, 408};
    std::vector<int> serverC{103, 205};
    std::vector<int> merged;

    // 检查输入是否有序
    std::cout << "A 有序: " << std::is_sorted(serverA.begin(), serverA.end()) << "\n";
    std::cout << "B 有序: " << std::is_sorted(serverB.begin(), serverB.end()) << "\n";

    // 合并前预留空间
    merged.reserve(serverA.size() + serverB.size() + serverC.size());

    // 第一步:合并 A 和 B
    std::merge(serverA.begin(), serverA.end(),
               serverB.begin(), serverB.end(),
               std::back_inserter(merged));

    // 第二步:将 C 合并到结果中(inplace_merge 需要临时缓冲区)
    auto mid = merged.size();
    merged.insert(merged.end(), serverC.begin(), serverC.end());
    std::inplace_merge(merged.begin(), merged.begin() + mid, merged.end());

    std::cout << "\n合并后日志ID: ";
    for (int id : merged) std::cout << id << " ";
    std::cout << "\n共 " << merged.size() << " 条\n";
    std::cout << "合并后有序: " << std::is_sorted(merged.begin(), merged.end()) << "\n";
}

预期输出:

A 有序: true
B 有序: true

合并后日志ID: 101 102 103 203 204 205 305 306 407 408 
共 10 条
合并后有序: true

逐段分析:

  • is_sorted(C++11)检查区间是否有序
  • merge 合并两个有序区间,结果保持有序
  • inplace_merge 将两个相邻的有序子区间原地合并
  • reserve 预分配避免多次扩容

易错场景与面试考点

易错场景

1. 二分查找前未排序

std::vector<int> v{3, 1, 4, 1, 5};
// auto it = std::lower_bound(v.begin(), v.end(), 3);  // ❌ 未排序,结果不可预测
std::sort(v.begin(), v.end());  // ✅ 必须先排序
auto it = std::lower_bound(v.begin(), v.end(), 3);

2. 比较器不一致

std::sort(v.begin(), v.end(), std::greater<>());
// std::binary_search(v.begin(), v.end(), x);  // ❌ 比较器不匹配!
std::binary_search(v.begin(), v.end(), x, std::greater<>());  // ✅

3. lower_bound 的比较器参数顺序

// 自定义结构需要传入比较器,注意参数顺序
auto it = std::lower_bound(vec.begin(), vec.end(), target,
    [](const Employee& e, double val) { return e.revenue < val; });
// lower_bound: comp(*it, value) → true 时向右移动

面试考点

考点要点
sort 时间复杂度O(n log n),introsort 混合策略
stable_sort 场景需要保持相等元素原始相对顺序时
partial_sort 场景Top-K 问题,比全排序高效
nth_elementO(n) 中位数/分位数选择
lower_bound vs upper_bound第一个 ≥ vs 第一个 >
equal_range返回 pair<lower_bound, upper_bound>
sorted 前置条件所有二分算法要求输入有序
上一页
变异算法
下一页
Lambda 与算法组合