リストにない最小の整数を見つける


87

私の同僚が使用する興味深いインタビューの質問:

符号なし64ビット整数の非常に長いソートされていないリストが与えられたとします。リストにない最小の非負の整数をどのように見つけますか?

フォローアップ:ソートによる明らかな解決策が提案されたので、O(n log n)よりも速く実行できますか?

フォローアップ:アルゴリズムは、たとえば1GBのメモリを搭載したコンピュータで実行する必要があります

明確化:リストはRAMにありますが、大量に消費する可能性があります。リストのサイズ、たとえばNが事前に与えられます。


6
符号なし整数についてどのように話しているかを見て、負でない部分を省略できると思います。
KevenDenen 2009年

4
IMOのオフベースでない限り、この質問はかなり基本的なものですが、他の人が述べているように、質問や前提条件を述べる必要があります。
ジェームスブラック

8
@paxdiablo:これは、O(n)と言ってもそれほど意味がない場合です。2 ^ 64ビット配列をイースター島の粘土板に保存し、伝書鳩でアクセスした場合でも、アルゴリズムはO(n)のままです。
IJケネディ

6
途中でメモリ要件を変更すると、これは素晴らしいインタビューの質問になります;-)
Chris Ballance

1
すべての答えが同じ一般的な解決策(配列を並べ替えて、シーケンスを壊す最初の値を見つける)を行うのは面白いと思いますが、それらはすべて異なる並べ替えを使用します。(変形クイックソート、基数ソート、...)受け入れ答えはN.上記破棄要素というカウンティングソートと同等です
Joren

回答:


121

データ構造をその場で変更でき、ランダムアクセスをサポートしている場合は、O(N)時間とO(1)追加スペースでそれを行うことができます。配列を順番に調べ、すべてのインデックスについて、値で指定されたインデックスにインデックスの値を書き込み、その場所の任意の値をその場所に再帰的に配置し、値> Nを破棄します。次に、配列をもう一度調べてスポットを探します。ここで、値はインデックスと一致しません。これは、配列にない最小の値です。これにより、最大で3Nの比較が行われ、一時スペースに相当する数個の値のみが使用されます。

# Pass 1, move every value to the position of its value
for cursor in range(N):
    target = array[cursor]
    while target < N and target != array[target]:
        new_target = array[target]
        array[target] = target
        target = new_target

# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
    if array[cursor] != cursor:
        return cursor
return N

9
小さなニッチ。リストが{0、...、N-1}の場合、些細なケースを見逃しました。その場合、パス1は何も実行せず、パス2ではarray [cursor] ==リスト内のすべてのエントリのカーソルであるため、アルゴリズムは戻りません。したがって、最後に「returnN」ステートメントが必要です。
アレックス

12
ソリューションはドメインと範囲を統合します(ターゲットは値とインデックスの両方です)。範囲は、使用可能なストレージによって128M要素に制限されていますが、ドメインのサイズは2Gです。配列に割り当てることができるエントリの数よりも大きい値を持つ単一のエントリで失敗します。質問で「非常に長い」が指定されていない場合、入力が破壊されたとしても、答えはエレガントです。この問題では時空間のトレードオフが非常に明白であり、提供された制約の下ではO(N)ソリューションが不可能な場合があります。
ペッカ2013年

2
2番目のパスでは、線形検索の代わりにバイナリ検索を使用できます。
user448810 2013

4
このソリューションは、値の範囲とインデックスが同等である場合にのみ機能します。
ダビー2014年

7
値が大きいほど正常に機能します。大きい値は、配列にない最小値とは何の関係もないため、無視できます。あなたの例では、最初のパスはターゲット<Nのためにすべての値を無視して配列をループし、2番目のパスの最初の反復で0を返します。
Ants Aasma 2015

89

O(N)これは、O(N)スペースを使用する簡単なソリューションです。入力リストを非負の数に制限していて、リストにない最初の非負の数を見つけたいと想定しています。

  1. リストの長さを見つけます。としましょうN
  2. Nallに初期化されたブール値の配列を割り当てますfalse
  3. Xリスト内の各数値について、Xが未満の場合は、配列の要素をNに設定X'thtrueます。
  4. インデックスから始めて配列をスキャン0し、最初の要素であるを探しfalseます。falseインデックスIで最初のものを見つけたら、それIが答えです。それ以外の場合(つまり、すべての要素がである場合true)、答えはNです。

