ソートされた配列に要素を追加する


31

これを行うための最速の方法は何ですか(アルゴリズムの観点からも、実際的な問題からも)?

私は次の線に沿って何かを考えていました。

配列の末尾に追加して、これに近いベストケース(開始時に完全にソートされた配列)があり、実行時間が線形である(最適な場合)ので、バブルソートを使用できます。

一方、ソートされた配列から開始することがわかっている場合、バイナリ検索を使用して、指定された要素の挿入ポイントを見つけることができます。

私の考えでは、2番目の方法はほぼ最適ですが、そこに何があるのか​​興味があります。

どうすればこれを最適に行うことができますか?


1
頻繁にやらなければならない場合の最速の方法は、そもそも配列を使用しないことです。
reinierpost

あなたが意味する自己平衡二分木?
-soandos

はい、おそらく。答えをご覧ください...
reinierpost

回答:


25

配列要素の読み取りと書き込みの数をカウントします。バブルソートを行うには、アクセスが必要です(最初の書き込みから最後への書き込み、最悪の場合、n回のスワップを行うために2回の読み取りと2回の書き込み)。バイナリ検索を行うために、我々は必要な2 ログのn + 2 のn + 12 ログnは最悪の場合には、バイナリサーチのため、、2をn個に配列要素を記述するために、その後1、右側に配列要素をシフトしますその適切な位置)。1+4nn2ログn+2n+12ログn2n

したがって、両方の方法は配列の実装に対して同じ複雑さを持ちますが、バイナリ検索方法では、長期的には配列へのアクセスが少なくなります...漸近的には半分になります。当然、他の要因もあります。

実際には、より良い実装を使用して、実際の配列アクセスのみをカウントできます(挿入される要素へのアクセスはカウントしません)。バブルソートにを実行し、n + 2 nログに記録できます2n+1端から検索し、道(賢く沿って移動させるので、レジスタ/キャッシュ・アクセスが安価であると配列アクセスが高価であれば...バイナリ検索挿入のためのバブルソート)はより良いかもしれませんが、漸近的にそうではありません。ログn+2n+1

より良いソリューションには、異なるデータ構造の使用が含まれる場合があります。配列はO(1)アクセス(ランダムアクセス)を提供しますが、挿入と削除にコストがかかる場合があります。ハッシュテーブルにはO(1)の挿入と削除があり、アクセスにコストがかかります。他のオプションには、BSTやヒープなどがあります。挿入、削除、およびアクセスのためのアプリケーションの使用ニーズを検討し、より特殊な構造を選択することは価値があります。

また、n個の要素の並べ替えられた配列に要素を追加する場合、m個のアイテムを効率的に並べ替えてから、2つの配列をマージすることをお勧めします。また、ソートされた配列は、たとえばヒープを使用して効率的に構築できます(ヒープソート)。mnm


1
「ハッシュテーブルにはO(1)の挿入と削除が含まれる可能性があります」-通常は償却されます。
ラファエル

8
予想される償却。
-JeffE

BSTには、検索と挿入(wikipedia)のためにがあるので、なぜここで一番の推奨選択ではないのですか?O 2 l o g n 検索および挿入します。Olog nO2 log n
Kashyap

8

ヒープを使用しない理由がある場合は、バブルソートではなく挿入ソートの使用を検討してください。並べ替えられていない要素がいくつかある場合に適しています。


8

配列を使用しているため、アイテムの挿入にはかかります-配列の中央に何かを追加する場合、たとえば、配列がソートされたままになるように、その後のすべての要素を1つシフトする必要があります。On

アイテムを置く場所を見つける最も速い方法は、あなたが言ったように、であるバイナリ検索です。したがって、合計の複雑さはO n + lg n になります。O n OlgnOn+lgnOn

O1、説明では配列が示されていないため、単に配列の最後にそれを叩くだけで新しい要素を挿入した後、ソートされたままにする必要があります。

とにかく、この問題を解決するためにバブルを抜く理由は見当たりません。


2
にとどまることはあまり役に立ちません Oすべてが線形時間を要するアルゴリズムを比較する場合のレベル。
ラファエル

陰気であるために+1 .. :-)
Kashyap

4

Patrick87はこれを非常によく説明しました。ただし、追加できる最適化の1つは、循環バッファーのようなものを使用することです。通常どおり、挿入された要素の位置の右側に項目を移動できます。ただし、アイテムを左の正しい位置から左に移動することもできます。これを行うには、配列を循環として扱う必要があります。つまり、最後のアイテムは最初のアイテムの直前にあり、アイテムが現在開始しているインデックスを保持する必要があります。

これを行うと、配列アクセスの約半分を行うことになります(挿入するインデックスの均一な分布を想定)。位置を見つけるためにバイナリ検索を行う場合、左にシフトするか右にシフトするかを選択するのは簡単です。バブルソートの場合、開始する前に正しく「推測」する必要があります。しかし、それは簡単です。挿入された項目を配列の中央値と比較するだけです。これは、単一の配列アクセスで実行できます。


4

この問題に対して挿入ソートアルゴリズムを効果的に使用しました。一時的にハッシュテーブルオブジェクトのパフォーマンスの問題が発生したときに、パフォーマンスを大幅に向上させる代わりにバイナリ検索を使用する新しいオブジェクトを作成しました。リストをソートしたままにするために、検索要求のためにリストをソートする必要がある場合、最後のソート以降に追加されたアイテムの数(ソートされていないアイテムの数)を追跡し、挿入ソートまたはクイックソートを実行します未分類のアイテムの割合。挿入ソートの使用は、パフォーマンスを向上させる上で重要でした。


償却後の運用コストに関して正式な結果はありますか?そして:ようこそ!
ラファエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.