使用STL算法查找集合中的所有匹配项

Using STL algorithm to find all matches in a set

本文关键字:STL 算法 查找 集合 使用      更新时间:2023-10-16

我有一个包含Song对象的集合。我想搜索所有出现的具有特定标题的歌曲。我被要求使用STL算法。

问题是,当它发现第一个事件时,它会停止搜索。

我正试图通过使用while循环并更改从哪里开始搜索来修复它,但我很难让它正常工作。

原始功能

set<Song> SongLibrary::SearchSong(string title)
{
set<Song> foundSongs;
find_if(begin(m_songs), end(m_songs),[&](Song const& p)
{
if (p.getTitle() == title)
{
foundSongs.insert(p);
}
return p.getTitle() == title; });
return foundSongs;
}

这是我试图将其修改为使用while循环

while (startSearch != m_songs.end)
{
find_if(startSearch, end(m_songs), [&](Song const& p) // wont take start search as a parameter
{
if (p.getTitle() == title)
{
startSearch == m_songs.Position(); // this Line is wrong
foundSongs.insert(p);
}
return p.getTitle() == title; });
}

尝试使用Copy_if

copy_if(m_songs.begin(), m_songs.end(), foundSongs, [&](Song const& s), s.getTitle() == title);

对于这样的情况,通常需要使用std::copy_if:

set<Song> SongLibrary::SearchSong(string title)
{
set<Song> foundSongs;
std::copy_if(begin(m_songs), end(m_songs), 
std::inserter(foundSongs, end(foundSongs)),
[&](Song const &s) { return s.getTitle() == title; });
}

[无关:我使用了与问题中相同的签名,但至少值得考虑通过常量引用而不是值传递参数。]

如果您将歌曲存储在序列容器中(例如,vectordeque),则可能会使用std::partition

auto pos = std::partition(begin(m_songs), end(m_songs), 
[&](Song const &s) { return s.getTitle() == title; });

在这种情况下,[m_songs.begin(), pos)是与请求匹配的歌曲范围,[pos, m_songs.end())是不匹配的歌曲。

还要注意,set按排序顺序存储项目。因此,如果在对集合的成员进行排序时使用getTitle()作为关键字(或者至少是主键),您可能可以更高效地执行操作——您可以使用集合的equal_range成员来查找所有具有相同标题的歌曲。这将为您提供具有对数复杂度的范围的开始和结束的迭代器。然后,您将复制该范围(具有线性复杂性),因此,如果您关心的范围仅占整个集合的一小部分,则这可能会大大提高速度。

不幸的是,在像copy_if这样的函数中,不能将std::back_inserter用于没有push_back函数和std::set函数的容器,但是您可以通过将其复制到向量中来实现这一点。

// Suppose i want to copy all occurrences of 1
set<int> v{1, 2, 1, 4,1, 6, 1, 1, 9, 10};
vector<int> newSet;
std::copy_if(v.begin(),v.end(),std::back_inserter(newSet),[](int val) {  return val == 1;});

但是你为什么要用set呢。从代码中看,Songs实例似乎不应该按排序顺序存储。

除非您需要按唯一元素的排序顺序存储Songs,否则您可以使用"vector"来实现您尝试通过进行的操作

vector<int> v{1, 2, 1, 4,1, 6, 1, 1, 9, 10};
vector<int> newSet;
std::copy_if(v.begin(),v.end(),std::back_inserter(newSet),[](int val) { return val == 1;});