二重連結リスト二分探索

ヒント: この記事はMeteor 007からの転載です。通常の連結リストが二分探索の効率を達成する理由を知っていますか?
侵害がある場合は、連絡して削除してください


序文

如有侵权 请联系删除 侵害がある場合は、連絡して削除してください 侵害がある場合は、連絡して削除してください
著作権表記:この記事はCSDNブロガー「メテオ007」のオリジナル記事で、CC 4.0 BY-SA著作権契約に従います。転載の場合は、元のソース リンクとこのステートメントを添付してください。
元のリンク: https://blog.csdn.net/qq_33220089/article/details/114641975


1.配列と連結リスト

1.配列

計算機科学では配列データ構造(英: array data structure)を配列(英: Array)と呼び、同じ型の要素の集合で構成されるデータ構造で、連続したメモリを割り当てて格納します。要素に対応する格納アドレスは、要素のインデックスを使用して計算できます。

利点
a. ランダム アクセスの方が高速です (添え字アクセスに基づく)。
b. 実装が簡単で使いやすい。
c, メモリアドレスは連続的です, これはCPUキャッシュに非常に優しいです. 例えば, 高性能キューディスラプターもCPUキャッシュ+アレイアドレスの連続性を利用してパフォーマンスを大幅に最適化します.

短所
a. メモリの連続性は長所にも短所にもなり得ます. メモリが逼迫している場合, 配列は大幅に制限されます.
b. 挿入と削除を行うと、要素の移動 (データ コピー) が発生し、速度が低下します。
c. 配列のサイズが固定されているため、要素の数が大幅に制限され、多くの動的データには適していません。

2.連結リスト

連結リスト (Linked list) は一般的な基本データ構造で、線形リストですが、データを線形順に格納するのではなく、各ノードに次のノードへのポインタ (Pointer) を格納します。順番に格納する必要がないため、連結リストは挿入時に O(1) の複雑さに達することができます。これは、別の線形リスト シーケンス テーブルよりもはるかに高速ですが、ノードの検索またはアクセスに O(n) かかります。特定の番号付きノード時間。

利点
a. リンクされたリスト構造により、コンピュータのメモリ空間を最大限に活用でき、柔軟な動的メモリ管理を実現できます。
b. 他の要素を移動せずに挿入を削除します。
c. 要素のサイズに制限されず、自由に拡張できます。

短所
a. 配列のランダム読み取りの利点が失われると同時に、ノードのポインタ フィールドの増加により、連結リストに比較的大きなスペース オーバーヘッドが発生します。
b. ランダム アクセスの効率は、配列の効率よりも低くなります。

3. 時間複雑度分析

時間計算量と空間計算量を理解する
ランダム アクセス
a. 配列: O(1) b. リンクされたリストはランダム アクセスのランダムな挿入、削除を
サポートしていませんa. 配列: 順序付けられた配列 -> O(n) インデックス、順序付けられていない配列を維持する必要があります - > O(1) b. リンクされたリスト: O(1)


2.ジャンプ台

1.スキップテーブルとは

連結リストの概念は上で述べた. 連結リストのクエリ時間複雑度は: O(n). 順序付けられた連結リストであっても, 連結リストの最初の要素からトラバースして検索する必要がある. 時間複雑度最適化させれば解決策はありますか?このとき、スキップ テーブルが表示されます。

ジャンプ リスト、フルネーム: ジャンプ リスト、コンピュータ サイエンスでは、ジャンプ リストはデータ構造です。n 個の要素を含む順序付けられたシーケンスの検索および挿入操作の平均時間の複雑さは、次のようになります。logn

