用于成对比较和跟踪最大/最长序列的 STL 算法
Posted
技术标签:
【中文标题】用于成对比较和跟踪最大/最长序列的 STL 算法【英文标题】:STL algorithms for pairwise comparison and tracking max/longest sequence 【发布时间】:2020-09-03 17:57:07 【问题描述】:考虑这个相当简单的算法问题:
给定一个(未排序的)数字数组,找出最长的相邻数字序列的长度。例如,如果我们有
1,4,2,3,5
,我们期望结果为 3,因为 2,3,5 给出了相邻/连续元素的最长递增序列。请注意,对于非空数组,例如 4,3,2,1,最小结果将为 1。
这行得通:
#include <algorithm>
#include <iostream>
#include <vector>
template <typename T, typename S>
T max_adjacent_length(const std::vector<S> &nums)
if (nums.size() == 0)
return 0;
T maxLength = 1;
T currLength = 1;
for (size_t i = 0; i < nums.size() - 1; i++)
if (nums[i + 1] > nums[i])
currLength++;
else
currLength = 1;
maxLength = std::max(maxLength, currLength);
return maxLength;
int main()
std::vector<double> nums = 1.2, 4.5, 3.1, 2.7, 5.3;
std::vector<int> ints = 4, 3, 2, 1;
std::cout << max_adjacent_length<int, double>(nums) << "\n"; // 2
std::cout << max_adjacent_length<int, int>(ints) << "\n"; // 1
return 0;
作为我自己的练习,我想知道是否有 STL 算法可以达到相同的效果,从而(理想情况下)避免我拥有的原始 for 循环。这样做的动机是更多地了解 STL 算法,并练习使用抽象算法来使我的代码更加通用和可重用。
这是我的想法,但它们并没有完全实现我想要的。
std::adjacent_find
实现了成对比较,可用于查找非递增对的索引,但不容易促进保持当前和最大长度并比较两者的能力。可以将这些状态变量作为我的谓词函数的一部分,但这似乎有点错误,因为理想情况下您希望您的谓词函数没有任何副作用,对吧?
std::adjacent_difference
很有趣。可以使用它来构建相邻数字之间差异的向量。然后,从第二个元素开始,根据差异是正还是负,我们可以再次跟踪看到的连续正差异的最大数量。这实际上非常接近于实现我们想要的。请参见下面的示例代码:
#include <numeric>
#include <vector>
template <typename T, typename S> T max_adjacent_length(std::vector<S> &nums)
if (nums.size() == 0)
return 0;
std::adjacent_difference(nums.begin(), nums.end(), nums.begin());
nums.erase(std::begin(nums)); // keep only differences
T maxLength = 1, currLength = 1;
for (auto n : nums)
currLength = n > 0 ? (currLength + 1) : 1;
maxLength = std::max(maxLength, currLength);
return maxLength;
这里的问题是,如果我们想计算差异,我们会丢失nums
的常量,或者我们必须牺牲空间并创建nums
的副本,这是一个禁忌,因为原始解决方案是O(1) 空间复杂度。
是否有我忽略的想法/解决方案以简洁易读的方式实现我想要的?
【问题讨论】:
你真的认为有必要将结果类型模板化吗?例如std::vector::size_type
就足够了。
不,我从size_t
开始,然后再玩模板,以防人们可能知道最大长度可能在预期范围内。无论哪种方式,它与我的问题都不是特别相关,所以我保持原样。
我认为更通用和更有用的是一个函数,它接受一对迭代器和一个比较器函子并返回一对最长序列的迭代器。
【参考方案1】:
在您的代码 sn-ps 中,您都在遍历一个范围(在第一个版本中,使用基于索引的循环,在第二个版本中使用 range-for 循环)。如果您想使用标准算法,这不是您应该编写的那种代码,标准算法与范围内的迭代器一起工作。如果您开始考虑成对的迭代器,而不是将范围视为元素的集合,那么选择正确的算法会变得更容易。
对于这个问题,这里有一个合理的写这段代码的方法:
auto max_adjacent_length = [](auto const & v)
long max = 0;
auto begin = v.begin();
while (begin != v.end())
auto next = std::is_sorted_until(begin, v.end());
max = std::max(std::distance(begin, next), max);
begin = next;
return max;
;
这是demo。
请注意,在选择合理的算法方面,您已经走上了正确的道路。这也可以通过adjacent_find
解决,只需多做一点工作。
【讨论】:
“如果您考虑成对的迭代器,那么选择正确的算法会变得更容易......”只需检查我是否理解您的意思 - 您在这里提到的一对是开始和结束迭代器向量,而不是我明确迭代元素的循环? 是的,但不仅仅是范围的开始和结束。请注意,开始不断变化,distance
使用不同的结束迭代器。它是真正代表子范围的任何对迭代器。以上是关于用于成对比较和跟踪最大/最长序列的 STL 算法的主要内容,如果未能解决你的问题,请参考以下文章