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

Posted

技术标签:

【中文标题】使用 STL 算法查找集合中的所有匹配项【英文标题】:Using STL algorithm to find all matches in a set 【发布时间】:2017-11-06 16:39:07 【问题描述】:

我有一个包含 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::find_if,您似乎可以使用std::copy_if,因为这就是您真正在做的事情。 @tobi303 这是一个不同的问题,从那时起我一直在研究它以返回一个集合 std::find_if 在找到匹配项时停止搜索。这是设计使然,它将迭代器返回到容器中找到的第一个元素。您只是使用了错误的算法。在我看来,您将std::find_ifstd::for_each 混淆了。考虑使用 std::copy_if 代替另一条评论中提到的。 @FrançoisAndrieux 我目前正在尝试使用 copy_if,但是如何获得每首歌曲的标题? copy_if(m_songs.begin(), m_songs.end(), foundSongs, Song.getTitle() == title); 你可以使用 std::multiset 和基于标题的比较器吗? 【参考方案1】:

对于这种情况,您通常希望使用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; );


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

如果您将歌曲存储在序列容器中(例如,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 成员来找到所有具有相同标题的歌曲。这将为您提供具有对数复杂度的范围开始和结束的迭代器。然后您将复制该范围(具有线性复杂性),因此如果您关心的范围仅占整个集合的一小部分,这可能会显着提高速度。

【讨论】:

谢谢,这解决了我的问题,并给了我一些非常有用的信息。 :)【参考方案2】:

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

// Suppose i want to copy all occurrences of 1
set<int> v1, 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<int> v1, 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;);

【讨论】:

以上是关于使用 STL 算法查找集合中的所有匹配项的主要内容,如果未能解决你的问题,请参考以下文章

STL查找排序替换集合算法

为集合 A 中的所有点查找集合 B 中的最近邻的算法

Groovy集合遍历 ( 使用集合的 findAll 方法查找集合中符合匹配条件的所有元素 | 代码示例 )

Groovy集合遍历 ( 使用集合的 findAll 方法查找集合中符合匹配条件的所有元素 | 代码示例 )

最快的 c++ / stl 算法来查找成对的字符串

使用 STL 在相反类别中查找具有匹配属性的元素的算法