実際には、「Nブール値の配列」はおそらく、byteまたはint配列として表される「ビットマップ」または「ビットセット」としてエンコードされます。これにより、通常、使用するスペースが少なくなり(プログラミング言語によって異なります)、最初のスキャンをfalseより迅速に実行できます。


これが、アルゴリズムが機能する方法/理由です。

Nリスト内の番号が明確でないか、1つ以上の番号がN。より大きいと仮定します。これは、リストにない範囲に少なくとも1つの数値が存在する必要があること意味し0 .. N - 1ます。したがって、最小の欠落数を見つける問題はN未満の最小の欠落数を見つける問題に還元する必要があります。これはN、答えにはならないため、...以上の数値を追跡する必要がないことを意味します。

前の段落の代わりに、リストはからの番号の順列です0 .. N - 1。この場合、ステップ3は配列のすべての要素をに設定しtrue、ステップ4は最初の「欠落している」数がであると通知しNます。


アルゴリズムの計算の複雑さは、O(N)比例定数が比較的小さいことです。リストを2回線形パスするか、リストの長さが最初からわかっている場合は1回だけパスします。リスト全体をメモリに保持することを表す必要はないため、アルゴリズムの漸近的なメモリ使用量は、ブール値の配列を表すために必要なものです。すなわちO(N)ビット。

(対照的に、メモリ内の並べ替えまたはパーティショニングに依存するアルゴリズムは、リスト全体をメモリ内で表現できることを前提としています。質問された形式では、O(N)64ビットワードが必要になります。)


@Jornは、ステップ1から3は、ソートのカウントのバリエーションであるとコメントしています。ある意味で彼は正しいですが、違いは重要です。

  • カウントソートには、(少なくとも)Xmax - Xminカウンターの配列が必要です。ここXmaxで、はリスト内の最大数であり、はリスト内Xminの最小数です。各カウンターは、N個の状態を表すことができなければなりません。つまり、バイナリ表現を想定すると、(少なくとも)整数型のceiling(log2(N))ビットが必要です。
  • 配列サイズを決定するには、カウントソートでリストを最初に通過してXmaxとを決定する必要がありXminます。
  • したがって、最悪の場合の最小スペース要件はceiling(log2(N)) * (Xmax - Xmin)ビットです。

対照的に、上記のアルゴリズムNでは、最悪の場合と最良の場合にビットが必要です。

ただし、この分析により、アルゴリズムがゼロを探してリストを最初に通過した場合(および必要に応じてリスト要素をカウントした場合)、ゼロが見つかった場合はスペースをまったく使用せずに迅速な回答が得られるという直感につながります。リスト内に少なくとも1つのゼロが見つかる可能性が高い場合は、これを行う価値があります。そして、この余分なパスは全体的な複雑さを変えません。


編集:ビットとビットマップを使用した元の説明が混乱していると思われるため、アルゴリズムの説明を「ブール値の配列」を使用するように変更しました。


3
@ adi92ステップ3ですべてのビットが1に設定されたビットマップが得られた場合、リストには0からN-1までのすべての値が含まれます。これは、リスト内の最小の非負の整数がNであることを意味します。リストにない0からN-1までの値がある場合、対応するビットは設定されません。したがって、そのような最小の値が答えです。
divegeek 2009年

4
@ adi92あなたの例では、リストには300個の要素が含まれます。つまり、「欠落している」値がある場合は、300未満である必要があります。アルゴリズムを実行して、300スロットのビットフィールドを作成し、スロット1、2、および3にビットを繰り返し設定して、すべてを残します。他のスロット(0および4から299)はクリアされます。ビットフィールドをスキャンすると、スロット0のフラグがクリアされていることがわかるので、0が答えであることがわかります。
divegeek 2009年

4
このアルゴリズムは、「サイズNのブール配列を作成する」などのビットをいじることなく、より簡単に理解できることに注意してください。そのように理解すれば、ビット単位のバージョンへの移行は概念的に簡単です。
Jon Skeet

