【问题标题】:Removing duplicates in two vectors去除两个向量中的重复项
【发布时间】:2018-05-15 10:38:36
【问题描述】:

我将尝试通过以下示例来解释我的问题:

vector<pair<string, string>> a = { { "A","1" }, {"B","2" },{ "C","3" },{ "D","3" },{ "E","5" } };

vector<pair<string, string>> b = { { "A","1" },{ "B","3" },{ "D","3" },{ "E","4" },{ "Z","5" } };

消除重复并将输出放入相同向量的最有效方法是什么?对的数量非常大,假设大约 100 000。

两个向量都按第一个元素排序。

vector<pair<string, string>> a = { { "B","2" },{ "C","3" },{ "E","5" } };

vector<pair<string, string>> b = { { "B","3" },{ "E","4" },{ "Z","5" } };

问题是,我需要在删除重复项后比较这个向量。 该对中的第一个元素是文件路径,第二个元素是它的校验和。 例如,如果我在第一个容器中有"B","2",而"B","3" 是第二个容器,我可以将此文件列为“已修改”。我愿意使用std::set,如果这会让这个问题变得更容易。

【问题讨论】:

  • 当你说重复时,你的意思是两个容器中存在相同的对吗?你需要one of the standard algorithms吗?
  • 在速度、内存、编码时间方面“高效”?
  • @LightnessRacesinOrbit 是的,我的意思是两个容器中存在相同的对,但我宁愿不创建新向量。我将需要这两个向量来进一步比较它们。高效 - 速度和内存
  • 如果您正在寻找容器中排序和唯一的元素,那么为什么不 Set 呢?将向量转换为 C++11 风格的集合很容易: std::set> s(std::make_move_iterator(a.begin()), std::make_move_iterator(a.end() ));
  • 获得最佳时间最佳空间的东西通常是困难的。算法是关于权衡取舍的,通常是在这两件事之间。我相信这个操作的标准算法需要一个不同的目标容器;您也许可以想出一些可以就地工作的东西,但几乎可以肯定它会更慢。

标签: c++ algorithm vector duplicates std-pair


【解决方案1】:

使用运行索引会给你 O(len(a) + len(b)) 时间复杂度和 O(1) 额外空间(给定 ab 已经排序)

void removeDuplicate(vector<pair<string, string>>& a, vector<pair<string, string>>& b) {

    //Add these two lines if there can be duplicates in a or b themselves.
    //a.erase(std::unique(a.begin(), a.end()), a.end());
    //b.erase(std::unique(b.begin(), b.end()), b.end());

    size_t i = 0;
    size_t j = 0;

    size_t p1 = 0;
    size_t p2 = 0;

    while(i < a.size() && j < b.size()) {
        if(a[i] == b[j]) {
            i++;
            j++;
        } else if (a[i] > b[j]) {
            b[p2++] = b[j++]; 
        } else if (b[j] > a[i]) {
            a[p1++] = a[i++];
        }
    }

    while(i < a.size()) {
        a[p1++] = a[i++]; 
    }

    while(j < b.size()) {
        b[p2++] = b[j++];
    }

    a.erase(a.begin()+p1, a.end());
    b.erase(b.begin()+p2, b.end());
}

【讨论】:

  • 可能使用if (p1 != i) a[p1] = std::move(a[i]); ++p1; ++i; 来避免字符串复制。然后在主循环之前尝试进行初始跳过可能值得也可能不值得,这样你就不需要那些if 分支。
  • 在迭代器上使用索引有什么特别的原因吗?
  • @aschepler 这是真的!我差点忘记了。在这种情况下,移动会比复制更有效率!
  • @Caleth 没有特别的原因:)
【解决方案2】:

我认为任何标准库算法在这里都没有直接的帮助。

我们首先检查是否应该(从两者中删除),否则我们将指向较小值的迭代器前进并继续。

for (auto ait = a.begin(), bit = b.begin(); ait != a.end() && bit != b.end();)
{
    if (*ait == *bit)
    {
        // Potenitally multiple duplicate values
        ait = a.erase(std::remove(ait, a.end(), *ait), a.end());
        bit = b.erase(std::remove(bit, b.end(), *bit), b.end());
    }
    else if (*ait < *bit) ++ait;
    else ++bit;
}

【讨论】:

    【解决方案3】:

    您可以使用 STL 库中的一些算法来帮助解决此任务。首先找到相同的元素并将它们放入临时向量中,然后从每个向量中删除这些元素,参见代码示例:

    vector<pair<string, string>> a = { { "A","1" }, {"B","2" },{ "C","3" },{ "D","3" },{ "E","5" } };
    vector<pair<string, string>> b = { { "A","1" },{ "B","3" },{ "D","3" },{ "E","4" },{ "Z","5" } };
    
    //Vector to hold same elements
    vector<pair<string, string>> same_elements {};
    
    //Fill same_elements vector
    std::for_each(a.begin(), a.end(), [&same_elements, b]( pair<string, string>& el )
                  {
                      if( find(b.begin(), b.end(), el) != b.end() )
                      {
                          same_elements.push_back(el);
                      }
                  });
    
    //Remove same elements from a and b
    std::for_each(same_elements.begin(), same_elements.end(), [&a, &b]( pair<string, string>& el_to_delete )
                  {
                      auto It_a = find(a.begin(), a.end(), el_to_delete);
                      if( It_a != a.end() )
                      {
                          a.erase(It_a);
                      }
    
                      auto It_b = find(b.begin(), b.end(), el_to_delete);
                      if( It_b != b.end() )
                      {
                          b.erase(It_b);
                      }
                  });
    

    我使用std::for_each 遍历向量的每个元素,使用std::find 查找向量中需要的元素,使用erase 向量方法通过迭代器从向量中删除相同的元素。

    【讨论】:

    • 你创建了一个新向量,但你没有使用 ab 已排序的事实(例如,你可以使用 std::binary_search,或者使用 std::set_intersection)。
    • @papagaga 实际上,当我开始写答案时,没有关于排序向量的消息,OP 后来添加了它
    猜你喜欢
    • 2020-08-03
    • 2022-01-20
    • 2021-11-22
    • 2020-04-29
    • 2017-11-03
    • 2016-12-23
    • 2011-11-29
    • 1970-01-01
    • 2015-05-03
    相关资源
    最近更新 更多