std::ranges::sort is_sorted is_sorted_until stable_sort partial_sort stable_partition

std::ranges::sort

 C++20 引入的算法,用于对范围中的元素进行排序。它是传统 std::sort 的范围版本,支持更简洁的语法和更安全的类型检查,同时允许使用投影(Projection)和自定义比较器。

Defined in header <algorithm>

Call signature

template< std::random_access_iterator I, std::sentinel_for<I> S,

          class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>
constexpr I

    sort( I first, S last, Comp comp = {}, Proj proj = {} );
(1)(since C++20)
template< ranges::random_access_range R, class Comp = ranges::less,

          class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr ranges::borrowed_iterator_t<R>

    sort( R&& r, Comp comp = {}, Proj proj = {} );

参数说明

  • firstlast 或 r: 要排序的范围的迭代器对或范围对象(如 std::vector)。
  • comp: 自定义比较器(默认为 std::ranges::less,即升序)。
  • proj: 投影函数,用于在比较前对元素进行转换(例如按结构体的某个成员排序)。

返回值

  • 返回排序后的范围的末尾迭代器(通常可忽略,因为排序直接在原范围上进行)。

示例

示例 1:基本用法(升序排序)
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> vec = {5, 3, 1, 4, 2};

    // 默认升序排序
    std::ranges::sort(vec);

    for (int x : vec) {
        std::cout << x << " "; // 输出:1 2 3 4 5
    }
}
示例 2:降序排序
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> vec = {5, 3, 1, 4, 2};

    // 使用自定义比较器实现降序排序
    std::ranges::sort(vec, std::ranges::greater{});

    for (int x : vec) {
        std::cout << x << " "; // 输出:5 4 3 2 1
    }
}
示例 3:自定义比较器和投影

按字符串长度排序:

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

int main() {
    std::vector<std::string> words = {"apple", "banana", "cherry", "date"};

    // 按字符串长度升序排序
    std::ranges::sort(
        words,
        [](const auto& a, const auto& b) { return a.size() < b.size(); },
        [](const std::string& s) { return s.size(); } // 投影到字符串长度
    );

    for (const auto& word : words) {
        std::cout << word << " "; // 输出:date apple banana cherry
    }
}

// 输出:

date apple banana cherry

注意事项

  1. 范围要求

    • 输入范围必须是随机访问范围(如 std::vector、数组),否则编译失败。
    • 不支持单向迭代器(如 std::forward_list)。
  2. 排序稳定性

    • std::ranges::sort 是不稳定排序,相等元素的相对顺序可能改变。若需稳定排序,使用 std::ranges::stable_sort
  3. 复杂度

    • 平均时间复杂度为 O(n log n),最坏情况 O(n²)(取决于具体实现,通常为快速排序的优化版本)。
  4. 比较器规则

    • 比较器必须满足严格弱序(Strict Weak Ordering),否则行为未定义。

与传统 std::sort 的区别

  1. 语法更简洁
    • 直接传递范围对象(如 std::ranges::sort(vec)),无需迭代器对。
  2. 支持投影
    • 允许在比较前对元素进行转换(例如按结构体的某个字段排序)。
  3. 类型安全增强
    • 范围版本通过概念(Concepts)约束参数类型,减少潜在错误。

std::ranges::is_sorted

 C++20 引入的算法,用于检查一个范围是否已按指定规则排序。默认使用 ranges::less排序。

Defined in header <algorithm>

Call signature

template< std::forward_iterator I, std::sentinel_for<I> S,

          class Proj = std::identity,
          std::indirect_strict_weak_order<std::projected<I, Proj>>
              Comp = ranges::less >
constexpr bool

    is_sorted( I first, S last, Comp comp = {}, Proj proj = {} );
(1)(since C++20)
template< ranges::forward_range R, class Proj = std::identity,

          std::indirect_strict_weak_order<
              std::projected<ranges::iterator_t<R>, Proj>>
                  Comp = ranges::less >
constexpr bool

    is_sorted( R&& r, Comp comp = {}, Proj proj = {} );

参数说明

  • firstlast 或 r: 要检查的范围的迭代器对或范围对象。
  • comp: 自定义比较器(默认为 std::ranges::less,即升序)。
  • proj: 投影函数,用于在比较前对元素进行转换(例如按结构体的某个成员排序)。

返回值

  • true: 范围已按 comp 和 proj 定义的规则排序。
  • false: 范围未排序。

示例

示例 1:基本用法(检查升序排序)
#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    bool sorted = std::ranges::is_sorted(vec);

    std::cout << (sorted ? "Sorted" : "Not sorted"); // 输出:Sorted
}
示例 2:使用投影检查结构体数组

