2012-04-06 19 views
7

私は構造に値を任意に挿入する高速(O(N)より速い)を可能にするデータ構造(配列のようなもの)を探しています。データ構造は、要素を挿入された形で印刷することができなければなりません。これは、List.Insert()のようなものです(ランダムにアクセスまたは削除する必要がないことを除いて、すべての要素を移動するには遅すぎます)。挿入は常に '配列'のサイズ内になります。すべての値は一意です。他の操作は必要ありません。挿入のための効率的なデータ構造

たとえば、Insert(x、i)が値xをインデックスi(0インデックス)に挿入するとします。その後:

  • インサート(1、0){1}
  • 挿入与える(3、1){1,3}
  • 挿入与える(2、1){1,2,3}を与えます
  • インサート(5、0){5,1,2,3}

を与え、それが最後に{5,1,2,3}をプリントアウトできるようにする必要があります。

私はC++を使用しています。

+0

"array like"はどういう意味ですか? – juanchopanza

+0

データ構造をトラバースする際の複雑さに関する要件はありますか? –

+0

@juanchopanza私は表面上を意味する、それは線形配列のように動作する必要があります。それは私がそれらを挿入した方法で要素を保持する必要があります。 – Peter

答えて

9

skip listを使用してください。別のオプションはtiered vectorである必要があります。スキップリストはconst O(log(n))に挿入を行い、順番に番号を保持します。階層化されたベクトルはO(sqrt(n))に挿入をサポートし、要素を順に印刷することができます。

EDIT:アミットのコメントごとに、私はあなたがスキップリストにk番目の要素を見つけるのです方法を説明します:

各要素についてあなたが次の要素に、あなたが知っている各リンクのリンク上のタワーを持っていますいくつの要素が飛び越えますか?だから、k番目の要素を探して、リストの先頭から始め、k要素以上にジャンプしないリンクを見つけるまで、塔の下に行く。このノードが指すノードに行き、ジャンプした要素の数でkを減らします。やって続けているユーザーは、k = 0あなただけそうのように、ベクトルのマップを使用することができ、C++で

+1

私もスキップリストの行を考えていましたが、任意の場所に要素を挿入した後、アクセスリンクリスト[O(logn)検索を保証する人]をどのように変更するかを詳しく教えてください。それは彼らの多くを変更する必要はありませんでしょうか?私はそれ[skip-list]はここに合うように変更することができると信じていますが、この点は詳しく述べる必要がありますIMO – amit

+0

実際にはスキップリストを実装した方法はありません。これは、均一に分布した高さの新しいノードを挿入すると、要素の高さが完全な高さに近くなるという事実に依存します。このアプローチの償却された複雑さに関するインターネット上の分析があり、それは可能な限り最悪ではないことを示しています。 –

+0

私が理解していないのは、高さではなく指標を変更する方法です。どのように要素がk'thだと言うことができますか?あなたの "キー"がインデックスの場合、リンクされたリストの末尾全体を変更する必要はありませんか? [それは私を心配する高さではない、非決定論的なリンクリストを使用してこの問題をきれいに解決] – amit

1

std::mapまたはstd::vectorをお使いですか?

std::mapをキーとして挿入ランクで使用できます。 vectorにはメンバー関数reserveがあります。

+1

OPはより速く、線形の任意の挿入を望んでおり、ベクトルとマップは両方ともO(n)ではありませんか? – amit

+0

はい、 'i'から' n'までの要素をシフトする必要があるため、 'std :: vector'を' i'の位置に挿入するとO( 'n')になります。 'std :: map'では、キーを更新する必要があるため、同様のことが起こります。 –

+0

@ Yavar:しかし、挿入するたびに、次のすべての要素のインデックスを変更する必要があります。あなたがmap = [(1、a)、(2、b)、(3、c)]を持っていて、位置0にzを追加したいと仮定したら、マップを[ (2、a)、(3、b)、(4、c)]。回避策がある場合は、詳細を述べる必要があります。 – amit

-1

持つまで:

int main() { 
    map<int, vector<int> > data; 
    data[0].push_back(1); 
    data[1].push_back(3); 
    data[1].push_back(2); 
    data[0].push_back(5); 
    map<int, vector<int> >::iterator it; 
    for (it = data.begin(); it != data.end(); it++) { 
    vector<int> v = it->second; 
    for (int i = v.size() - 1; i >= 0; i--) { 
     cout << v[i] << ' '; 
    } 
    } 
    cout << '\n'; 
} 

この版画:ちょうどあなたが望むような

5 1 2 3 

を、挿入はO(log n)です。

+2

次に2番目のインデックスで10を押すと失敗します。 – amit

1

マッピング時間(インデックス、挿入時間)のペアを値に使用することができます。ここで、挿入時間は「自動インクリメント」整数(SQLの場合)です。ペアの順序は、コードで

i < i* or t > t* 

IFF

(i, t) < (i*, t*) 

次のようになります。

struct lt { 
    bool operator()(std::pair<size_t, size_t> const &x, 
        std::pair<size_t, size_t> const &y) 
    { 
     return x.first < y.first || x.second > y.second; 
    } 
}; 

typedef std::map<std::pair<size_t, size_t>, int, lt> array_like; 

void insert(array_like &a, int value, size_t i) 
{ 
    a[std::make_pair(i, a.size())] = value; 
} 
+0

0に300、0で100、200で1を挿入するとします。起こるべきこと:[300]、次に[100 300]、次に[100 200 300]です。しかし実際には何が起こるのですか? '[]'、 '[((0、1)、300)]' '((0、2)、100)、((0、1)、300)]'これまでのところ良いですが、 '(((0、2)、100)、((0,1)、300)、((1,3)、200)]'となります。結論:注文統計なしでは、このタイプのものは通常行うのが難しいです。 –

1

あなたのコメントについて:ある

List.Insert()(すべての要素をシフトしなければならないほど遅い)

リストは値をシフトしないで、挿入する位置を見つけるために繰り返します。あなたの言うことに注意してください。これは私のような初心者に混乱することがあります。

0

デフォルトでGCCに含まれている解決策は、ロープデータ構造です。ここにはdocumentationがあります。通常、長い文字列で作業する場合、ロープが気になります。ここでは文字の代わりにintがありますが、それは同じように動作します。ちょうどintをテンプレートパラメータとして使用してください。 (pairなど)

ここにはdescription of rope on Wikipediaがあります。

基本的には、左右のサブツリーに含まれる要素の数(または注文統計と呼ばれるもの)を維持するバイナリツリーです。これらのカウントは、要素が挿入され、削除されます。これにより、O(lg n)の操作が可能になります。

関連する問題