多くの(ほとんどの)データ構造の挿入よりも、削除の実装が通常はるかに難しい特定の理由を考えることができますか?
簡単な例:リンクリスト。挿入はささいなことですが、削除にはいくつかの特殊なケースがあり、それが非常に難しくなります。AVLやRed-blackなどの自己バランス型バイナリ検索ツリーは、痛みを伴う削除の実装の典型的な例です。
私はそれがほとんどの人々が考える方法に関係していると言いたい:私たちが物事を建設的に定義することはより簡単であり、それはうまく挿入を容易に導く。
多くの(ほとんどの)データ構造の挿入よりも、削除の実装が通常はるかに難しい特定の理由を考えることができますか?
簡単な例:リンクリスト。挿入はささいなことですが、削除にはいくつかの特殊なケースがあり、それが非常に難しくなります。AVLやRed-blackなどの自己バランス型バイナリ検索ツリーは、痛みを伴う削除の実装の典型的な例です。
私はそれがほとんどの人々が考える方法に関係していると言いたい:私たちが物事を建設的に定義することはより簡単であり、それはうまく挿入を容易に導く。
回答:
それは単なる心の状態以上のものです。削除が難しい物理的な(つまりデジタルの)理由があります。
削除すると、以前は何かがあった場所に穴が残ります。結果のエントロピーの技術用語は「フラグメンテーション」です。リンクされたリストでは、削除されたノードを「パッチ処理」して、使用中のメモリの割り当てを解除する必要があります。バイナリツリーでは、ツリーの不均衡が発生します。メモリシステムでは、新しく割り当てられたブロックが削除によって残されたブロックよりも大きい場合、メモリがしばらく使用されなくなります。
つまり、挿入する場所を選択できるため、挿入が簡単になります。どのアイテムが削除されるかを事前に予測できないため、削除はより困難になります。
なぜ挿入するよりも削除するのが難しい傾向があるのですか?データ構造は、削除よりも挿入を念頭に置いて設計されているため、当然です。
これを考慮してください-データ構造から何かを削除するには、そもそもそこにある必要があります。そのため、最初に追加する必要があります。つまり、最大で挿入と同じ数の削除を行うことができます。データ構造を挿入用に最適化すると、少なくとも削除用に最適化された場合と同じくらいの利益が得られることが保証されます。
さらに、各要素を順番に削除するのにどのような用途がありますか?一度にすべてをクリアする関数を(単に新しい関数を作成して)呼び出すだけではどうでしょうか?また、データ構造は、実際に何かを含む場合に最も役立ちます。したがって、実際には、挿入と同数の削除を行うケースはあまり一般的ではありません。
何かを最適化するとき、あなたはそれが最もすることと最も時間がかかることを最適化したいです。通常の使用法では、データ構造の要素の削除は挿入よりも頻繁に発生しません。
k
要素のバッチを非常に高速に追加できます。並べ替え入力を逆にし、既存のベクターとマージします- O(k log k + n)
。次に、かなり複雑な挿入の構造がありますが、上位u
要素の消費は簡単で高速です。u
最後に、ベクトルの終わりを移動するだけです。しかし、誰かがそのようなことを必要とするなら、私は気の毒に思うでしょう。これが少なくともあなたの議論を強化することを願っています。
難しくありません。
二重リンクリストを使用すると、挿入時にメモリが割り当てられ、ヘッドまたは前のノードのいずれか、およびテールまたは次のノードのいずれかとリンクします。削除すると、まったく同じものからリンクが解除され、メモリが解放されます。これらの操作はすべて対称です。
これは、どちらの場合でも挿入/削除するノードがあることを前提としています。(また、挿入の場合、前に挿入するノードもあるため、ある意味、挿入はやや複雑であると考えることができます。)削除するノードではなくペイロードを削除しようとする場合ノードの場合は、もちろん最初にペイロードのリストを検索する必要がありますが、それは削除の欠点ではありませんか?
バランスの取れたツリーでも同じことが当てはまります。通常、ツリーは挿入直後と削除直後にバランスを取る必要があります。バランスルーチンを1つだけ試してみて、それが挿入であるか削除であるかに関係なく、各操作の後に適用することをお勧めします。ツリーのバランスを常に維持する挿入と、ツリーのバランスを常に維持する削除を実装しようとしている場合、2つが同じバランスルーチンを共有することはないため、不必要に複雑になります。
要するに、一方が他方よりも硬くなるべき理由はありません。もしあなたがそれを見つけているなら、あなたが考えるのがより自然であるという(非常に人間的な)傾向の犠牲者である可能性があります。つまり、必要以上に複雑な方法で削除を実装している可能性があります。しかし、それは人間の問題です。数学的な観点からは、問題はありません。
ランタイムに関しては、Wikipedia のデータ構造操作の時間の複雑さの比較を見て、挿入操作と削除操作の複雑さは同じであることに注意してください。プロファイルされた削除操作は、インデックスによる削除です。削除する構造要素への参照があります。挿入はアイテムごとです。実際には、削除の実行時間が長くなるのは、通常はインデックスではなく削除するアイテムがあるため、検索操作も必要だからです。テーブル内のほとんどのデータ構造では、配置位置がアイテムに依存していないか、挿入中に暗黙的に位置が決定されるため、挿入の追加の検索は必要ありません。
認知の複雑さに関しては、質問に答えがあります:エッジケース。削除は挿入よりも多くの可能性があります(これは一般的なケースではまだ確立されていません)。ただし、特定の設計では、これらのエッジケースの少なくとも一部を回避できます(リンクリストにセンチネルノードがあるなど)。
上記のすべての問題に加えて、データ参照の整合性が関係しています。SQLのデータベースのようなデータ構造を最も適切に構築するには、Oracleの参照整合性が非常に重要です。
誤って多くの異なるものが破壊されないようにするために。
たとえば、削除しようとするものを削除するだけでなく、関連データのクリーンアップもトリガーする削除時のカスケード。
これにより、ジャンクデータからデータベースをクリーンアップし、データの整合性を維持します。
たとえば、2番目のテーブルの関連レコードとして、親と種類を持つテーブルがあります。
親はメインテーブルです。参照整合性を強化していない場合、任意のテーブルのレコードを削除できますが、後で子テーブルにデータがあり、親テーブルには何もないため、完全な家族情報を取得する方法がわかりません。
そのため、参照整合性チェックでは、子テーブルのレコードがクリーンアップされるまで、親テーブルからレコードを削除できません。
そして、それがほとんどのデータソースでデータを削除することがより難しい理由です。
pop
、extract-min
?