更优雅的方法来检查C ++数组中的重复项?

时间:2010-10-23 10:48:05

标签: c++ arrays duplicates

我在C ++中编写此代码作为uni任务的一部分,我需要确保数组中没有重复项:

// Check for duplicate numbers in user inputted data
    int i; // Need to declare i here so that it can be accessed by the 'inner' loop that starts on line 21
    for(i = 0;i < 6; i++) { // Check each other number in the array
        for(int j = i; j < 6; j++) { // Check the rest of the numbers
            if(j != i) { // Makes sure don't check number against itself
                if(userNumbers[i] == userNumbers[j]) {
                    b = true;
                }
            }
            if(b == true) { // If there is a duplicate, change that particular number
                cout << "Please re-enter number " << i + 1 << ". Duplicate numbers are not allowed:" << endl;
                cin >> userNumbers[i];
            }
        } // Comparison loop
        b = false; // Reset the boolean after each number entered has been checked
    } // Main check loop

它完美无缺,但我想知道是否有更优雅或更有效的检查方式。

12 个答案:

答案 0 :(得分:19)

您可以在O(nlog(n))中对数组进行排序,然后直视下一个数字。这比你的O(n ^ 2)现有算法要快得多。代码也更清洁。您的代码也不能确保在重新输入时不会插入重复项。您需要首先防止重复存在。

std::sort(userNumbers.begin(), userNumbers.end());
for(int i = 0; i < userNumbers.size() - 1; i++) {
    if (userNumbers[i] == userNumbers[i + 1]) {
        userNumbers.erase(userNumbers.begin() + i);
        i--;
    }
}

我也推荐使用std :: set - 没有重复项。

答案 1 :(得分:9)

以下解决方案基于对数字进行排序,然后删除重复项:

#include <algorithm>

int main()
{
    int userNumbers[6];

    // ...

    int* end = userNumbers + 6;
    std::sort(userNumbers, end);
    bool containsDuplicates = (std::unique(userNumbers, end) != end);
}

答案 2 :(得分:7)

确实,最快,到目前为止,我可以看到最优雅的方法如上所述:

std::vector<int> tUserNumbers;
// ...
std::set<int> tSet(tUserNumbers.begin(), tUserNumbers.end());
std::vector<int>(tSet.begin(), tSet.end()).swap(tUserNumbers);

是O(n log n)。但是,如果需要保留输入数组中的数字顺序,则无法实现...在这种情况下,我做了:

    std::set<int> tTmp;
    std::vector<int>::iterator tNewEnd = 
        std::remove_if(tUserNumbers.begin(), tUserNumbers.end(), 
        [&tTmp] (int pNumber) -> bool {
            return (!tTmp.insert(pNumber).second);
    });
    tUserNumbers.erase(tNewEnd, tUserNumbers.end());

仍为O(n log n)并保持tUserNumbers中元素的原始排序。

干杯,

答案 3 :(得分:6)

您可以添加集合中的所有元素,并在添加时检查它是否已存在。那将更加优雅和高效。

答案 4 :(得分:5)

我不确定为什么没有提出这个问题,但是这里有一种方法可以在基数10中找到O(n)中的重复项。我在已经建议的O(n)解决方案中看到的问题是它需要首先对数字进行排序。此方法为O(n),不需要对集进行排序。很酷的是检查特定数字是否有重复是O(1)。我知道这个帖子可能已经死了,但也许它会帮助别人! :)

/*
============================
Foo
============================
* 
   Takes in a read only unsigned int. A table is created to store counters 
   for each digit. If any digit's counter is flipped higher than 1, function
   returns. For example, with 48778584:
    0   1   2   3   4   5   6   7   8   9
   [0] [0] [0] [0] [2] [1] [0] [2] [2] [0]

   When we iterate over this array, we find that 4 is duplicated and immediately
   return false.

*/
bool Foo( unsigned const int &number)
{
    int temp = number;
    int digitTable[10]={0};

    while(temp > 0)
    {
        digitTable[temp % 10]++; // Last digit's respective index.
        temp /= 10; // Move to next digit
    }

    for (int i=0; i < 10; i++)
    {
        if (digitTable [i] > 1)
        {
            return false;
        }
    }
    return true;
}

