使用std :: map从数组中删除重复项

时间:2012-06-09 22:48:06

标签: c++ performance algorithm

我是直接发布我在5分钟内写在 collabedit 上的代码(包括算法算法),因此即使在效率方面完全取笑的风险我也是如此我想问一下我的同事经验丰富的堆栈溢出算法爱好者的问题;

基本上从数组中删除重复的元素。 我的方法:基本上使用 std :: map 作为我的哈希表,并且对于重复数组中的每个元素,如果尚未分配值,请将其添加到我们的新数组中。如果分配只是跳过。最后返回唯一的数组。这是我的代码,我就面试问题提出的唯一问题是我的解决方案能更有效吗?

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

using namespace std;

vector<int>uniqueArr(int arr[],int size){
    std::map<int,int>storedValues;
    vector<int>uniqueArr;
    for(int i=0;i<size;i++){
        if(storedValues[arr[i]]==0){
            uniqueArr.push_back(arr[i]);
            storedValues[arr[i]]=1;
        }
    }
    return uniqueArr;  
}

int main()
{   
    const int size=10;
    int arr[size]={1,2,2,4,2,5,6,5,7,1};
    vector<int>uniArr=uniqueArr(arr,size);
    cout<<"Result: ";
    for(int i=0;i<uniArr.size();i++) cout<<uniArr[i]<<" ";
    cout<<endl;
    return 0;
}

4 个答案:

答案 0 :(得分:4)

首先,不需要地图,集合在概念上更正确,因为您不想存储任何值,只能存储密钥。

在性能方面,使用std::unordered_set而不是std::set可能更好一点,因为前者是经过哈希处理的,并且在最佳情况下可以为您提供O(1)插入和查找,而后者是二叉搜索树,只给你O(log n)访问。

vector<int> uniqueArr(int arr[], int size)
{
    std::unordered_set<int> storedValues;
    vector<int> uniqueArr;
    for(int i=0; i<size; ++i){
        if(storedValues.insert(arr[i]).second)
            uniqueArr.push_back(arr[i]);
    return uniqueArr;  
}

但是如果您被允许更广泛地使用C ++标准库,您也可以使用std::sortstd::unique来考虑其他答案,尽管它们是 O(n log n)< / strong>(而不是上面的 ~O(n)解决方案)并破坏元素的顺序。


如果你想使用更灵活和std驱动的方法,但是复杂度很高且不破坏元素的顺序,你可以将上面的例程转换成下面的类似std的算法,即使是一个简单的面试问题有点太牵强了:

template<typename ForwardIterator>
ForwardIterator unordered_unique(ForwardIterator first, ForwardIterator last)
{
    typedef typename std::iterator_traits<ForwardIterator>::value_type value_type;
    std::unordered_set<value_type> unique;
    return std::remove_if(first, last, 
                          [&unique](const value_type &arg) mutable -> bool
                              { return !unique.insert(arg).second; });
}

然后您可以按照通常的擦除方式应用std::unique

std::vector<int> values(...);
values.erase(unordered_unique(values.begin(), values.end()), values.end());

删除唯一值而不复制矢量,无需事先对其进行排序。

答案 1 :(得分:2)

既然你是在询问面试问题,我会说你没有得到这份工作。

const int size=10;
int arr[size]={1,2,2,4,2,5,6,5,7,1};

std::sort( &arr[0], &arr[size] );
int* new_end = std::unique( &arr[0], &arr[size] );

std::copy(
    &arr[0], new_end,
  , std::ostream_iterator< int >( std::cout, " " )
);

没有临时地图,没有临时矢量,没有动态内存分配,编写了很多少代码,因此更容易编写和保存。

答案 2 :(得分:1)

#include <algorithm>
#include <vector>

int main()
{
    std::vector<int> vec({1,2,3,2,4,4,5,7,6,6});
    std::sort(vec.begin(), vec.end());
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    // vec = {1,2,3,4,5,6,7}
    return 0;
}
//works with C++11
// O(n log n)

答案 3 :(得分:1)

就地删除对速度有好处 - 就像这样(返回新的大小):

template <typename T, size_t N>
size_t keep_unique(T (&array)[N])
{
    std::unordered_set<T> found;
    for (size_t i = 0, j = 0; i < N; ++i)
        if (found.insert(array[i]).second))
            if (j != i) // (optional) avoid copy to self, as may be slower or unsupported by T
                array[j++] = array[i];
            else
                ++j;
    return j;
}

(对于较大的对象,或那些无法安全复制的对象,可能需要和/或更快,更有效地将T*存储在unordered_set中 - 还必须提供解除引用比较运算符和散列功能。)

要想象它是如何工作的,请考虑处理以下输入:

1  3  6  3  5  6  0  2  1
         <--+<----+  |
               <-----+

上面的箭头表示产生答案所需的最小就地压缩:

1  3  6  5  0  2

这正是上面的算法所做的,查看[i]处的所有元素,并跟踪[j]中需要复制到的位置(以及有多少非重复项)