アレイから重複を削除し、別のアレイに保存します

Nov 23 2020

そのため、別の既存の配列から値が重複しないように配列を作成する必要があります。だから私はそれをしました、しかし私はそれをする他のより良い方法があるか知りたいです。

入力/出力の例:

Input10, 15, 10, 5, 1, 3

Output10, 15, 5, 1, 3

これが私のコードです。

#include <stdio.h>

int main(void) {
  const int MAX_ARRAY_SIZE = 5;

  int m[MAX_ARRAY_SIZE], p[MAX_ARRAY_SIZE];


  for(int i = 0; i < MAX_ARRAY_SIZE; i++) {
    printf("Enter number: ");
    scanf("%d",&m[i]);
  }
  int k = 0;
  int dup = 0;
  for(int i =0; i < MAX_ARRAY_SIZE; i++) {
    for(int j = i +1; j <MAX_ARRAY_SIZE; j++) {
        if(m[i] == m[j]) {
            dup = 1;
        }
    }
    if(dup != 1) {
      p[k++] = m[i];
    }
    dup = 0;
  }

  printf("The new array without repeated values\n");
  for(int i = 0; i < k; i++) {
    printf("%d\n",p[i]);
  }

  return 0;
}

これが私がそれを行う正しい簡単な方法であるかどうかはわかりません。いくつかの提案が欲しいです。

前もって感謝します。:)

回答

4 Reinderien Nov 23 2020 at 22:31

早期終了

        dup = 1;

あなたがすべきbreakです。残りのループを実行する必要はありません。

ブール値

を使用し<stdbool.h>、作成しbool dup = false、後で割り当てtrue、そして書くことを検討してくださいif (!dup)

複雑

実際には、5つの値の配列は計算コストを発生させません。ただし、教授が複雑さの分析に関心がある場合、これに対する「適切な」ソリューションは、ハッシュセットのようなものを疑似コードとともに使用して、(現在の2次時間ではなく)線形時間で完了する必要があります。

Set *seen = make_set();
for (int i = 0; i < MAX_ARRAY_SIZE; i++)
{
    int m;
    scanf(&m);
    if (!contains(seen, m))
        add(seen, m);
}

for (m in seen)
    printf(m);
Noname Nov 24 2020 at 16:07

このQには、重複排除自体が必要です。重複を削除します...しかし、これは内部ループの3番目のバージョンであるため、新たな開始を利用します。

この不快な割り当てはint j = i + 1;、元々for-expression-listにパックされていましたjが、最後のiを初期化するだけでなく、m[j]違法/未定義になります。

目標(?)は、dupフラグを回避し、ループを「正規化」することです。この再配置はそれだけの価値があると思います。

    int j;
    for (int i = 0; i < ARRAY_SIZE; i++) {
        j = i;
        do
            if (++j == ARRAY_SIZE) {   // already past end?    
                p[k++] = m[i];            // copy this one
                break;                    // and finish
            }
        while (m[i] != m[j]);          // if match, then just finish 
    }

今、すべてが自然な場所にあります。

do statement while (expr);構造を説明するために中括弧なしで書きました。少し隠されているのは、ループの増分if (++j...です。


実際の(ソートされた)構造の代わりに、新しい一意の配列を使用して重複を検索できます。0すでに新しい配列にあるため、最初に最初の要素を無条件にコピーしてから、2番目の要素でループを開始します。

    int k = 1;
    /* First is always unique */
    printf("m[0] -> p[0]\n");
    p[0] = m[0];
    for (int i = 1; i < ARRAY_SIZE; i++)
        for (int j = 0;; j++) {
            if (j == k) {         
                printf("m[i=%d] -> p[k=%d]\n", i, k);
                p[k++] = m[i];
                break;
            }
            if (p[j] == m[i])
                break;
        }

それでも、これif (p[j] == m[i])は論理的に後if (j == k)でなければならないので、forループは少しフリースタイルにする必要があります。

は次printfのことを示しています。

Enter number: 6
Enter number: 6
Enter number: 0
Enter number: 0
Enter number: 8
m[0] -> p[0]
m[i=2] -> p[k=1]
m[i=4] -> p[k=2]
The array without repeated values
6
0
8

副作用:順序が保持されるようになりました。

検索と挿入は非常に密接に関連しているため、これは少し注意が必要だと思います。kインデックスは正確に処理する必要があります。(他のものも)

パフォーマンス: kまでの新しい配列を使用する方が、元の配列の残りを検索するOPよりも高速かどうかさえわかりません。少なくともいくつかのケースでは同じになるようです。

問題は、新しい配列がソートされていないことです。挿入するたびに、単純に行うと、ソートを維持するのにコストがかかりすぎます。

したがって、効率的に検索するには、最初に「拡散」する必要があります。(ランダムな)整数の場合、モジュロ10は10個の異なる配列(またはバケット)を作成できます。b[][](OPの代わりにp[])2Dを使用

b[0] {100}
b[1] {1, 31, 20001}
b[2] {12, 32, 502}
b[3] {}
b[4] {94}
...

すべての(サブ)配列にはARRAY_SIZE、最悪の場合のオリジナルが必要です。しかし現在、重複を検索する配列は平均で10分の1になっています。


したがって、インタラクティブ入力を100万整数の配列ジェネレーターに変更して、いくつかのテストを行うことができます。


すべてそのdupループフラグのため;)