2
抽象的解決策を与えるときは、概念的に最も単純な方法を使用し、過度に専門化しないでください。あなたのソリューションは(抽象的な)ブール配列の使用を叫ぶので、それをそれと呼んでください。この配列をbool[]ビットマップまたはビットマップで実装することは、一般的な解決策とは無関係です。
Joren 2009年

2
この解決策は、「Nを超える要素を無視するカウントソートを使用し、最初から線形検索を実行して最初の欠落している要素を見つける」によって最もよく説明できると思います。
Joren 2009年

13

OPは、元のリストがRAMに保持され、コンピューターにはたとえば1GBのメモリしかないことを指定しているので、手足に出て、答えがゼロであると予測します。

1GBのRAMは、リストに最大で134,217,728個の番号を含めることができることを意味します。しかし、2 64 = 18,446,744,073,709,551,616の可能な数があります。したがって、ゼロがリストに含まれる確率は、137,438,953,472分の1です。

対照的に、今年の落雷の確率は70万分の1です。そして、隕石に見舞われる確率は約10兆分の1です。ですから、答えがゼロでないよりも、天体による早すぎる死のために、私は科学雑誌に書かれる可能性が約10倍あります。


11
計算は、値が均一に分散され、ランダムに選択された場合にのみ成立します。それらは、順次生成された可能性もあります。
divegeek 2009年

1
もちろん、あなたは正しいです。しかし、私はすべて、一般的なケースに合わせて最適化することを目的としています。:)
バリーブラウン

10
では、面接対象者がこの回答で選ばれる可能性はどのくらいですか?
Amarghosh 2009年

6
質問は、数字がランダムに均一に選択されるとは言いません。それらは、この質問を設定する人によって選択されます。これを考えると、0がリストに含まれる確率は137,438,953,472分の1よりはるかに大きく、おそらく2分の1よりもさらに大きいでしょう。:-)
ShreevatsaR

8
@Amarghoshその質問への答えもゼロです。
PeterAllenWebb 2009年

10

他の回答で指摘されているように、並べ替えを行ってから、ギャップが見つかるまでスキャンするだけです。

ギャップを含む可能性のある候補ではないパーティションを削除する修正されたクイックソートを使用することにより、アルゴリズムの複雑さをO(N)に改善し、O(N)スペースを維持できます。

  • 最初のパーティションフェーズで、重複を削除します。
  • パーティショニングが完了したら、下のパーティションのアイテム数を確認します
  • この値は、パーティションの作成に使用された値と同じですか?
    • もしそうなら、それはギャップがより高いパーティションにあることを意味します。
      • 下位パーティションを無視して、クイックソートを続行します
    • それ以外の場合、ギャップは下部パーティションにあります
      • 上位のパーティションを無視して、クイックソートを続行します

これにより、多数の計算が節約されます。


それはかなり気の利いたです。線形時間未満でパーティションの長さを計算できると想定します。これは、パーティション配列と一緒に格納されている場合に実行できます。また、元のリストがRAMに保持されていることも前提としています。
バリーブラウン

2
リストの長さがわかっている場合は、len(list)より大きい値をカリングすることもできます。鳩の巣原理によ​​り、「穴」はlen(list)未満でなければなりません。
divegeek 2009年

1
それはO(n)ではないと思います... 1つは、リストが完全にソートされるまで重複を削除できるかどうかわかりません。(あなたは中間の下と上に分けたので)あなたが離れて半分の探索空間の各反復を投げることを保証することができますしながら、第二に、あなたはまだ、Nに依存だデータ上(Nに依存)複数のパスを持っています。
paxdiablo 2009年

1
paxdiablo:Stephen Cが提案したようなビットマップ方式を使用して、一意の値のみで新しいリストを作成できます。これは、O(n)の時間と空間で実行されます。それ以上にできるかどうかはわかりません。
ニック

9

O(N)思考の落とし穴の1つを説明するために、ここにスペースO(N)を使用するアルゴリズムがありますO(1)

for i in [0..2^64):
  if i not in list: return i

print "no 64-bit integers are missing"