检查结构体数组是否按年龄升序排序:

#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {
        {"Alice", 20},
        {"Bob", 25},
        {"Charlie", 25},
        {"Dave", 30}  // 已按 age 升序排序
    };

    bool sorted = std::ranges::is_sorted(
        people,
        std::ranges::less{},    // 默认升序比较器
        &Person::age            // 投影到 age 成员
    );

    std::cout << "People sorted by age: " << sorted << "\n"; // 输出 1
}

输出:

People sorted by age: 1

注意事项

  1. 范围要求

    • 输入范围必须是 前向范围forward_range),例如 std::vectorstd::list 或数组。
    • 不支持单向迭代器(如 std::forward_list),因为需要多次遍历。
  2. 复杂度

    • 时间复杂度为 O(n),需要遍历整个范围。
  3. 比较器规则

    • 比较器必须满足 严格弱序(Strict Weak Ordering),否则行为未定义。
    • 默认比较器为 std::ranges::less(升序),可替换为 std::ranges::greater(降序)或自定义逻辑。
  4. 投影函数

    • 投影允许在比较前对元素进行转换(例如提取结构体的某个成员)。
    • 若未提供投影,直接比较元素本身。

常见错误

错误 1:未排序的范围
std::vector<int> data = {3, 1, 2}; 
bool sorted = std::ranges::is_sorted(data); // 返回 false
错误 2:错误使用比较器
// 范围按升序排序,但错误使用降序比较器 
std::vector<int> data = {1, 2, 3}; 
bool sorted = std::ranges::is_sorted(data, std::ranges::greater{}); // 返回 false
错误 3:未投影到正确字段
struct Item { int id; double value; };
std::vector<Item> items = {{1, 3.0}, {2, 2.0}, {3, 1.0}};

// 错误:未投影到 value,实际按 id 排序
bool sorted = std::ranges::is_sorted(items, {}, &Item::id); 

// 正确:投影到 value 并按升序检查
bool sorted_correct = std::ranges::is_sorted(items, {}, &Item::value); // 返回 false

std::ranges::is_sorted 是检查范围有序性的高效工具,尤其适合需要结合投影或自定义比较器的场景。它在处理复杂数据类型(如结构体或自定义类)时表现出色,避免了手动编写循环的繁琐。使用时需确保输入范围满足前向迭代要求,并正确实现比较器和投影逻辑。

std::ranges::is_sorted_until

 C++20 引入的算法,用于查找范围中第一个破坏排序顺序的元素。它返回一个迭代器,指向最后一个满足排序条件的元素的下一个位置。若整个范围已排序,则返回范围的末尾迭代器。

Defined in header <algorithm>

Call signature

template< std::forward_iterator I, std::sentinel_for<I> S,

          class Proj = std::identity,
          std::indirect_strict_weak_order<std::projected<I, Proj>>
              Comp = ranges::less >
constexpr I

    is_sorted_until( I first, S last, Comp comp = {}, Proj proj = {} );
(1)(since C++20)
template< std::forward_range R, class Proj = std::identity,

          std::indirect_strict_weak_order<
              std::projected<ranges::iterator_t<R>, Proj>>
                  Comp = ranges::less >
constexpr ranges::borrowed_iterator_t<R>

    is_sorted_until( R&& r, Comp comp = {}, Proj proj = {} );

参数说明

  • firstlast 或 r: 要检查的范围的迭代器对或范围对象。
  • comp: 自定义比较器(默认为 std::ranges::less,即升序)。
  • proj: 投影函数,用于在比较前对元素进行转换。

返回值

  • 返回一个迭代器,指向第一个破坏排序的位置。
  • 若整个范围已排序,返回 last

示例

示例 1:基本用法

查找第一个破坏升序的位置:

#include <algorithm>
#include <vector>
#include <ranges>
#include <iostream>

int main() {
    std::vector<int> data = {1, 3, 5, 4, 6, 7};

    auto it = std::ranges::is_sorted_until(data);

    if (it != data.end()) {
        std::cout << "Order breaks at index: " 
                  << std::distance(data.begin(), it)
                  << ", value: " << *it << "\n";
        // 输出:Order breaks at index 3, value: 4
    } else {
        std::cout << "Entire range is sorted.\n";
    }
}

输出:

Order breaks at index: 3, value: 4

