4

通常,STL 是为速度而构建的。但是,map 和 set 数据结构上只有 upper_bound 和 lower_bound,没有操作来检索最大 key 小于输入 key 的条目k。为什么是这样?我知道我可以简单地执行 anlower_bound并执行 a--it来检索它,但是根据数据结构,立即搜索正确的条目而不是搜索另一个然后返回一步可能更有效。

例如,std::map使用红黑树,即二叉搜索树。如果返回的upper_bound元素是大于根的最小元素,则--it必须走回根,查询 O(log n) 额外成本。

如果这是 Java,我会接受设计决策。但是,STL 是为实现最高速度而构建的,那么为什么忽略此操作呢?

澄清:我不是在谈论std::upper_bound哪个可以采用另一个比较器,而是std::mapand的方法std::set

4

2 回答 2

7

首先,要获得小于键的最大元素,您必须执行以下操作:

auto it = m.lower_bound(k);
--it;

这意味着找到不小于的第一个元素k并向后移动一个元素。如果有一个函数来执行此操作,例如 ,auto it = m.last_less_element(k);则该函数必须以完全相同的方式实现,即找到不少于第一个元素k并向后移动一次。这是因为没有办法知道没有大于但仍小于的元素,k除非您找到不小于 的第一个元素k。因此,拥有这样的功能并没有性能提升,它只是一种语法快捷方式/便利。即使这样,与查找本身的成本相比,返回一次的成本也可以忽略不计。

其次,如果您想从开头迭代到最后一个小于键的元素,那么您可以这样做:

for(auto it = m.begin(); it != m.lower_bound(k); ++it) { /* ... */ };

换句话说,lower_boundandupper_bound函数的制作方式,它们是标准迭代器范围的惯用语,这非常重要。仅在这一点上,不包含一个特殊函数是合理的,因为它会在or类last_less_element的接口中引入一个反惯用函数,并且没有性能提升。所以,它会引起痛苦,但没有收获。mapset

最后一点,二叉搜索树(如红黑树)主要用于查找操作,具有按顺序遍历的边际优势。它们不是为按顺序遍历而简化的,如果这是您想要执行的主要操作,则不应使用它们。当主要操作是查找操作时,有序映射或集合很有用,偶尔需要按顺序遍历。在相反的情况下应考虑其他替代方案(例如,排序向量、树布局à la von Emde Boas 等)。

于 2013-03-03T19:36:19.160 回答
4

因为您std::lower_bound只需反转谓词即可完全按照您的意愿行事。

于 2013-03-03T18:36:46.683 回答