1
意志は正しい。ここには実際には2つのループがあるため、これはO(n)ではありませんが、1つは暗黙的です。値がリストにあるかどうかを判断するのはO(n)演算であり、forループでそれをn回実行しています。それはそれをO(n ^ 2)にします。
ニック

6
Nic、Will、それはO(n * N)です。ここで、nはリストのサイズ、Nはドメインのサイズ(64ビット整数)です。Nは膨大な数ですが、それでも定数であるため、形式的には、前述の問題の複雑さはO(n)です。
Ants Aasma 2009年

1
アリ、私はそれがO(n N)であることに同意しますが、Nは一定ではありません。アルゴリズムは答えを見つけたときに終了するため、外側のループを通過する完全な反復回数は答えと等しく、それ自体がリストのサイズによって制限されます。したがって、この場合、O(N n)はO(n ^ 2)です。
ウィルハリス

12
N個の要素のリストで数字を探すのは明らかにO(N)です。これを2 ^ 64回行います。大きいですが、2 ^ 64は定数です。したがって、アルゴリズムはC * O(N)であり、それでもO(N)です。
IJケネディ

3
私は以前の声明を撤回しなければなりません。最も厳密な定義によれば、この操作は確かにO(n)です。
ニック

8

数値はすべて64ビット長であるため、基数ソートを使用できます。これはO(n)です。'emを並べ替えてから、探しているものが見つかるまで' emをスキャンします。

最小数がゼロの場合は、ギャップが見つかるまで前方にスキャンします。最小数がゼロでない場合、答えはゼロです。


本当ですが、基数ソートではメモリ要件がかなり厳しくなる可能性があります。
PeterAllenWebb 2009年

1
基数ソートは、非常に大きなデータセットでは機能しません。ただし、パーティションと基数ソートは機能する可能性があります。
ダースベイダー2009年

5

スペース効率の高い方法で、すべての値が異なる場合は、スペースO( k )と時間でそれを行うことができますO( k*log(N)*N )。スペース効率が高く、データの移動がなく、すべての操作が基本的です(加算と減算)。

  1. セットする U = N; L=0
  2. まず、番号スペースをkリージョンに分割します。このような:
    • 0->(1/k)*(U-L) + L0->(2/k)*(U-L) + L0->(3/k)*(U-L) + L...0->(U-L) + L
  3. count{i}各地域にいくつの数字()があるかを調べます。(N*kステップ)
  4. hいっぱいではない最初の領域()を見つけます。つまり、count{h} < upper_limit{h}。(kステップ)
  5. h - count{h-1} = 1あなたがあなたの答えを持っているなら
  6. セットする U = count{h}; L = count{h-1}
  7. 後藤2

これはハッシュを使用して改善できます(Nicのこのアイデアに感謝します)。

  1. 同じ
  2. まず、番号スペースをkリージョンに分割します。このような:
    • L + (i/k)->L + (i+1/k)*(U-L)
  3. inc count{j} を使用して j = (number - L)/k (if L < number < U)
  4. hk個の要素を含まない最初の領域()を見つける
  5. count{h} = 1hがあなたの答えなら
  6. セットする U = maximum value in region h L = minimum value in region h

これはで実行されO(log(N)*N)ます。


私はこの答えが本当に好きです。少し読みづらかったですが、質問を読んだときに頭の中にあったものとよく似ています。
ニック

また、ある時点で、Stephen Cによるそのビットマップソリューションに切り替えるのが賢明でしょう。おそらく次の場合U-L < k
Egon

これはO(log(N)* N)ではなく、O(N)で実行されます。あなたの答えは@cdigginsの一般お答えして、合計ので、それは(1 / K **私は、私の範囲内(はceil(log_k(N)))のために)<= 2 O(N)で動作します
Lapinot

O(N)の数値を反復するたびに、O(log_k(N))の合計反復が必要になります。したがって、O(log_k(N)* N)== O(log(N)* N)です。元の番号は並べ替え/バケット化されていないため、すべてを確認する必要があります。
エゴン2016年

ただし、元のリストをk個の領域(サイズn / k)に分割した場合は、いっぱいになっていない最初の領域を選択します。したがって、次の反復では、選択した領域を検討し、それをk個の新しい領域(サイズn / k ** 2)などに分割するだけで済みます。実際には、リスト全体を毎回反復する必要はありません(そうでない場合、分割のポイントは何ですか)。 ?)。
ラピノット2016年

