C++20如何实现基于自定义序的惰性排序范围词典比较
问题解答
关于std::ranges::sort的可行性
std::ranges::sort是立即求值的算法,调用时就会完成全量排序,不存在惰性求值的可能。同时它要求输入范围是可变的随机访问范围,只读视图无法直接传入,因此直接调用完全不符合你的需求。
适配你的场景的最优实现
你给出的场景有非常适合优化的特性:每个v[i]都已经是非递增排序,你需要的S序本质是多个有序序列的降序多路归并,完全不需要全量排序,仅需惰性取当前最大元素即可,刚好匹配你“找到第一个差异就终止”的需求。
核心逻辑说明
你定义的S序优先级为:
- 优先比较数值大小,数值更大的排前面
- 数值相等时,子数组索引
i更小的排前面
而每个v[i]本身就是非递增的,因此每个子数组的元素天然符合S序的顺序,你只需要每次从所有非空的子数组头部取出符合S序最大的元素即可,完全不需要提前处理所有元素。
简易实现示例
你甚至不需要实现完整的视图适配器,直接写一个惰性比较函数即可满足需求:
#include <vector> #include <array> #include <queue> #include <tuple> template<size_t N> bool compare_by_S(const std::array<std::vector<unsigned int>, N>& left, const std::array<std::vector<unsigned int>, N>& right) { // 堆元素定义:(当前数值, 子数组索引i, 当前元素在子数组的下标j) using HeapElem = std::tuple<unsigned int, size_t, size_t>; // 大顶堆比较器,符合S序规则 constexpr auto heap_comp = [](const HeapElem& a, const HeapElem& b) { if (std::get<0>(a) != std::get<0>(b)) { return std::get<0>(a) < std::get<0>(b); // 数值大的在前 } return std::get<1>(a) > std::get<1>(b); // 数值相等时i小的在前 }; std::priority_queue<HeapElem, std::vector<HeapElem>, decltype(heap_comp)> left_heap(heap_comp), right_heap(heap_comp); // 初始化两个堆,放入所有非空子数组的第一个元素 for (size_t i = 0; i < N; ++i) { if (!left[i].empty()) left_heap.emplace(left[i][0], i, 0); if (!right[i].empty()) right_heap.emplace(right[i][0], i, 0); } while (!left_heap.empty() && !right_heap.empty()) { auto [l_val, l_i, l_j] = left_heap.top(); auto [r_val, r_i, r_j] = right_heap.top(); // 第一个差异直接返回结果 if (l_val != r_val) return l_val < r_val; if (l_i != r_i) return l_i > r_i; // 数值相等时i小的更大,所以l_i>r_i说明left更小 // 相等则弹出当前元素,放入子数组下一个元素 left_heap.pop(); right_heap.pop(); if (l_j + 1 < left[l_i].size()) left_heap.emplace(left[l_i][l_j+1], l_i, l_j+1); if (r_j + 1 < right[r_i].size()) right_heap.emplace(right[r_i][r_j+1], r_i, r_j+1); } // 一个空一个非空,空的更小 return left_heap.empty() && !right_heap.empty(); }
调用该函数即可得到你需要的比较结果,比如你给出的示例中:
compare_by_S(v,w)返回true,即w > vcompare_by_S(w,z)返回true,即z > w
完全符合你的预期,且仅需比较到第一个差异就终止,不需要处理后续元素。
通用惰性排序视图的实现思路
如果不依赖你场景的预排序特性,要实现通用的lazily_sort视图,核心逻辑是内部维护一个堆:
- 第一次调用
begin()时遍历输入范围的所有元素建堆,时间复杂度为O(n) - 每次迭代时弹出堆顶元素作为当前值,时间复杂度为O(logn)
- 直到堆为空时迭代结束
这种实现不需要提前完成全量排序,仅在需要时才输出下一个元素,当你只需要前k个元素时,总时间复杂度为O(n + k logn),仅当k等于元素总数时才和全排序的O(nlogn)开销一致。
标准库目前没有提供该视图的现成实现,需要自行实现符合C++20范围规范的自定义视图适配器。
内容的提问来源于stack exchange,提问作者Reimundo Heluani
相关产品推荐
相关产品推荐