注意事项

  1. 范围要求

    • 输入范围必须是前向范围(forward_range),如 std::vectorstd::list 或数组。
    • 不支持单向迭代器(如 std::forward_list)。
  2. 复杂度

    • 时间复杂度为 O(n),需要遍历范围直到找到第一个破坏顺序的元素。
  3. 与 is_sorted 的区别

    • is_sorted 返回布尔值表示整个范围是否有序。
    • is_sorted_until 返回迭代器,提供更具体的信息。

std::ranges::stable_sort

C++20 引入的算法,用于对范围进行稳定排序。稳定排序的特点是相等元素的相对顺序在排序后保持不变。

Defined in header <algorithm>

Call signature

template< std::random_access_iterator I, std::sentinel_for<I> S,

          class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>

    I stable_sort( I first, S last, Comp comp = {}, Proj proj = {} );
(1)(since C++20)
(constexpr since C++26)
template< ranges::random_access_range R, class Comp = ranges::less,

          class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
ranges::borrowed_iterator_t<R>

    stable_sort( R&& r, Comp comp = {}, Proj proj = {} );

参数说明

  • r: 要排序的随机访问范围(如 vectorarray 等)。
  • comp: 自定义比较器(默认为 std::ranges::less)。
  • proj: 投影函数,用于在比较前转换元素(默认为恒等投影 std::identity)。

返回值

返回指向排序后范围末尾的迭代器(通常是 r.end())。


示例

示例 1:自定义比较器 + 投影

对整数向量进行升序排序:

#include <algorithm>  // 包含 ranges::stable_sort
#include <vector>
#include <string>
#include <iostream>
#include <functional> // 包含 std::greater

// 定义一个包含需要排序字段的结构体
struct Student {
    std::string name;
    int age;
    int score; // 按此字段降序排序
};

int main() {
    // 初始化测试数据(包含相同分数的元素)
    std::vector<Student> students = {
        {"Alice", 20, 85},
        {"Bob",   22, 90},
        {"Cathy", 19, 90}, // 与Bob分数相同,但原顺序在后
        {"David", 21, 88},
        {"Eva",   23, 90}  // 与Cathy分数相同,原顺序最后
    };

    // 打印排序前的顺序
    std::cout << "Before stable_sort:\n";
    for (const auto& s : students) {
        std::cout << s.name << " (Score: " << s.score << ")\n";
    }

    // 使用 ranges::stable_sort + 自定义比较器 + 投影
    std::ranges::stable_sort(students, std::greater{}, &Student::score);

    // 打印排序后的结果
    std::cout << "\nAfter stable_sort:\n";
    for (const auto& s : students) {
        std::cout << s.name << " (Score: " << s.score << ")\n";
    }

    return 0;
}

输出:

Before stable_sort:
Alice (Score: 85)
Bob (Score: 90)
Cathy (Score: 90)
David (Score: 88)
Eva (Score: 90)

After stable_sort:
Bob (Score: 90)
Cathy (Score: 90)
Eva (Score: 90)
David (Score: 88)
Alice (Score: 85)

    • std::greater{}:自定义比较器,实现降序排列(比较投影后的score值)。
    • &Student::score:投影函数,将每个Student对象映射到其score成员,使排序基于score进行。
  1. 输出结果

    • 排序前,Bob、Cathy、Eva的顺序为Bob → Cathy → Eva。
    • 排序后,它们的score相同,稳定排序会保持它们的原始相对顺序,因此输出顺序仍为Bob → Cathy → Eva。
    • score相同时,原始顺序(Bob → Cathy → Eva)被保留。

std::ranges::partial_sort

是 C++20 引入的一个算法,用于对范围内的元素进行部分排序。它允许你指定一个范围和一个中间位置,使得该位置之前的元素是整个范围内最小的元素,并且这些元素是有序的,而该位置之后的元素则不一定有序。默认排序使用的是ranges::less。

Defined in header <algorithm>

Call signature

template< std::random_access_iterator I, std::sentinel_for<I> S,

          class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>
constexpr I

    partial_sort( I first, I middle, S last, Comp comp = {}, Proj proj = {} );
(1)(since C++20)
template< ranges::random_access_range R,

          class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr ranges::borrowed_iterator_t<R>
    partial_sort( R&& r, ranges::iterator_t<R> middle, Comp comp = {},

                  Proj proj = {} );

参数说明

  • r: 要排序的范围,必须是一个随机访问范围(如 std::vectorstd::array 等)。

  • middle: 一个迭代器,指向范围内的某个位置。partial_sort 会使得 [begin(r), middle) 范围内的元素是整个范围内最小的元素,并且是有序的。

  • comp: 比较函数对象,用于定义元素的顺序。默认是 std::ranges::less

  • proj: 投影函数对象,用于在比较之前对元素进行转换。默认是 std::identity