3

それらを並べ替えてから、ギャップ(ゼロと最初の数値の間の最初のギャップを含む)が見つかるまでシーケンスを実行します。

アルゴリズムに関しては、次のようなものがそれを行います。

def smallest_not_in_list(list):
    sort(list)
    if list[0] != 0:
        return 0
    for i = 1 to list.last:
        if list[i] != list[i-1] + 1:
            return list[i-1] + 1
    if list[list.last] == 2^64 - 1:
        assert ("No gaps")
    return list[list.last] + 1

もちろん、CPUのうなり声よりもはるかに多くのメモリがある場合は、可能なすべての64ビット値のビットマスクを作成し、リスト内のすべての数値のビットを設定することができます。次に、そのビットマスクで最初の0ビットを探します。それは時間の点ではO(n)演算になりますが、メモリ要件の点ではかなり高価です:-)

O(n)を改善できるとは思えません。なぜなら、各数値を少なくとも1回は確認しない方法がわからないからです。

そのためのアルゴリズムは、次の線に沿っています。

def smallest_not_in_list(list):
    bitmask = mask_make(2^64) // might take a while :-)
    mask_clear_all (bitmask)
    for i = 1 to list.last:
        mask_set (bitmask, list[i])
    for i = 0 to 2^64 - 1:
        if mask_is_clear (bitmask, i):
            return i
    assert ("No gaps")

説明から、最初の要素はリストにない最小のものであるため、0を除外しているようです。しかし、それは私がした仮定であり、私は間違っている可能性があります。
ジェームスブラック

私の考えでは、ソートされたシーケンスが4、5、6の場合、0はリストにない最小のものになります。
paxdiablo 2009年

2、3、5、答えは4になると思いますが、間違っている可能性があります。
ジェームスブラック

OPが回答する必要のある質問。検索スペースは「すべて64ビットの符号なし整数」ですか、それとも「リストの最下位から最上位までのすべての数値」ですか?
paxdiablo 2009年

最悪の場合、おそらく二分木ですでにソートされていない限り、少なくとも1回は調べる必要があることに同意します。
ジェームスブラック


1

隠れた要素はかなり大きいですが、O(n)時間とO(1)追加スペースでそれを行うことができます。これは問題を解決するための実用的な方法ではありませんが、それでも興味深いかもしれません。

符号なし64ビット整数(昇順)ごとに、ターゲット整数が見つかるか、リストの最後に到達するまで、リストを繰り返し処理します。リストの最後に到達した場合、ターゲット整数はリストにない最小の整数です。64ビット整数の終わりに達すると、すべての64ビット整数がリストに含まれます。

これはPython関数としてのものです:

def smallest_missing_uint64(source_list):
    the_answer = None

    target = 0L
    while target < 2L**64:

        target_found = False
        for item in source_list:
            if item == target:
                target_found = True

        if not target_found and the_answer is None:
            the_answer = target

        target += 1L

    return the_answer

この関数は、O(n)を維持するために意図的に非効率的です。特に、答えが見つかった後でも、関数はターゲット整数をチェックし続けることに注意してください。回答が見つかるとすぐに関数が返される場合、外側のループが実行された回数は、回答のサイズ(nによって制限される)によって制限されます。この変更により、実行時間はO(n ^ 2)になりますが、はるかに高速になります。


本当。O(1)空間とO(n)時間であるアルゴリズムのいくつかが、この質問で実際にどれほどひどく失敗するかはおかしいです。
PeterAllenWebb 2009年

1

私のインスピレーションを与えてくれたegon、swilden、StephenCに感謝します。まず、リストのサイズを超えることはできないため、目標値の範囲がわかります。また、1GBのリストには、最大で134217728(128 * 2 ^ 20)の64ビット整数を含めることができます。

ハッシュ部分
ハッシュを使用して検索スペースを大幅に削減することを提案します。まず、リストのサイズの平方根。1GBのリストの場合、N = 11,586です。サイズNの整数配列を設定します。リストを反復処理し、見つかった各数値の平方根*をハッシュとして使用します。ハッシュテーブルで、そのハッシュのカウンターをインクリメントします。次に、ハッシュテーブルを繰り返し処理します。最大サイズと等しくない最初のバケットは、新しい検索スペースを定義します。