ログを忘れてしまった方は、これを読めば思い出せます [対数ログとは?

通常の連結リスト構造を描いてみましょう:
ここに画像の説明を挿入
これは通常の単一連結リスト構造です。ノード 58 を見つけたい場合は、1 -> 4 -> 7 -> 15 -> 20 -> 35 -> 50 を照会する必要があります。 -> 58、必要な結果を見つけるのに合計 8 回、時間の複雑さ: O(n)、効率は想像できるので、一緒に最適化してみましょう。
Mysql のクエリ効率は、データ量が多いと急激に低下します.どのように効率を最適化しますか? それがインデックスです。ここでは、「インデックス」のようなものを使用して、この通常の単一リンク リストを最適化することもできます。
ここに画像の説明を挿入
データ検索を容易にするインデックスを作成するために、元のリンクされたリストの 2 つのノードごとにノードをコピーします

引き続き 58 を探します. 1 -> 7 -> 20 -> 50 -> 58 とたどればよいのですが, 見つかりましたか? たった 5 回の検索で目的のノードが見つかりました. 効率的ですか?少し改善?
この図を説明しましょう. インデックスのレイヤーを確立したので、クエリを実行するときは、まずインデックス レイヤーの最初のレイヤーを検索します. インデックス レイヤーを検索すると、50 と前のノードがすべて 58 未満であることがわかりました. 50 にトラバースするとき、彼の次のノード 66 は照会するノードよりも大きいため、この時点で、ノード 50 のダウン ノード (元のリンク リスト ノードは 50 のポインタ) が見つかり、次にトラバースします。 58 が見つかります。

「インデックス」を追加してもクエリは 3 つしか節約できず、最適化は明らかではありません。この最適化は必要ですか? ここで「索引」の層を確立しましたが、「索引」の層をあと何層構築すればよいでしょうか。それはどのようになりますか?

ここに画像の説明を挿入
現時点では、必要なノード 58 を見つけるのに 4 回しかかからないことがわかりました. 4 回は 8 回の半分の速度です. ここにはあまりデータがありません. 十分なデータがある場合, 効果より明白になります。

レイヤーごとのインデックスで構成されるこのデータ構造を次のように跳表呼んでいます:
インデックス層を構築するためにはどうしても容量を消費してしまうので、これは時間と空間を交換する方法ですが、データ量が多いと容量が足りなくなるのでしょうか?

ジャンピング テーブルの時間/空間の複雑さを分析してみましょう。

2.テーブル分析をスキップ

元の連結リストのノード数が n であるとすると、2 ノードごとに 1 つのノードをインデックス レイヤーのノードとして抽出することがわかっているため、最初のインデックス レイヤーのノードの数が元の連結リスト ノードになるはずです。数の 1/2、つまり n/2、第 2 レベルのインデックスのノード数は第 1 レベルのインデックスのノード数の 1/2、つまりノード数の 1/4 です。元の連結リスト、つまり n /4 では、第 3 層のインデックスのノード数は第 2 層のノード数の 1/2、第 1 層のインデックスのノード数の 4/1 です。元の連結リストのノード数の 1/8 など、レイヤー k でインデックス付けされたノード数はレイヤー k-1 でのインデックスの 1/2 であり、レイヤー k でインデックス付けされたノード数:
ここに画像の説明を挿入
ここに画像の説明を挿入

対数で底 2 がないのはなぜですか? 実際、時間複雑度分析では、分析はトレンドにすぎず、固定値ではありません. 相対数レベル、基数は無視できます. 具体的な関連紹介はここでは紹介しません. 興味がある場合は、 Baidu it, 複雑ではありません. 今把握する必要があるのは、この x はいくらですか?

上記のジャンプ テーブル構造図では、2 つのノードごとにインデックス ノードを抽出します。そのため、ジャンプ テーブル内の特定のノードをクエリする必要がある場合、インデックス レイヤーを上から下にトラバースする必要があり、各レイヤーはそれを超えることはありません。多くても 3 つですが、なぜ 4、5、6 ではなく 3 なのですか? 簡単な図を描いて説明しましょう。

ここに画像の説明を挿入
ノード 12 を見つける必要があります.ノード 9 に到達すると、12 > 9 && 12>11 とわかります.このとき、彼は次のノード 11 of 9 と 11 < 12 && 12<13 を判断します。 1 レベル インデックスを下って最初のレイヤー インデックスに到達し、9 ~ 13 の間にノードが 3 つしかないため、元の連結リストに降りても、2 つの範囲ノードの前に最大で 3 つのノードしかないため、各レイヤーx は直接省略できる定数 3 であるため、ノードの最大数はこの方法で取得されます。したがって、ジャンプ テーブルの最終的な時間計算量は次のようになります。log(n)

3.ジャンプテーブルがメモリを占有する

クエリの効率は大幅に改善されましたが、非常に重要な問題もあります。つまり、ジャンプ テーブルには多くのレイヤーのインデックスが必要なため、追加のメモリ スペースが必要になるため、このデータ構造は推奨する価値がありません。redis が正式に使用されるようになった今、まだ恐れていることは何ですか?

ジャンプ テーブルのスペースの複雑さをもう一度分析して、大量のメモリを消費するかどうかを確認してみましょう。

元の連結リストの長さを n とすると、最初のレイヤーのインデックスの長さは n/2、2 番目のレイヤーのインデックスの長さは n/4、3 番目のレイヤーのインデックスの長さは n/8、および最後のレイヤー (最上位レイヤー) インデックスの長さ: 2 、これが幾何学的シーケンスであることは明らかです:
ここに画像の説明を挿入
インデックス レイヤーが占める合計サイズは: n - 2 に元の連結リスト n = 2n -2 を加えたものです。したがって、スキップ リストのスペースの複雑さは次のとおりです: 、O(n)定数は省略できます。これはそれぞれ 2 つのノードからインデックス ノードを抽出するスペースの複雑さです。インデックス ノードを抽出するにはあと何個のノードがありますか? たとえば、3 ごとに 1 が抽出され、5 ごとに 1 が抽出されます。計算方法は上記と同じであり、結果のスペースの複雑さも になりますが、スペースは実際には大幅に削減されますO(n)

クエリが終了したので、ジャンプ テーブルの挿入と削除の効果はどうでしょうか。性能も良いでしょうか?一緒に見てみましょう。

リンク リストは順序付けられているため、挿入するときは最初に挿入位置を見つける必要があります. ジャンプ テーブルのクエリ時間の複雑さは、挿入操作である挿入位置を見つけた後、単一のリンク リストの挿入時間の複雑さはO(logn)O(1)、ここでは証明しませんので、挿入プロセス全体は、挿入位置を見つける + 挿入 = と同じですO(logn)

実際、削除操作の時間の複雑さも O(logn) ですが、なぜでしょうか?
実際には挿入と同じですが、削除すると前後のノードのポインタが変化するため、前のノードを見つける操作が追加されます. 後者のノードは現在削除されているノードに存在しますが、クエリもO(logn)なので、O(logn)+O(logn)を削除するので、削除の時間計算量は最終的にO(logn)ですが、削除に関して注意すべき点が1つあります。 、削除するノードがインデックスレイヤーにも存在する場合、インデックスレイヤーのノードも同時に削除する必要があり、先行ノードの問題は、二重リンクリストを使用して解決できます

4.インデックス更新

ジャンプテーブルのクエリも削除も追加もすべて性能的に優れているので、このままでいいのだろうか。ジャンプ テーブルはデータの格納にも使用されるため、間違いなく頻繁な追加と削除が伴います. 隣接する特定の間隔のインデックス ノード間にさらに多くのデータが挿入されると仮定すると、データの分布が不均一になります. 均等に, クエリ時間ある間隔での効率が低下し、最悪の場合、一重リンクのリストに退化する可能性があります.すべてのデータはこの間隔にあるため、ジャンプを確実にするために、データが挿入されたときにインデックスレイヤーをリアルタイムで更新および維持します.テーブルのデータ構造が過度に縮退することはないので、インデックス レイヤーで変更を維持するにはどうすればよいでしょうか。

実は難しいことではありません. ジャンプテーブルはランダム関数を使用してデータのバランスを維持します, つまり、データを比較的均一に保ちます. ジャンプテーブルにデータを追加すると、ランダム関数によって乱数が生成されます.関数. この乱数はインデックスです. レイヤーの数, 次に、追加するノードをこのレイヤーのインデックス レイヤー ノードに追加します, 挿入する必要があるデータが 7 であると仮定し、ランダムによって生成された乱数function is: 2 の場合、挿入は次のようになります。図に示すように:

ここに画像の説明を挿入
ここで、ランダム関数の要件は非常に高く、ジャンプ テーブルのバランスを十分に確保することができ、ジャンプ テーブルのパフォーマンスを低下させることはありません.これは、左手/右手の赤黒ツリーと同じです. .

要約する

1.スキップテーブルとは

ずっとスキップ リスト: ジャンプ リスト コンピュータ サイエンスでは、ジャンプ リストはデータ構造です。n 個の要素を含む順序付けられたシーケンスの検索および挿入操作の平均時間の複雑さは、次のようになります。logn

2. ジャンプ リストの時間/空間の複雑さ

時間の複雑さ: lO(logn)l、空間の複雑さ: lO(n)l、空間の複雑さはインデックス間隔の距離の増加とともに減少しますが、空間の複雑さの傾向は依然としてlO(n)l です。これは、インデックス レイヤーに格納されている情報が元のリンクされたリストに関連しているためです。はるかに少ないので、O(n) のスペースの複雑さでさえ許容されます。

3. スキップテーブルの適用

  1. redis の順序付きコレクション。
  2. Google のオープンソース キー/バリュー ストレージ エンジン。
  3. HBase MemStore 内部ストレージ構造。

4.ジャンプリストの動的更新

スキップテーブルは多層インデックスで構成されているため、頻繁に挿入すると、一方の端に隣接するインデックスノードの前にデータが過剰になり、最悪の場合、すべてのデータが特定のセグメントに集中します。これにより、ジャンプ リストが通常の連結リストに縮退し、時間計算量も O(n) に縮退するため、このとき、ジャンプ リスト内のインデックス ノードを動的に更新する必要があります。ランダム関数の実装を通じて。

5. redis が順序付きコレクションを実装するためにジャンプ リストを使用するのはなぜですか

  1. ジャンプリストは連結リスト+多段索引からなるデータ構造で、時間と空間を交換するという設計思想により、連結リストに基づく「二分探索」を実現し、検索・削除・挿入の時間計算量はO(ログ)。
  2. Bツリー、赤黒ツリー、AVLツリーなどと比較して、スキップテーブルの実装ははるかに簡単です.これらのタイプのツリーのバランス維持は非常に面倒ですが、スキップテーブルの動的インデックス更新は比較的簡単です. .
  3. ジャンプ テーブルの区間検索は、赤黒木よりも優れています。

侵害がある場合は、連絡して
著作権ステートメントを削除してください。この記事は CSDN ブロガー "Meteor 007" のオリジナル記事です。CC 4.0 BY-SA 著作権契約に従い、元のソース リンクとこのステートメントを添付して転載してください。
元のリンク: https://blog.csdn.net/qq_33220089/article/details/114641975

おすすめ

転載: blog.csdn.net/packge/article/details/126803650