【问题标题】:How to remove 'all duplicated ' values?如何删除“所有重复的”值?
【发布时间】:2020-10-20 07:08:14
【问题描述】:

有很多关于删除重复值的教程,我都检查过了

喜欢

{1,2,3,4,1,2,3,4,5} -> {1,2,3,4,5}

通过使用 sort(), unique() 函数。

但如果我想删除“所有重复的”值

喜欢

{1,2,3,4,1,2,3,4,5} -> {5}

如何实现?

我已手动将原始向量分成两部分,并逐个删除第一个中的重复元素。

这是有道理的,但是如果原始向量变得很大,那么我无法手动拆分原始向量。

【问题讨论】:

  • “但是如果我想删除‘所有重复的’值” - 所以你只想保存只出现一次的值?
  • 可能遍历每个元素并检查第一个元素是否在另一个索引中找到,然后将两个索引存储在变量中然后擦除
  • @OP -- 您在问题中指定了这一点:这是有道理的,但是如果原始向量大小变得很大, -- 但您接受的答案小于最优,而其他解决方案提供更好的时间复杂度。

标签: c++ algorithm sorting vector stl


【解决方案1】:

我的建议:不要尝试就地进行。

我会对向量进行排序,然后通过,识别多次出现的元素(这应该很容易,因为它们现在是相邻的),然后将那些不出现的元素写入另一个向量。

类似copy_if的界面

OutIter copy_unique(Iterator first, Iterator last, OutIter out);

这是一个O(n log n + n) 解决方案。

就地解决方案(如@william_ 建议的)将是O(N^2)

【讨论】:

  • O(n log n + n) = O(n log n),并且可以在O(n log n) 中就地执行此操作,方法是先排序,然后以与std::remove 类似的方式删除多次出现的元素。
  • 当然可以原地做;但如果您可以复制非重复元素,则会更容易。因此我的“建议”
【解决方案2】:

这是使用 range-v3 的解决方案:

namespace rv = ranges::views;
    
ranges::sort(v);
   
auto res = v 
      | rv::group_by(std::equal_to{}) 
      | rv::filter([](auto r) { return ranges::size(r) == 1; }) 
      | rv::join
      | ranges::to<std::vector<int>>;

这是demo

这是一个使用 STL 的 O(n log(n)) 就地解决方案:

auto begin = v.begin(), end = v.end();
    
std::sort(begin, end);

while(begin != end)
  if (auto f = std::find_if(begin + 1, end, 
                 [begin](int i) { return i != *begin; });
      begin + 1 != f)  // if duplicate elements found
    end = std::move(f, end, begin);   // move them to the end
  else ++begin;
  
v.erase(end, v.end());

这是demo

【讨论】:

  • 很好,但我有一个问题:可以使用范围就地(在向量 v 中)完成吗?
  • @NoSenseEtAl 有可能,但没有想到。
  • @NoSenseEtAl 也添加了就地解决方案。
  • 谢谢你,不能投票,因为我之前已经投票了:)
【解决方案3】:

一个unordered_map应该处理这个案子:

#include <iostream> 
#include <vector>
#include <unordered_map>
using namespace std;

int main() {
    int arr[] = {1,2,3,4,1,2,3,4,5};
    int n = 9;
    unordered_map<int, int> counter;

    for(int i = 0; i < n; i++){
        counter[arr[i]]++;
    }
    
    vector<int> ans;
    
    for (auto& it: counter) {
        if(it.second == 1) ans.push_back(it.first);
    }
    
    for(int i = 0; i < ans.size(); i++) cout<<ans[i]<<" ";
    return 0;
}

它只是计算数组元素的出现次数。如果一个元素的出现次数为 1,那么它是唯一的,所以你将它存储在一个向量或数组中(地图大小)。

复杂性:O(n) 时间和空间。

您也可以使用unordered_multiset,因为它也存储元素计数。

【讨论】:

  • 前两行是一个错误的答案。
  • &lt;bits/stdc++.h&gt; 不是标准 C++ 的一部分。
【解决方案4】:
#include<iostream>
#include<vector>

using namespace std;

int main()
{
    vector<int> list = { 1,2,3,4,1,2,3,4,5 };
    vector<int> rest;
    

    for (int i = 0; i < list.size(); i++) {
        int count = 0;
        for (int j = 0; j < list.size(); j++) {
            if (list[i] == list[j]) {
                count++;
                
            }
        }
        if (count == 1) {
            rest.push_back(list[i]);
        }
    }

    for (int i = 0; i < rest.size(); i++) {
        cout << rest[i] << " ";
    }

    cout << endl;

    return 0;
}

只需使用 2 个 for 循环即可实现。

【讨论】:

  • 现在想象一下如果有 10000 个元素——你会循环 100000000 次。 OP 指定数组可以变大。
  • 我们必须使用地图,
【解决方案5】:

对不起,我基本上重复了用户丹尼尔的答案,但是因为我看到了他的代码的前两行,所以我不得不。对不起。

那么,您真正想要的,应该是问题,是:如何获取数组中恰好出现一次的值?

这个问题已经引导你找到答案。您需要计算数组中每个值的出现次数。这可以使用std::mapstd::unordered map 轻松完成。

它有一个索引运算符 [],它执行以下操作:

返回对映射到与 key 等效的键的值的引用,如果这样的键不存在,则执行插入。

详情请见here

因此,无论如何,它将返回对std::map 中的值的引用。我们将使用后自增运算符进行计数。

然后我们可以简单地将结果显示在屏幕上,从而得到一个非常紧凑的程序:

#include <iostream>
#include <map>
#include <vector>

int main() {
    // The test data
    std::vector values{ 1,2,3,4,1,2,3,4,5 };

    // We will count all instances of values
    std::map<int, size_t> counter{};
    for (const int i : values) counter[i]++;

    // Display the result
    for (const auto& [value, count] : counter) 
        if (1==count) std::cout << value << '\n';

    return 0;
}

如果你想在不同的std::vector 中得到结果,那么你可以简单地使用一个小的 for 循环和push_back 计数为 1 的值。

【讨论】:

    猜你喜欢
    • 2016-11-25
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 2011-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    相关资源
    最近更新 更多