ビットマップ部分次
に、新しい検索スペースのサイズに等しい通常のビットマップを設定し、ソースリストを繰り返し処理して、検索スペースで各番号を見つけたらビットマップに入力します。完了したら、ビットマップの最初の未設定ビットが答えを示します。

これは、O(n)時間とO(sqrt(n))空間で完了します。

(*ビットシフトなどを使用してこれをより効率的に行うことができ、それに応じてバケットの数とサイズを変更するだけです。)


1
メモリーフットプリントを削減するために検索スペースをRoot-Nバケットに分割するというアイデアは気に入っていますが、リストに重複があるとこの方法が壊れてしまいます。直せるかな。
PeterAllenWebb 2009年

そうです、私は重複したエントリを検討することを怠りました。それを回避できるかどうかはわかりません。
ニック

1

番号のリストに欠落している番号が1つしかない場合、不足している番号を見つける最も簡単な方法は、シリーズを合計し、リスト内の各値を減算することです。最終的な値は欠落している数です。


ええ。それは別の古典的なインタビューの質問です。
PeterAllenWebb 2009年

1
それよりもさらに簡単なのは、リスト内の数値を一緒にXORし、範囲内の数値を一緒にXORし、結果を一緒にXORすることです。
John Kurlak 2014

1
 int i = 0;
            while ( i < Array.Length)
            {

                if (Array[i] == i + 1)
                {
                    i++;
                }

                if (i < Array.Length)
                {
                    if (Array[i] <= Array.Length)
                    {//SWap

                        int temp = Array[i];
                        int AnoTemp = Array[temp - 1];
                        Array[temp - 1] = temp;
                        Array[i] = AnoTemp;

                    }
                    else
                       i++;



                }
            }

            for (int j = 0; j < Array.Length; j++)
            {
                if (Array[j] > Array.Length)
                {
                    Console.WriteLine(j + 1);
                    j = Array.Length;
                }
                else
                    if (j == Array.Length - 1)
                        Console.WriteLine("Not Found !!");

            }
        }

1

ハッシュテーブルを使用して数値を保持できます。すべての数値が完了したら、最小値が見つかるまで0からカウンターを実行します。適度に優れたハッシュは、一定時間でハッシュおよび保存され、一定時間で取得されます。

for every i in X         // One scan Θ(1)
   hashtable.put(i, i);  // O(1)

low = 0;

while (hashtable.get(i) <> null)   // at most n+1 times
   low++;

print low;

n配列に要素があり、がである場合の最悪のケース。{0, 1, ... n-1}その場合、答えはで取得されn、それを維持しO(n)ます。


1

これがJavaで書かれた私の答えです:

基本的な考え方:1-配列をループして、重複する正、ゼロ、および負の数を破棄し、残りを合計して、最大の正の数も取得し、一意の正の数をマップに保持します。

2-合計をmax *(max + 1)/ 2として計算します。

3-ステップ1と2で計算された合計の差を見つけます

4- 1から[合計差、最大]まで再度ループし、手順1で入力したマップにない最初の数値を返します。

public static int solution(int[] A) {
    if (A == null || A.length == 0) {
        throw new IllegalArgumentException();
    }

    int sum = 0;
    Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
    int max = A[0];
    for (int i = 0; i < A.length; i++) {
        if(A[i] < 0) {
            continue;
        }
        if(uniqueNumbers.get(A[i]) != null) {
            continue;
        }
        if (A[i] > max) {
            max = A[i];
        }
        uniqueNumbers.put(A[i], true);
        sum += A[i];
    }
    int completeSum = (max * (max + 1)) /  2;
    for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
        if(uniqueNumbers.get(j) == null) { //O(1)
            return j;
        }
    }
    //All negative case
    if(uniqueNumbers.isEmpty()) {
        return 1;
    }
    return 0;
}

0

Stephen Cが賢く指摘したように、答えは配列の長さよりも小さい数でなければなりません。次に、二分探索で答えを見つけます。これにより、最悪のケースが最適化されます(したがって、インタビュアーは「もしも」の病理学的シナリオであなたを捕まえることができません)。面接では、最悪の場合に最適化するためにこれを行っていることを指摘してください。

