如何找到分类容器的匹配元素的索引

How to find the indices of matching elements of sorted containers?

本文关键字:元素 索引 何找 分类      更新时间:2023-10-16

我正在尝试获取一个元素匹配的一个容器的索引。两个容器均按顺序排序。是否有算法或算法组合可以将匹配的容器的元素匹配到另一个容器中?

我已经编码了一种算法,但是想知道是否以某种方式以某种方式以STL进行了编码?

我希望该算法具有与我建议的算法相当的运行复杂性,我相信它是O(min(m,n((。

#include <iterator>
#include <iostream>
template <typename It, typename Index_it>
void get_indices(It selected_it, It selected_it_end, It subitems_it, It subitems_it_end, Index_it indices_it)
{
    auto reference_it = selected_it;
    while (selected_it != selected_it_end && subitems_it != subitems_it_end) {
        if (*selected_it == *subitems_it) {
            *indices_it++ = std::distance(reference_it, selected_it);
            ++selected_it;
            ++subitems_it;
        }
        else if (*selected_it < *subitems_it) {
            ++selected_it;
        }
        else {
            ++subitems_it;
        }
    }
}
int main()
{
    int items[] = { 1, 3, 6, 8, 13, 17 };
    int subitems[] = { 3, 6, 17 };
    int indices[std::size(subitems)] = {0};
    auto selected_it = std::begin(items), it = std::begin(subitems);
    auto indices_it = std::begin(indices);
    get_indices(std::begin(items), std::end(items)
        , std::begin(subitems), std::end(subitems)
        , std::begin(indices));
    for (auto i : indices) {
        std::cout << i << ", ";
    }
    return 0;
}

我们可以使用 find_if来简化函数的实现:

template<class SourceIt, class SelectIt, class IndexIt>
void get_indicies(SourceIt begin, SourceIt end, SelectIt sbegin, SelectIt send, IndexIt dest) {
    auto scan = begin; 
    for(; sbegin != send; ++sbegin) {
        auto&& key = *sbegin; 
        scan = std::find_if(scan, end, [&](auto&& obj) { return obj >= key; }); 
        if(scan == end) break;
        for(; scan != end && *scan == key; ++scan) {
            *dest = std::distance(begin, scan); 
            ++dest; 
        }
    }
}

这不会使其短得多,但是代码现在看起来更干净。您正在扫描,直到找到与钥匙一样大或等于键的东西,然后复制即可将目的地指示,只要源匹配键。

也许我误解了这个问题。但是算法库中有一个函数。

std::set_intersection

这样做,您想要一个功能。请参阅:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
    // Input values
    std::vector<int> items{ 1,3,6,8,13,17 };
    std::vector<int> subitems{ 3,6,17 };
    // Result
    std::vector<int> result;
    // Do the work. One liner
    std::set_intersection(items.begin(),items.end(), subitems.begin(),subitems.end(),std::back_inserter(result));
    // Debug output: Show result
    std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

如果我误解了,请告诉我,我会找到另一个解决方案。

编辑:

我确实被误解了。您想要这些索引。那也许是这样的?

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using Iter = std::vector<int>::iterator;
int main()
{
    // Input values
    std::vector<int> items{ 1,3,6,8,13,17 };
    std::vector<int> subitems{ 3,6,17 };
    // Result
    std::vector<int> indices{};
    Iter it;
    // Do the work.
    std::for_each(subitems.begin(), subitems.end(), [&](int i) {it = find(items.begin(), items.end(), i); if (it != items.end()) indices.push_back(std::distance(items.begin(),it));});
    // Debug output: Show result
    std::copy(indices.begin(), indices.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}

不幸的是,一个很长的"单线"。

我需要思考更多。。。

答案是肯定的,但它会带有 C++20

您可以为此目的使用范围:

首先与您喜欢的一些谓词一起制作view

auto result = items | ranges::view::filter(predicate);

然后将iteratorbase带到原始数组,例如result.begin().base()将为您提供原始数组中resultbegin的迭代器。

#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
int main()
{
   std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
   std::vector<int> subitems = { 3, 6, 17 };
   auto predicate = [&](int& n){
       for(auto& s : subitems)
        if(n == s)
            return true;
        return false; 
        };
   auto result = items | ranges::view::filter(predicate);
   for (auto& n : result)
   {
      std::cout << n << 'n';
   }
   for(auto it = result.begin(); it != result.end(); ++it )
    std::cout << it.base() - items.begin() << ' ';
}

请参阅Godbolt

通过使用std::set_intersection,定义assignment_iterator类和assignment助手,这是可能的:

#include <iterator>
#include <iostream>
#include <algorithm>
#include <vector>
template <typename Transform>
class assignment_iterator
{
    Transform transform;
public:
    using iterator_category = std::output_iterator_tag;
    using value_type        = void;
    using difference_type   = void;
    using pointer           = void;
    using reference         = void;
    assignment_iterator(Transform transform)
        : transform(transform)
    {}
    // For some reason VC++ is assigning the iterator inside of std::copy().
    // Not needed for other compilers.
    #ifdef _MSC_VER
    assignment_iterator& operator=(assignment_iterator const& copy)
    {
        transform.~Transform();
        new (&transform) Transform(copy.transform);
        return *this;
    }
    #endif
    template <typename T>
    constexpr assignment_iterator& operator=(T& value) {
        transform(value);
        return *this;
    }
    constexpr assignment_iterator& operator* (   ) { return *this; }
    constexpr assignment_iterator& operator++(   ) { return *this; }
    constexpr assignment_iterator& operator++(int) { return *this; }
};
template <typename Transform>
assignment_iterator<Transform> assignment(Transform&& transform)
{
    return { std::forward<Transform>(transform) };
}
int main()
{
    int items[] = { 1, 3, 6, 8, 13, 17 };
    int subitems[] = { 3, 6, 17 };
    std::vector<int> indices;
    std::set_intersection(std::begin(items), std::end(items)
        , std::begin(subitems), std::end(subitems)
        , assignment([&items, &indices](int& item) {
            return indices.push_back(&item - &*std::begin(items));
        })
    );
    std::copy(indices.begin(), indices.end()
        , assignment([&indices](int& index) {
            std::cout << index;
            if (&index != &std::end(indices)[-1])
              std::cout <<  ", ";
        })
    );
    return 0;
}

demo

这是更多的代码,但是assignment可能是执行其他操作的更通用手段,目前需要特定的实现,例如back_inserterostream_iterator,因此从长远来看,代码较少(例如,就像上面的std::copy使用的其他用途一样(?

这应该基于文档始终正常工作:

元素将从第一个范围复制到目标范围。

您可以使用std :: find and std ::距离查找比赛的索引,然后将其放入容器中。

#include <vector>
#include <algorithm>
int main ()
{
   std::vector<int> v = {1,2,3,4,5,6,7};
   std::vector<int> matchIndexes;
   std::vector<int>::iterator match = std::find(v.begin(), v.end(), 5);
   int index = std::distance(v.begin(), match);
   matchIndexes.push_back(index);
   return 0;
}

要匹配多个元素,您可以以类似方式使用std ::搜索。