返回值

返回一个迭代器,指向排序后的范围的末尾(即 middle)。

示例 1

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>

int main() {
    std::vector<int> v = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3};

    // 对前5个元素进行部分排序
    std::ranges::partial_sort(v, v.begin() + 5);

    // 输出结果
    for (int i : v) {
        std::cout << i << " ";
    }

    return 0;
}

输出:

0 1 2 3 4 8 7 9 6 5 

在这个例子中,std::ranges::partial_sort 对 v 的前5个元素进行了排序,使得 v[0] 到 v[4] 是整个向量中最小的5个元素,并且是有序的。剩下的元素 v[5] 到 v[9] 则没有特定的顺序。

示例 2 自定义比较函数

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>

int main() {
    std::vector<int> v = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3};

    // 对前5个元素进行部分排序,按降序排列
    std::ranges::partial_sort(v, v.begin() + 5, std::ranges::greater{});

    // 输出结果
    for (int i : v) {
        std::cout << i << " ";
    }

    return 0;
}

输出:

9 8 7 6 5 4 2 1 0 3 

std::ranges::partial_sort 是一个非常有用的算法,特别适用于当你只需要部分排序结果时。它可以有效地减少排序的开销,尤其是在处理大数据集时。

std::ranges::stable_partition

C++20 引入的一个算法,用于将范围内的元素重新排列,使得满足特定条件的元素排在前面,不满足条件的元素排在后面。与 std::ranges::partition 不同的是,stable_partition 会保持元素的相对顺序。 

Defined in header <algorithm>

Call signature

template< std::bidirectional_iterator I, std::sentinel_for<I> S,

          class Proj = std::identity,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
requires std::permutable<I>
ranges::subrange<I>

    stable_partition( I first, S last, Pred pred, Proj proj = {} );
(1)(since C++20)
(constexpr since C++26)
template< ranges::bidirectional_range R, class Proj = std::identity,

          std::indirect_unary_predicate<
              std::projected<ranges::iterator_t<R>, Proj>> Pred >
requires std::permutable<ranges::iterator_t<R>>
ranges::borrowed_subrange_t<R>

    stable_partition( R&& r, Pred pred, Proj proj = {} );

参数说明

  • r: 要重新排列的范围,必须是一个前向范围(如 std::vectorstd::list 等)。

  • pred: 一个谓词函数对象,用于判断元素是否满足条件。满足条件的元素会被放在范围的前半部分。

  • proj: 投影函数对象,用于在应用谓词之前对元素进行转换。默认是 std::identity

返回值

返回一个子范围(subrange),表示不满足条件的元素的起始和结束位置。

示例 1

以下是一个简单的示例,展示如何使用 std::ranges::stable_partition 将一个向量中的偶数放在前面,奇数放在后面,并保持相对顺序。

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 将偶数放在前面,奇数放在后面,并保持相对顺序
    auto result = std::ranges::stable_partition(v, [](int i) { return i % 2 == 0; });

    // 输出结果
    std::cout << "Partitioned vector: ";
    for (int i : v) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    // 输出不满足条件的元素范围
    std::cout << "Unpartitioned elements: ";
    for (auto it = result.begin(); it != result.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";

    return 0;
}

 输出:

Partitioned vector: 2 4 6 8 10 1 3 5 7 9 
Unpartitioned elements: 1 3 5 7 9 

示例 2 自定义投影函数

你可以提供一个自定义的投影函数来对元素进行转换,然后再应用谓词。例如,假设我们有一个结构体,我们想根据结构体的某个成员进行分区:

#include <iostream>
#include <vector>
#include <algorithm>
#include <ranges>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {
        {"Alice", 25},
        {"Bob", 17},
        {"Charlie", 30},
        {"David", 16},
        {"Eve", 22}
    };

    // 将成年人(age >= 18)放在前面,未成年人放在后面,并保持相对顺序
    auto result = std::ranges::stable_partition(people, [](const Person& p) { return p.age >= 18; }, &Person::age);

    // 输出结果
    std::cout << "Partitioned people: \n";
    for (const auto& p : people) {
        std::cout << p.name << " (" << p.age << ")\n";
    }

    // 输出不满足条件的元素范围
    std::cout << "Unpartitioned people: \n";
    for (auto it = result.begin(); it != result.end(); ++it) {
        std::cout << it->name << " (" << it->age << ")\n";
    }

    return 0;
}

输出:

Partitioned people: 
Alice (25)
Charlie (30)
Eve (22)
Bob (17)
David (16)
Unpartitioned people: 
Bob (17)
David (16)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值