二分探索を使用する方法は、配列の各要素から探している数を減算し、否定的な結果をチェックすることです。


0

私は「推測ゼロ」のアプローチが好きです。数値がランダムである場合、ゼロである可能性が高くなります。「審査官」がランダムでないリストを設定した場合は、リストを追加してもう一度推測します。

LowNum=0
i=0
do forever {
  if i == N then leave /* Processed entire array */
  if array[i] == LowNum {
     LowNum++
     i=0
     }
   else {
     i++
   }
}
display LowNum

最悪のケースはn * Nでn = Nですが、実際にはnは少数である可能性が高いです(例:1)


0

質問があったかどうかわかりません。しかし、リスト1、2、3、5、6で、欠落している番号が4の場合、欠落している番号はO(n)で次のように見つけることができます:(n + 2)(n + 1)/ 2-(n + 1)n / 2

編集:申し訳ありませんが、私は昨夜あまりにも速く考えていたと思います。とにかく、2番目の部分は実際にはsum(list)に置き換える必要があります。これは、O(n)が来る場所です。この式は、その背後にある考え方を明らかにしています。n個の連続する整数の場合、合計は(n + 1)* n / 2でなければなりません。欠落している数がある場合、合計は(n + 1)個の連続する整数の合計から欠落している数を引いたものに等しくなります。

私が頭の中にいくつかの中間部分を置いていたという事実を指摘してくれてありがとう。


1
私は、これがどのように機能するかを一見してわかりません。あなたの場合、n = 5であり、その中のいくつが欠落していても、公式は修正されます。
sisve 2009年

サイモン:私の編集によると、反対票を削除していただけませんか?
コディズム2009年

0

よくやったアリAasma!私は約15分間答えを考え、あなたと同じような考え方で独自に答えを思いつきました。

#define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
    int m = n;
    for (int i = 0; i < m;) {
        if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
            m--;
            SWAP (a[i], a[m]);
            continue;
        }
        if (a[i] > i) {
            SWAP (a[i], a[a[i]]);
            continue;
        }
        i++;
    }
    return m;
}

mは、「最初のi入力について知っていることを前提として、m-1のエントリまで値について他に何も想定しない、現在の最大可能出力」を表します。

このmの値は、(a [i]、...、a [m-1])が値(i、...、m-1)の順列である場合にのみ返されます。したがって、a [i]> = mの場合、a [i] <iの場合、またはa [i] == a [a [i]]の場合、mは間違った出力であり、少なくとも1要素下でなければならないことがわかります。したがって、mをデクリメントし、a [i]をa [m]と交換すると、再帰できます。

これが当てはまらないがa [i]> iの場合、a [i]!= a [a [i]]であることがわかっているので、a [i]をa [a [i]]と交換すると要素の数が増えることがわかります。自分の場所で。

それ以外の場合、a [i]はiと等しくなければなりません。その場合、このインデックスまでのすべての値がそれらのインデックスに等しいことを知って、iをインクリメントできます。

これが無限ループに入ることができないという証拠は、読者の練習問題として残されています。:)


0

Antsの回答からのDafnyフラグメントは、インプレースアルゴリズムが失敗する理由を示しています。requires前提条件は、各項目の値が配列の境界を越えてはならないことを説明しています。

method AntsAasma(A: array<int>) returns (M: int)
  requires A != null && forall N :: 0 <= N < A.Length ==> 0 <= A[N] < A.Length;
  modifies A; 
{
  // Pass 1, move every value to the position of its value
  var N := A.Length;
  var cursor := 0;
  while (cursor < N)
  {
    var target := A[cursor];
    while (0 <= target < N && target != A[target])
    {
        var new_target := A[target];
        A[target] := target;
        target := new_target;
    }
    cursor := cursor + 1;
  }

  // Pass 2, find first location where the index doesn't match the value
  cursor := 0;
  while (cursor < N)
  {
    if (A[cursor] != cursor)
    {
      return cursor;
    }
    cursor := cursor + 1;
  }
  return N;
}

