古典的な問題に関連する 40億の整数ではない整数を見つける しかし、まったく同じではない。
明確にするために、integersで本当に意味するのは、その数学的な定義のサブセットにすぎません。つまり、整数の数が有限であると仮定します。 C++では、[INT_MIN, INT_MAX]
の範囲のint
です。
std::vector<int>
(重複なし)またはstd::unordered_set<int>
が与えられ、そのサイズは40、400、4000程度ですが、大きすぎません。与えられたもの?
オーバーフローの心配がない場合は、ゼロ以外のすべてを1で乗算し、積に1を加算できます。しかし、あります。攻撃者のテストケースには、意図的にINT_MAX
を含めることができます。
私は、単純で非ランダムなアプローチを支持しています。何かありますか?
ありがとうございました!
更新:あいまいさを解消するために、重複がないことが保証されている未ソートのstd::vector<int>
を考えてみましょう。だから、私はO(n log(n))よりも良いものがあるかどうか尋ねています。また、テストケースにはINT_MIN
とINT_MAX
の両方が含まれることがあることに注意してください。
入力に含まれていない最初の_N+1
_候補整数を返すことができます。最も単純な候補は、番号_0
_からN
です。これにはO(N)
スペースと時間が必要です。
_ int find_not_contained(container<int> const&data)
{
const int N=data.size();
std::vector<char> known(N+1, 0); // one more candidates than data
for(int i=0; i< N; ++i)
if(data[i]>=0 && data[i]<=N)
known[data[i]]=1;
for(int i=0; i<=N; ++i)
if(!known[i])
return i;
assert(false); // should never be reached.
}
_
ランダムな方法はスペース効率が高くなりますが、最悪の場合はデータのパスを増やす必要があります。
ここでは、ランダムメソッドが実際に非常に効率的です。
決定論的な方法を使用し、サイズnが大きすぎない、たとえば4000であると仮定する場合、サイズm = n + 1
のベクトルx(または少し大きく、たとえば計算を容易にするために4096)、0で初期化されます。
範囲内の各i
に対して、x [array [i] modulo m] = 1を設定します。
次に、単純なO(n) xで検索すると、arrayにない値が提供されます
注:モジュロ演算は、厳密には「%」演算ではありません
編集:ここで4096のサイズを選択すると計算が簡単になると述べました。より具体的には、これはモジュロ演算が単純な&
演算で実行されることを意味します
入力ベクトルの順序を変更できる場合、O(N) timeを使用してO(1)補助スペース次のアルゴリズム[注1](ベクトルに繰り返しデータが含まれている場合も、アルゴリズムは機能します。)
size_t smallest_unused(std::vector<unsigned>& data) {
size_t N = data.size(), scan = 0;
while (scan < N) {
auto other = data[scan];
if (other < scan && data[other] != other) {
data[scan] = data[other];
data[other] = other;
}
else
++scan;
}
for (scan = 0; scan < N && data[scan] == scan; ++scan) { }
return scan;
}
最初のパスでは、範囲k
の後に[0, N)
の範囲にあるk
が見つかった場合、k
の位置に存在することが保証されます。この再配置は、データの損失を避けるためにスワッピングによって行われます。スキャンが完了すると、インデックスと値が同じではない最初のエントリは、配列内のどこからも参照されません。
エントリは以前のインデックスから参照される可能性があるため、このアサーションは100%明白ではない場合があります。ただし、その場合、エントリはインデックスに等しくない最初のエントリにはなりません。これは、以前のエントリがその基準を満たすためです。
このアルゴリズムがO(N)であることを確認するには、6行目と7行目のスワップは、ターゲットエントリがそのインデックスと等しくない場合にのみ発生し、スワップ後、ターゲットエントリはそのインデックスと等しいことに注意してください。したがって、最大N
スワップを実行でき、5行目のif
条件はtrue
で最大N
回です。一方、if
条件がfalseの場合、scan
がインクリメントされますが、これはN
回しか発生しません。したがって、if
ステートメントは最大で2N
回(O(N))回評価されます。
[INT_MIN, 0)
から符号なし整数[INT_MAX, INT_MAX - INT_MIN)
にマッピングすることにより(減算は、結果が得られないCのセマンティクスに従ってではなく、数学的に行われます2の補数では、それは同じビットパターンです。もちろん、これは数字の順序を変更し、「最小の未使用整数」のセマンティクスに影響します。順序を保持するマッピングも使用できます。ランダムなx(INT_MIN..INT_MAX)を作成し、すべてに対してテストします。失敗時にx ++をテストします(40/400/4000の非常にまれなケース)。
ステップ1:ベクトルをソートします。
これはO(n log(n))で実行できます。オンラインでいくつかの異なるアルゴリズムを見つけ、最も好きなアルゴリズムを使用できます。
ステップ2:ベクターにない最初のintを見つけます。
INT_MINからINT_MIN + 40/400/4000に簡単に反復し、ベクトルに現在のintがあるかどうかを確認します。
擬似コード:
SIZE = 40|400|4000 // The one you are using
for (int i = 0; i < SIZE; i++) {
if (array[i] != INT_MIN + i)
return INT_MIN + i;
解決策はO(n log(n)+ n)の意味:O(n log(n) )
編集:何かを求めて編集内容を読むだけbetterよりO(n log( n))、ごめんなさい。
(std::unordered_set<int>
ではなく)std::vector<int>
で整数が提供される場合、unordered_set<int>
に存在しない1つの整数値に到達するまで、整数値の範囲を単純にトラバースできます。 std::unordered_set<int>
はfind()
メンバー関数を介した検索を提供するため、std::unodered_set
の整数の存在を検索するのは非常に簡単です。
このアプローチのスペースの複雑さはO(1)になります。
int
の有効な値lowest(つまり、std::numeric_limits<int>::min()
)で走査を開始すると、std::unordered_set<int>
に含まれていないlowestint
を取得します。
int find_lowest_not_contained(const std::unordered_set<int>& set) {
for (auto i = std::numeric_limits<int>::min(); ; ++i) {
auto it = set.find(i); // search in set
if (it == set.end()) // integer not in set?
return *it;
}
}
同様に、int
のgreatestの可能な値(つまり、std::numeric_limits<int>::max()
)でトラバースを開始すると、lowestint
がstd::unordered_set<int>
:
int find_greatest_not_contained(const std::unordered_set<int>& set) {
for (auto i = std::numeric_limits<int>::max(); ; --i) {
auto it = set.find(i); // search in set
if (it == set.end()) // integer not in set?
return *it;
}
}
int
sが均一ハッシュ関数によってunordered_set<int>
のバケットにマッピングされていると仮定すると、unordered_set<int>
の検索操作は一定の時間で実現できます。実行時の複雑さはO(M)になります。ここで[〜#〜] m [〜#〜]は探している整数範囲のサイズです含まれていない値。 [〜#〜] m [〜#〜]はunordered_set<int>
のサイズの上限です(つまり、あなたの場合M <= 40)。
確かに、このアプローチでは、サイズがunordered_set
のサイズよりも大きい整数範囲を選択すると、unordered_set<int>
に存在しない整数値に遭遇することが保証されます。