web-dev-qa-db-ja.com

C ++配列の重複をチェックするよりエレガントな方法?

このコードを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

それは完全に機能しますが、よりエレガントで効率的なチェック方法があるかどうか知りたいです。

16
Saladin Akara

配列を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を使用することをお勧めします-重複はありません。

20
Puppy

次の解決策は、数値をソートしてから重複を削除することに基づいています。

#include <algorithm>

int main()
{
    int userNumbers[6];

    // ...

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

確かに、私が見ることができる最も高速で最もエレガントな方法は、上記のアドバイスです。

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の要素の元の順序を維持します。

乾杯、

ポール

7
Paul Michalik

セットのすべての要素を追加し、追加するときに、すでに存在するかどうかを確認できます。それはよりエレガントで効率的です。

6
Benoit Thiery

なぜこれが提案されなかったのかはわかりませんが、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
Josh Sanders

現在のベストアンサーである@Puppyによるアンサーの拡張です。

PS:@Puppyによる現在のベストアンサーにコメントとしてこの投稿を挿入しようとしましたが、まだ50ポイントがないため、挿入できませんでした。また、さらに役立つように、ここでは実験データを少し共有しています。

Std :: setとstd :: mapはどちらも、バランスバイナリ検索ツリーのみを使用してSTLに実装されています。したがって、どちらもO(nlogn))の複雑さにつながります。この場合に限ります。ハッシュテーブルを使用すると、パフォーマンスが向上します。std:: unordered_mapは、検索を高速化しました。3つすべての実装を実験したところ、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;
}
</ code>

unordered_mapパフォーマンス(実行時間はここでは52ミリ秒でした) enter image description here

Set/Mapパフォーマンス enter image description here

5
ViFI

特に配列の長さが短い場合は問題ありません。配列が大きい場合は、より効率的なアプローチ(n ^ 2/2比較未満)を使用します-DeadMGの回答を参照してください。

コードのいくつかの小さな修正:

  • _int j = i_ write _int j = i +1_の代わりに、if(j != i)テストを省略できます
  • iステートメントの外でfor変数を宣言する必要はありません。
4
leonbloy
#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;
}
1
FaridLU
//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));
1
coh

@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();
}
0
Hanzhou Tang

@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;
}
0