forall ...句の有無にかかわらず、コードをバリデーターに貼り付けて、検証エラーを確認します。2番目のエラーは、ベリファイアがパス1ループの終了条件を確立できない結果です。これを証明するのは、ツールをよりよく理解している人に任されています。


0

これは、入力を変更せず、O(N)時間とNビットに加えて、メモリの小さな一定のオーバーヘッド(Nはリストのサイズ)を使用するJavaの回答です。

int smallestMissingValue(List<Integer> values) {
    BitSet bitset = new BitSet(values.size() + 1);
    for (int i : values) {
        if (i >= 0 && i <= values.size()) {
            bitset.set(i);
        }
    }
    return bitset.nextClearBit(0);
}

0
def solution(A):

index = 0
target = []
A = [x for x in A if x >=0]

if len(A) ==0:
    return 1

maxi = max(A)
if maxi <= len(A):
    maxi = len(A)

target = ['X' for x in range(maxi+1)]
for number in A:
    target[number]= number

count = 1
while count < maxi+1:
    if target[count] == 'X':
        return count
    count +=1
return target[count-1] + 1

上記のソリューションで100%を取得しました。


0

1)負のフィルターとゼロ

2)ソート/個別

3)アレイにアクセス

複雑さ:O(N)またはO(N * log(N))

Java8を使用する

public int solution(int[] A) {
            int result = 1;
    boolean found = false;
    A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
    //System.out.println(Arrays.toString(A));
    for (int i = 0; i < A.length; i++) {
        result = i + 1;
        if (result != A[i]) {
            found = true;
            break;
        }
    }
    if (!found && result == A.length) {
        //result is larger than max element in array
        result++;
    }
    return result;
}

0

unordered_setを使用してすべての正の数を格納できます。次に、1からunordered_setの長さまで反復して、発生しない最初の数を確認できます。

int firstMissingPositive(vector<int>& nums) {

    unordered_set<int> fre;
    // storing each positive number in a hash.
    for(int i = 0; i < nums.size(); i +=1)
    {
        if(nums[i] > 0)
            fre.insert(nums[i]);
     }

    int i = 1;
    // Iterating from 1 to size of the set and checking 
    // for the occurrence of 'i'

    for(auto it = fre.begin(); it != fre.end(); ++it)
    {
        if(fre.find(i) == fre.end())
            return i;
        i +=1;
    }

    return i;
}

0

基本的なJavaScriptによる解決策

var a = [1, 3, 6, 4, 1, 2];

function findSmallest(a) {
var m = 0;
  for(i=1;i<=a.length;i++) {
    j=0;m=1;
    while(j < a.length) {
      if(i === a[j]) {
        m++;
      }
      j++;
    }
    if(m === 1) {
      return i;
    }
  }
}

console.log(findSmallest(a))

これが誰かに役立つことを願っています。


0

Pythonの場合、これは最も効率的ではありませんが、正しいです

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import datetime

# write your code in Python 3.6

def solution(A):
    MIN = 0
    MAX = 1000000
    possible_results = range(MIN, MAX)

    for i in possible_results:
        next_value = (i + 1)
        if next_value not in A:
            return next_value
    return 1

test_case_0 = [2, 2, 2]
test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
test_case_2 = [-1, -22]
test_case_3 = [x for x in range(-10000, 10000)]
test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
test_case_5 = [4, 5, 6]
print("---")
a = datetime.datetime.now()
print(solution(test_case_0))
print(solution(test_case_1))
print(solution(test_case_2))
print(solution(test_case_3))
print(solution(test_case_4))
print(solution(test_case_5))

0
def solution(A):
    A.sort()
    j = 1
    for i, elem in enumerate(A):
        if j < elem:
            break
        elif j == elem:
            j += 1
            continue
        else:
            continue
    return j

0

これは役に立ちます:

0- A is [5, 3, 2, 7];
1- Define B With Length = A.Length;                            (O(1))
2- initialize B Cells With 1;                                  (O(n))
3- For Each Item In A:
        if (B.Length <= item) then B[Item] = -1                (O(n))
4- The answer is smallest index in B such that B[index] != -1  (O(n))

これはスティーブンCの答えとは異なりますか?どうやって?
老い
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.