答案 5 :(得分:5)

这是@Puppy的答案的延伸,这是目前最好的答案。

PS:我试图在@Puppy当前的最佳答案中插入这篇文章作为评论,但不能这样,因为我还没有50分。此处还分享了一些实验数据以获得进一步的帮助。

std :: set和std :: map都是在STL中使用Balanced Binary Search树实现的。因此,只有在这种情况下,两者都会导致O(nlogn)的复杂性。如果使用哈希表,则可以实现更好的性能。 std :: unordered_map提供基于哈希表的实现,以加快搜索速度。我尝试了所有三个实现,并发现使用std :: unordered_map的结果比std :: set和std :: map更好。结果和代码在下面分享。图像是LeetCode在解决方案上测量的性能快照。

bool hasDuplicate(vector<int>& nums) {
    size_t count = nums.size();
    if (!count)
        return false;
    std::unordered_map<int, int> tbl;
    //std::set<int> tbl;
    for (size_t i = 0; i < count; i++) {
        if (tbl.find(nums[i]) != tbl.end())
            return true;
        tbl[nums[i]] = 1;
        //tbl.insert(nums[i]);
    }
    return false;
}

bool hasDuplicate(vector<int>& nums) { size_t count = nums.size(); if (!count) return false; std::unordered_map<int, int> tbl; //std::set<int> tbl; for (size_t i = 0; i < count; i++) { if (tbl.find(nums[i]) != tbl.end()) return true; tbl[nums[i]] = 1; //tbl.insert(nums[i]); } return false; } unordered_map 效果(此处运行时间为52毫秒) enter image description here

设置/地图效果 enter image description here

答案 6 :(得分:4)

没关系,特别适合小阵列长度。如果数组更大,我会使用更高效的aproaches(小于n ^ 2/2比较) - 请参阅DeadMG的答案。

您的代码的一些小修正:

  • 而不是int j = iint j = i +1而您可以省略if(j != i)测试
  • 您不需要在i声明之外声明for变量。

答案 7 :(得分:1)

//std::unique(_copy) requires a sorted container.
std::sort(cont.begin(), cont.end());

//testing if cont has duplicates
std::unique(cont.begin(), cont.end()) != cont.end();

//getting a new container with no duplicates
std::unique_copy(cont.begin(), cont.end(), std::back_inserter(cont2));

答案 8 :(得分:1)

#include<iostream>
#include<algorithm>

int main(){

    int arr[] = {3, 2, 3, 4, 1, 5, 5, 5};
    int len = sizeof(arr) / sizeof(*arr); // Finding length of array

    std::sort(arr, arr+len);

    int unique_elements = std::unique(arr, arr+len) - arr;

    if(unique_elements == len) std::cout << "Duplicate number is not present here\n";
    else std::cout << "Duplicate number present in this array\n";

    return 0;
}

答案 9 :(得分:0)

如@underscore_d所述,一种优雅而有效的解决方案将是

#include <algorithm>
#include <vector>

template <class Iterator>
bool has_duplicates(Iterator begin, Iterator end) {
    using T = typename std::iterator_traits<Iterator>::value_type;
    std::vector<T> values(begin, end);

    std::sort(values.begin(), values.end());
    return (std::adjacent_find(values.begin(), values.end()) != values.end());
}

int main() {
    int user_ids[6];
    // ...
    std::cout << has_duplicates(user_ids, user_ids + 6) << std::endl;
}

答案 10 :(得分:0)

我认为@Michael Jaison G的解决方案非常出色,为了避免排序,我对他的代码做了一些修改。 (通过使用unordered_set,该算法可能会更快一点。)

template <class Iterator>
bool isDuplicated(Iterator begin, Iterator end) {
    using T = typename std::iterator_traits<Iterator>::value_type;
    std::unordered_set<T> values(begin, end);
    std::size_t size = std::distance(begin,end);
    return size != values.size();
}

答案 11 :(得分:0)

快速O(N)时空解决方案 重复时先返回

template <typename T>
bool containsDuplicate(vector<T>& items) {
    return any_of(items.begin(), items.end(), [s = unordered_set<T>{}](const auto& item) mutable {
        return !s.insert(item).second;
    });
}