【问题标题】:What is the fastest way to find the positions of the first common entry in two vectors in c++?在c ++中找到两个向量中第一个公共条目的位置的最快方法是什么?
【发布时间】:2021-06-20 03:38:33
【问题描述】:

我有两个向量 u = {32, 25, 13, 42, 55, 33}v = {18, 72, 53, 39, 13, 12, 28},我想确定它们的第一个公共条目的位置 13。对于这些示例向量,这些位置是 3 和 5。找到最快的方法是什么这些职位?我必须多次执行此操作。

【问题讨论】:

  • {10, 20, 30}{15, 30, 20} 的“第一个公共条目”是什么?
  • 向量(元素的数量)和每个元素有多大?
  • 我的向量形成一棵树,所以我相信这样的例子不会出现。所以我们可以说30和20的位置都是很好的答案。
  • 向量不会太长,它们有 50-100 个整数条目。
  • @Jarod42 然后检查第二个向量中最近的公共条目的位置是什么?

标签: c++ search vector lowest-common-ancestor


【解决方案1】:

除了“小尺寸向量”和“无重复”之外,如果您的键总是在一个小范围内(例如“没有键将永远大于 10.000 "),那么您可以利用这些额外信息来实现 O(max(N,M)) 解决方案(@Jarod 的解决方案是 O(max(N,M) * log(N+M)),@Adrian 的解决方案是 O(N *M))。

首先,建立一个足够大的素数(即一个大于最大键的素数),然后开始构建一个哈希图直到第一次发生冲突的点

std::pair<size_t, size_t> findFirstMatch(const std::vector<int>& u, const std::vector<int>& v, const int& prime) {
    std::vector<size_t> hashmap(prime, INT_MAX);    // ---> 'INT_MAX' returned if no common entries found.
    size_t smallerSz = std::min(u.size(), v.size());
    std::pair<size_t, size_t> solution = { INT_MAX, INT_MAX };
    bool noCollision = true;

    // Alternate checking, to ensure minimal testing:
    for (size_t i = 0; i < smallerSz; ++i) {
        //One step for vetor u:
        size_t& idx = hashmap[u[i] % prime];
        if (idx < INT_MAX) {    // ---> Collision!
            solution = { i, idx };
            noCollision = false;
            break;
        }
        idx = i;

        //One step for vector v:
        idx = hashmap[v[i] % prime];
        if (idx < INT_MAX) {    // ---> Collision!
            solution = { idx, i };
            noCollision = false;
            break;
        }
        idx = i;
    }

    //If no collisions so far, then the remainder of the larger vector must still be checked:
    if(noCollision){
        bool uLarger = u.size() > v.size();
        const std::vector<int>& largerVec = (uLarger) ? u : v;
        for (size_t i = smallerSz; i < largerVec.size(); ++i) {
            size_t& idx = hashmap[largerVec[i] % prime];
            if (idx < INT_MAX) {    // ---> Collision!
                if (uLarger) solution = { i, idx };
                else         solution = { idx, i };
                break;
            }
            idx = i; 
        }
    }
    return solution;
}

用法

int main()
{
    std::vector<int> u = { 32, 25, 13, 42, 55, 33 }, v = { 18, 72, 53, 39, 13, 12, 28 };
    const int prime = 211; // ---> Some suitable prime...

    std::pair<size_t, size_t> S = findFirstMatch(u, v, prime);
    std::cout << "Solution = {" << S.first << "," << S.second << "}." << std::endl;
    return 0;
}

它输出“{2, 4}”而不是“{3, 5}”,因为第一个索引是 0。随意修改它。

【讨论】:

    【解决方案2】:

    如果不是最快的,那么肯定是最简单的(假设,就像你的问题一样,你想要基于 1 的索引,所以我们可以使用 {0, 0} 作为“未找到”信号和索引的 size_t 类型):

    #include <utility>   // For std::pair
    #include <algorithm> // For std::find
    #include <vector>
    #include <iostream>
    
    std::pair<size_t, size_t> FirstCommon(std::vector<int>& a, std::vector<int>& b)
    {
        for (size_t i = 0; i < a.size(); ++i) {
            auto f = std::find(b.begin(), b.end(), a.at(i));
            if (f != b.end()) return { i + 1, f - b.begin() + 1 }; // Found a[i] in b
        }
        return { 0, 0 };
    }
    
    int main()
    {
        std::vector<int> u = { 32, 25, 13, 42, 55, 33 };
        std::vector<int> v = { 18, 72, 53, 39, 13, 12, 28 };
        auto match = FirstCommon(u, v);
        std::cout << "Position is {" << match.first << "," << match.second << "}." << std::endl;
        return 0;
    }
    

    【讨论】:

    • 注意:对于您指出的向量大小(50 - 100 个元素),循环和数据初始化所涉及的开销更复杂(但技术上更快)算法可能会超过这些算法所带来的任何性能提升。
    • 我认为 OP 期望 {1, 2, 3, 4}{4, x, x, 3} 找到元素 4 而不是元素 3。
    • @Jarod42 在对该问题的第三条评论中,似乎两者都可以接受(但不太可能发生)。
    • MikeCAT 的示例确实是一个边缘案例:索引 2/3 与 3/2,只是顺序更改。 4/1 与 2/3 更“有趣”。
    • @Jarod42 同意。我想我的函数可以被调用两次(交换参数顺序)然后调用模块可以决定哪一对是最好的。
    【解决方案3】:

    假设您没有重复,您可能会使用以下内容:

    std::pair<std::size_t, std::size_t>
    common_entry_indexes(const std::vector<int>& u, const std::vector<int>& v)
    {
        const std::size_t min_size = std::min(u.size(), v.size());
        std::map<int, std::size_t> s; // might be `std::unordered_map`
    
        for (std::size_t i = 0; i != min_size; ++i)
        {
             if (auto [it, inserted] = s.insert({u[i], i}); !inserted) { return {i, it->second}; }
             if (auto [it, inserted] = s.insert({v[i], i}); !inserted) { return {it->second, i}; }
        }
        for (std::size_t i = min_size; i != u.size(); ++i)
        {
             if (auto [it, inserted] = s.insert({u[i], i}); !inserted) { return {i, it->second}; }
        }
        for (std::size_t i = min_size; i != v.size(); ++i)
        {
             if (auto [it, inserted] = s.insert({v[i], i}); !inserted) { return {it->second, i}; }
        }
        return {-1, -1}; // Not found
    }
    

    Demo

    • 可能会通过额外检查处理重复项。
    • 复杂度应该是O(max(N, M) * log(N + M)) 和地图(平均O(max(N, M))std::unordered_map

    【讨论】:

    • 愚蠢的问题:为什么要使用NumberWithIndex 来使用set,而不是仅仅使用std::map&lt;int, std::size_t&gt;(或std::unordered_map)来避免需要自定义struct使用自定义比较器?
    • @ShadowRanger: 确实 :-)(我可能会说命名,用index 而不是second)。
    • @ShadowRanger:改为地图。
    猜你喜欢
    • 1970-01-01
    • 2015-08-07
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多