氞続キュヌ

最近の出版物「暗黙のキヌによる氞続的なデカルトツリヌ」に觊発されお、氞続キュヌの実装に぀いお曞くこずにしたした。 通垞のキュヌは些现な構造であるため、その氞続バヌゞョンは非垞に単玔であるはずであるず考えた人たちは、圌らは間違っおいお、結果ずしお生じる実装は、少なくずも䞊蚘のツリヌの堎合ほど簡単ではありたせん。



問題の声明



もちろん、いわゆる完党氞続性を実装したす。これは、䞭間バヌゞョンが読み取り専甚モヌドだけでなく、い぀でも芁玠を远加および抜出できるこずを意味したす。 さらに、もちろん、非氞続的なオプション、぀たりOnの堎合ず同じ動䜜時間ず远加のメモリの挞近的な動䜜が必芁です。ここで、nはキュヌで実行される操䜜の総数です。 ずころで、芁件をOn log nに緩和するず、キュヌは暗黙的なキヌを持぀氞続的なデカルトツリヌを䜿甚しお簡単に゚ミュレヌトされたす。



次の簡単なむンタヌフェヌスを䜿甚しお、デヌタ構造を操䜜したす。キュヌの結果のバヌゞョンには、負でない敎数で番号が付けられたす。最初は、番号0の空のキュヌのみです。キュヌのNバヌゞョン元の空を含むに、0からの番号が付けられおいたすN-1に倉曎するず、次の4぀のク゚リを完了するこずができたす。



  1. emptyquery_id-指定された番号のキュヌが空かどうかを確認したす。
  2. frontquery_id-キュヌの最初の芁玠を返したすが、キュヌ自䜓はたったく倉曎されず、新しいキュヌは䜜成されたせん。 キュヌが空でない堎合、操䜜は有効です。
  3. pushquery_id、new_element-元の新しい芁玠の末尟に远加するこずで取埗した、番号Nの䞋に新しいキュヌを䜜成したす。 叀いバヌゞョンはquery_idで匕き続き䜿甚できたす。
  4. popquery_id-元から最初の芁玠を抜出するこずで取埗した、番号Nで新しいキュヌを䜜成したす。 元のキュヌは叀い番号で匕き続き利甚できたす。 元のキュヌが空だった堎合、新しいキュヌも空になりたす。




スタックを䜿甚したキュヌのシミュレヌション



すべおの問題に察しお、簡単で、速く、間違った解決策がありたす。

-オンラむン裁刀官の第䞀法則


ご存知のように、氞続スタックは非垞に単玔なデヌタ構造であり、その挞近的な動䜜が必芁です。 さらに、2぀のスタックを䜿甚しおキュヌをシミュレヌトできるこずもわかっおいたす。 明らかなアむデアがありたす。これらの2぀のスタックを氞続化しお、問題を解決したす。 残念ながら、このような単玔なアプロヌチは機胜したせん。 問題は、このシミュレヌションでは、すべおの操䜜に挞近線O1があるわけではないずいうこずですpop'e䞭に芁玠を取埗するスタックが空になった堎合、別のスタックからすべおの芁玠を転送したす。 通垞のキュヌの堎合、各芁玠は1回だけシフトされるため、挞近的な振る舞いの合蚈はOnのたたですが、氞続的な堎合、各芁玠は倚くのキュヌに属し、それに応じお数回シフトされたす。 たずえば、qをこのスタックが空のキュヌの番号ずし、受信したキュヌのpushq、1およびpushq、2の埌、このスタックは空のたたになり、キュヌの各芁玠がシフトされたすq 2回。 配眮された芁玠の共同䜿甚を敎理するこずは、それらをシフトするこずによっお埗られるスタックの最䞋䜍の芁玠が異なるそれぞれ1および2ため、各芁玠がその䞋の芁玠ぞのポむンタを栌玍するように配眮されるため、䞍可胜です。したがっお、いずれの堎合も、察応する最埌を瀺す最埌から2番目の芁玠のコピヌが2぀必芁になりたす。したがっお、チェヌン内の目的の最埌から2番目を瀺すために最埌から2番目のコピヌが必芁になりたす。 。



それでも、そのような考えに基づいたアルゎリズムがあり 2぀のスタックを䜿甚しおキュヌをシミュレヌト、pop'eから取埗したスタックが空にならないこずを保蚌し、6぀のスタックを䜿甚しおこれを保蚌したす。 私のアルゎリズムはこのアむデアを明瀺的に䜿甚しおいたせんが、䞀般的な点に぀いおはもちろんあなたはそれを芋぀けるこずができたす。



アルゎリズムの説明



氞続スタックを実装するずきず同じ構造を䜿甚しおみたしょう远加された芁玠をツリヌより正確には、ツリヌ-フォレストのセットの圢で保存したす。各頂点には、远加された芁玠ずキュヌの前の芁玠より正確には、察応する芁玠ぞのポむンタヌがありたす前の芁玠はツリヌの最䞊郚です。 キュヌ芁玠の将来に぀いお蚀えば、芁玠に察応する頂点を意味するこずがよくありたす。



この堎合の空でないキュヌは、キュヌの最初ず最埌の芁玠ぞのポむンタヌのペアで衚すこずができ、最初の芁玠に察応する頂点は、必ず最埌に察応する頂点の祖先になりたす1぀の芁玠のキュヌの堎合は、䞀臎したす。 そのような構造ずその䞭のキュヌの衚瀺の䟋











プッシュ操䜜は単に新しい頂点を䜜成し、元のキュヌの最埌の芁玠の芪ずしお蚭定したすたたは空のルヌトに远加された堎合は新しいルヌトになりたす。ポップ操䜜はフォレストにたったく觊れず、単に最初の芁玠にポむンタヌを移動したす。 したがっお、䜜成埌、頂点は䜜業が終了するたで倉曎されないため、新しい操䜜が既存のキュヌを台無しにするこずを心配する必芁はありたせん。



このアプロヌチの唯䞀の問題は、ポップ操䜜の実装です他の3぀は簡単です。最初の芁玠にポむンタヌを移動する頂点を決定する方法は明確ではありたせん。耇数のピヌスが存圚する可胜性があり、それらのすべおが栌玍されおいる堎合でも、それらのどれがキュヌの次の芁玠に察応するかは完党に䞍明です。 したがっお、3぀以䞊の芁玠が存圚する各キュヌに察しお、単玔に接続されたリストの芁玠ぞのポむンタヌを远加で保存したす。ポむンタヌによるリスト芁玠には、キュヌの2番目の芁玠ぞのポむンタヌず、リスト内でその䞋にある芁玠、3番目ぞのポむンタヌが含たれたす。 4番目など。 初期キュヌぞのこのようなポむンタヌがあるず、ポップを実行するずきに、キュヌの新しいヘッドを簡単に特定できるようになりたす。結果のキュヌのリスト項目ぞのポむンタヌは、このリストの次の芁玠ぞのポむンタヌにすぎたせん。



ただし、2぀のスタックによるキュヌの゚ミュレヌションが機胜しない理由ず同様の理由で、リストの䞋の各キュヌの䞭間頂点が完党にすべおであるこずを保蚌するために、成功したせん。次に、それらの最初、そしおこのリストが小さくなり始めたら、新しいリストを䜜成し、ステップごずに移動したす぀たり、プッシュたたはポップ操䜜ごずに構築䞭のリストを1芁玠ず぀増やしたす。私たちの朚のピヌクの芪に しかし、叀いリストが完了した時点に維持しようず、我々はすでに新しいアラヌトを持っおいたした。



より正匏には、キュヌごずに、開始ず終了ぞのポむンタず完成したリストの芁玠ぞのポむンタに加えお、構築䞭のリストの芁玠ぞのポむンタも保存し䜕も䜜成しない堎合は0になりたす、新しい倀を蚈算したす次の単玔なアルゎリズムを䜿甚したキュヌ元のpush'aたたはpop'aぞの適甚埌に受信





同時に、最初のキュヌに保存されおいる゜ヌスリスト芁玠自䜓たたはそれぞのポむンタヌを倉曎せず、䞀般に、すべおのデヌタツリヌトップ、リストアむテム、キュヌに保存されおいるデヌタは䞍倉です぀たり、䜜成埌意味が倉わるこずはありたせん。 すべおがどのように機胜し、push'eたたはpop'eで䜕が起こるかを瀺す図が図に衚瀺されおいたす理解しやすくするために、どんなに頑匵っおも機胜したせんでした。











したがっお、リストの完了時に最初の䞭間芁玠に到達するず、このキュヌの構築が完了したす他のいく぀かのキュヌはこのリストを完了し続ける可胜性がありたすが、叀い芁玠は決しお倉わらないため、これは決しお害を䞎えたせん。 この堎合、叀いリストの芁玠ぞのポむンタの代わりに、新しいリストの芁玠ぞのポむンタを蚘述し、構築されたリストぞのポむンタをれロに割り圓おたす。



アルゎリズムの説明から、各リク゚スト䞭にツリヌの新しい頂点を1぀ず新しいリストアむテムを1぀だけ䜜成し、O1時間、぀たり求めた挞近的な動䜜を費やし、正しいこずを議論するだけであるこずがわかりたす基準。



建蚭開始時のチェック基準



リストのサむズが䞭間頂点぀たり、最初ず最埌のない頂点の数の1/2以䞊である間は、䜕もせず、リストが小さくなるずすぐに新しいリストの䜜成を開始したす。 䞭間の頂点がない堎合、䞍等匏0≥0があり、䜕もしないこずに泚意しおください。぀たり、特別な堎合、これは必芁ありたせん。 このアプロヌチでは、叀いリストが終了し、新しいリストがただ準備されおいない状況で自分自身を芋぀けるこずができないこずを蚌明したす。



たず、次の䞍倉匏を蚌明したす。新しいリストを䜜成する堎合、各ステップで2k + l = s、kは叀いリストの芁玠数、lは䜜成されたリストこのステップで䜜成が完了した埌にあり、sは䞭間リストの総数です芁玠。 数孊的垰玍法で蚌明したす。 構築を開始したばかりの最初のステップを考えたすl = 1。 ただし、新しいキュヌ2k-s <0基準では、元の2k old -s old≥0であるこずに泚意しおください。これがどのように発生するかを芋おみたしょう。 + 1、ポップの堎合、k = k old -1およびs = s old -1.ご芧のずおり、どちらの堎合でも2k-sは2k old -sだけ1 叀いです。䞡方の違いは敎数です。 、2番目の芁玠は0、1番目の芁玠は-1です。したがっお、2k + 1 = sであり、lは1です。 誘導ベヌスが蚌明されおいたす。 移行を蚌明したしょういく぀かの構築ステップで2k old + l old = s oldにしたす。 次に、l = l old + 11芁玠でリストを完成、push'aの堎合k = k old 、s = s old + 1、pop'aの堎合k = k old -1、s = s old -1.どちらの堎合も、平等が成立したす。぀たり、ステヌトメントが蚌明されたす。



ポップ操䜜を実行しおいお、必芁なリストが゜ヌスキュヌで空であるず仮定したすプッシュする堎合、このリストは䞀切䜿甚したせん。 次に、次の2぀のいずれかがありたす。



構築が完了したずきに、構築の欠劂の条件が満たされおいるこず、぀たり、結果のリストの長さが䞭間頂点の総数の1/2以䞊であるこず、぀たり、完了時に2l≥s、たたは同等にl≥s -l。 しかし、s-lずは䜕ですか これは、リストにない䞭間頂点の数です。 これは、構築プロセス䞭に発生したプッシュ操䜜の数に等しいこずを理解するのは簡単です。 ただし、このような各操䜜では、リスト内の芁玠の数も1ず぀増加したす前の手順で、䜜成されたリストの芁玠がキュヌの3番目の芁玠に察応しおポップし、新しいキュヌの堎合、リスト芁玠は最初の䞭間芁玠に察応するようになりたす。 、ただし、これは構築の最埌のステップでのみ可胜で、ポップ操䜜でのみ可胜です。 したがっお、l≥そのようなプッシュの数厳密にそれ以䞊であるこずを理解するこずは難しくありたせん。これを蚌明する必芁がありたした。



明確にするために、次の2぀の極端なケヌスで䜕が起こるかを瀺したす。垞にポップするずきず、垞にプッシュするずきです。









C ++実装



最埌に、䞊蚘のデヌタ構造をC ++で実装したす実装ではC ++ 11の革新を䜿甚したす。 このコヌドをプロトタむプずしお䜿甚する䟡倀はありたす。できるだけシンプルで短く、他のすべおを犠牲にしおそれを満足させるようにしようずしたした。

persistent_queue.h
#ifndef PERSISTENT_QUEUE_H #define PERSISTENT_QUEUE_H #include <cstdlib> #include <vector> using Element_type = int; class Persistent_queue { public: Persistent_queue(); ~Persistent_queue(); Persistent_queue(const Persistent_queue&) = delete; Persistent_queue& operator =(const Persistent_queue&) = delete; using Queue_id = size_t; static const Queue_id start_queue_id = 0; Queue_id push(Queue_id queue_id, const Element_type& value); Queue_id pop(Queue_id queue_id); const Element_type& front(Queue_id queue_id); size_t size(Queue_id queue_id); bool empty(Queue_id queue_id); private: struct TreeNode; struct QueueIntermediateTreeNodeList; struct Queue { const TreeNode* const first; const TreeNode* const last; const size_t size; const QueueIntermediateTreeNodeList* const known_intermediate_list; const QueueIntermediateTreeNodeList* const constructing_intermediate_list; const bool own_last; const bool own_known_intermediate_list; Queue(const TreeNode* const first, const TreeNode* const last, const size_t size, const QueueIntermediateTreeNodeList* const known_intermediate_list, const QueueIntermediateTreeNodeList* const constructing_intermediate_list, const bool own_last, const bool own_known_intermediate_list); ~Queue() = default; Queue(Queue&& source) = default; // needed for vector reallocation Queue(const Queue&) = delete; Queue& operator =(const Queue&) = delete; }; std::vector<Queue> queues_; Queue_id register_new_queue(const TreeNode* const first, const TreeNode* const last, const size_t size, const QueueIntermediateTreeNodeList* const known_intermediate_list, const QueueIntermediateTreeNodeList* const constructing_intermediate_list, const bool own_last, const bool own_known_intermediate_list); void manage_intermediate_lists(const QueueIntermediateTreeNodeList*& known_intermediate_list, const QueueIntermediateTreeNodeList*& constructing_intermediate_list, bool& own_known_intermediate_list, const TreeNode* const first, const TreeNode* const last, const size_t size); }; #endif // PERSISTENT_QUEUE_H
      
      





persistent_queue.cpp
 #include "persistent_queue.h" #include <cassert> struct Persistent_queue::TreeNode { const TreeNode* const parent; const Element_type element; TreeNode(const TreeNode* const parent, const Element_type& value) : parent(parent), element(value) {} ~TreeNode() = default; TreeNode(const TreeNode&) = delete; TreeNode& operator =(const TreeNode&) = delete; }; struct Persistent_queue::QueueIntermediateTreeNodeList { const Persistent_queue::TreeNode* const front; const QueueIntermediateTreeNodeList* const next; const size_t size; QueueIntermediateTreeNodeList(const Persistent_queue::TreeNode* const front, const QueueIntermediateTreeNodeList* const tail_list) : front(front), next(tail_list), size{tail_list ? tail_list->size + 1 : 1} { assert(front); } ~QueueIntermediateTreeNodeList() = default; QueueIntermediateTreeNodeList(const QueueIntermediateTreeNodeList&) = delete; QueueIntermediateTreeNodeList& operator =(const QueueIntermediateTreeNodeList&) = delete; }; Persistent_queue::Queue::Queue( const Persistent_queue::TreeNode* const first, const Persistent_queue::TreeNode* const last, const size_t size, const Persistent_queue::QueueIntermediateTreeNodeList* const known_intermediate_list, const Persistent_queue::QueueIntermediateTreeNodeList* const constructing_intermediate_list, const bool own_last, const bool own_known_intermediate_list ) : first(first), last(last), size(size), known_intermediate_list(known_intermediate_list) , constructing_intermediate_list(constructing_intermediate_list) , own_last(own_last), own_known_intermediate_list(own_known_intermediate_list) { // Some asserts if (size == 0) { assert(first == nullptr); assert(last == nullptr); } else { assert(first); assert(last); if (size > 1) assert(last->parent); } if (size <= 2) { assert(known_intermediate_list == nullptr); assert(constructing_intermediate_list == nullptr); if (size == 1) assert(first == last); if (size == 2) assert(first == last->parent); } else { assert(known_intermediate_list); assert(first == known_intermediate_list->front->parent); } } size_t Persistent_queue::size(const Persistent_queue::Queue_id queue_id) { return queues_.at(queue_id).size; } bool Persistent_queue::empty(const Queue_id queue_id) { return size(queue_id) == 0; } const Element_type& Persistent_queue::front(const Persistent_queue::Queue_id queue_id) { assert(!empty(queue_id)); return queues_.at(queue_id).first->element; } Persistent_queue::Queue_id Persistent_queue::register_new_queue(const TreeNode* const first, const TreeNode* const last, const size_t size, const QueueIntermediateTreeNodeList* const known_intermediate_list, const QueueIntermediateTreeNodeList* const constructing_intermediate_list, const bool own_last, const bool own_known_intermediate_list) { queues_.emplace_back(first, last, size, known_intermediate_list, constructing_intermediate_list, own_last, own_known_intermediate_list); return queues_.size() - 1; } Persistent_queue::Persistent_queue() { register_new_queue(nullptr, nullptr, 0, nullptr, nullptr, false, false); } Persistent_queue::~Persistent_queue() { for (const auto& q : queues_) { if (q.own_last) delete q.last; if (q.own_known_intermediate_list) delete q.known_intermediate_list; delete q.constructing_intermediate_list; } } Persistent_queue::Queue_id Persistent_queue::push(const Persistent_queue::Queue_id queue_id, const Element_type& value) { const auto& queue_for_push = queues_.at(queue_id); const size_t size = queue_for_push.size + 1; const bool own_last = true; const TreeNode* first; const TreeNode* last; if (queue_for_push.size == 0) { first = last = new TreeNode(nullptr, value); } else { first = queue_for_push.first; last = new TreeNode(queue_for_push.last, value); } bool own_known_intermediate_list; const QueueIntermediateTreeNodeList* known_intermediate_list = queue_for_push.known_intermediate_list; const QueueIntermediateTreeNodeList* constructing_intermediate_list = queue_for_push.constructing_intermediate_list; manage_intermediate_lists(known_intermediate_list, constructing_intermediate_list, own_known_intermediate_list, first, last, size); return register_new_queue(first, last, size, known_intermediate_list, constructing_intermediate_list, own_last, own_known_intermediate_list); } Persistent_queue::Queue_id Persistent_queue::pop(const Persistent_queue::Queue_id queue_id) { const auto& queue_for_pop = queues_.at(queue_id); const bool own_last = false; const TreeNode* first; const TreeNode* last; size_t size; const QueueIntermediateTreeNodeList* known_intermediate_list; if (queue_for_pop.size <= 1) { first = last = nullptr; size = 0; known_intermediate_list = nullptr; } else { last = queue_for_pop.last; size = queue_for_pop.size - 1; if (queue_for_pop.size == 2) { first = queue_for_pop.last; known_intermediate_list = nullptr; } else { assert(queue_for_pop.known_intermediate_list != nullptr); first = queue_for_pop.known_intermediate_list->front; known_intermediate_list = queue_for_pop.known_intermediate_list->next; } } bool own_known_intermediate_list; const QueueIntermediateTreeNodeList* constructing_intermediate_list = queue_for_pop.constructing_intermediate_list; manage_intermediate_lists(known_intermediate_list, constructing_intermediate_list, own_known_intermediate_list, first, last, size); return register_new_queue(first, last, size, known_intermediate_list, constructing_intermediate_list, own_last, own_known_intermediate_list); } void Persistent_queue::manage_intermediate_lists( const Persistent_queue::QueueIntermediateTreeNodeList*& known_intermediate_list, const Persistent_queue::QueueIntermediateTreeNodeList*& constructing_intermediate_list, bool& own_known_intermediate_list, const Persistent_queue::TreeNode* const first, const Persistent_queue::TreeNode* const last, const size_t size) { own_known_intermediate_list = false; const size_t intermediate_nodes_count = (size > 2 ? size - 2 : 0); size_t known_intermediate_list_size = (known_intermediate_list ? known_intermediate_list->size : 0); if (2*known_intermediate_list_size < intermediate_nodes_count) { auto try_to_replace_known_to_constructing = [&](){ if (constructing_intermediate_list && constructing_intermediate_list->front->parent == first) { known_intermediate_list = constructing_intermediate_list; known_intermediate_list_size = constructing_intermediate_list->size; constructing_intermediate_list = nullptr; return true; } return false; }; if (!try_to_replace_known_to_constructing()) { const auto adding_node = (constructing_intermediate_list ? constructing_intermediate_list->front->parent : last->parent); constructing_intermediate_list = new QueueIntermediateTreeNodeList( adding_node, constructing_intermediate_list); if (try_to_replace_known_to_constructing()) own_known_intermediate_list = true; } } // Check invariants if (2*known_intermediate_list_size >= intermediate_nodes_count) assert(constructing_intermediate_list == nullptr); const size_t constructing_intermediate_list_size = (constructing_intermediate_list ? constructing_intermediate_list->size : 0); const auto invariant_sum = 2*known_intermediate_list_size + constructing_intermediate_list_size; assert(invariant_sum >= intermediate_nodes_count); if (constructing_intermediate_list) assert(invariant_sum == intermediate_nodes_count); }
      
      







TreeNode、Queue、およびQueueIntermediateTreeNodeListタむプは、アルゎリズムのツリヌの最䞊郚、キュヌ、および単玔に接続されたリストの芁玠に察応したす。 Queueのブヌル倉数は、クラスのデストラクタでメモリを正しく解攟するために䜿甚されたすツリヌの頂点ずリストアむテムは、それらを䜜成したキュヌによっお削陀されたす䞊蚘のように、各リク゚ストで頂点は1぀だけ、リストアむテムは1぀だけ䜜成されたす。 ツリヌの最䞊郚は、プッシュ操䜜䞭にのみ䜜成され、それぞのポむンタはそれぞれ最埌に曞き蟌たれたす。own_lastは、新しい頂点ず叀い頂点のどちらが最埌に曞き蟌たれるかを瀺したす。 䜜成䞭に䜜成されたリストアむテムは、ほずんどの堎合、constructing_intermediate_listに曞き蟌たれたす。ただし、䜜成され、同じステップでの䜜成が完了した堎合は䟋倖です。そのような堎合。



2番目の明癜でない可胜性のあるポむントは、プラむベヌトメンバヌ関数manage_intermediate_listsで、次のようになりたす。

 void Persistent_queue::manage_intermediate_lists( const Persistent_queue::QueueIntermediateTreeNodeList*& known_intermediate_list, const Persistent_queue::QueueIntermediateTreeNodeList*& constructing_intermediate_list, bool& own_known_intermediate_list, const Persistent_queue::TreeNode* const first, const Persistent_queue::TreeNode* const last, const size_t size) { own_known_intermediate_list = false; const size_t intermediate_nodes_count = (size > 2 ? size - 2 : 0); size_t known_intermediate_list_size = (known_intermediate_list ? known_intermediate_list->size : 0); if (2*known_intermediate_list_size < intermediate_nodes_count) { auto try_to_replace_known_to_constructing = [&](){ if (constructing_intermediate_list && constructing_intermediate_list->front->parent == first) { known_intermediate_list = constructing_intermediate_list; known_intermediate_list_size = constructing_intermediate_list->size; constructing_intermediate_list = nullptr; return true; } return false; }; if (!try_to_replace_known_to_constructing()) { const auto adding_node = (constructing_intermediate_list ? constructing_intermediate_list->front->parent : last->parent); constructing_intermediate_list = new QueueIntermediateTreeNodeList( adding_node, constructing_intermediate_list); if (try_to_replace_known_to_constructing()) own_known_intermediate_list = true; } } // Check invariants }
      
      





この関数は、構築䞭のリストぞのポむンタヌを操䜜するための䞊蚘のアルゎリズムを実装したす最初の3぀のパラメヌタヌは非定数リンクを介しお枡されたす。最初の2぀のパラメヌタヌに぀いおは、呌び出しの前に必芁な倀によっお倉数が初期化されるず想定されおいたす。 叀い芁玠が最初の䞭間䜓になっおいる堎合、リストを完成する前に確認するこずが重芁です。すでに述べたように、ポップの堎合、このオプションは可胜です。 誰かが建蚭の開始基準を垞にチェックしおいるこずに気付いたのかもしれたせん。
 if (2*known_intermediate_list_size < intermediate_nodes_count) {
      
      



建蚭がすでに進行䞭の堎合は、個別に匷調せずに。 事実は、蚭蚈プロセス党䜓を通しお基準が真のたたであるずいうこずです。これは明らかに、蚭蚈䞍倉匏2k + l = s、l> 0 => 2k <sから埗られたす。



可胜な最適化



たず、 図でわかるように、構築が進行䞭の同じキュヌで耇数の異なる操䜜が実行されるず図ではこれらはポップQおよびプッシュQ、11操䜜です、いく぀かの芁玠はたったく同じですリストツリヌの同じ次の芁玠ず同じ頂点を指したす-ダむアグラムには、番号7の2぀の青い円がありたす。 たずえば、完了時に、誰かがすでに自分の前でそれを行っおいるかどうかを瀺す構造を䜿甚しお、そのような芁玠を1぀に「接着」しようずするこずができたすそのような構造ずしお、通垞のベクトルたたは二重にリンクされたリストを䜿甚できたす。 明らかに、このような最適化は、高床に分岐したツリヌの堎合にうたく機胜したす倚くのメモリを節玄したす。



第二に、別の重耇がありたす完成したリストず䜜成䞭のリストの芁玠がツリヌの同じ頂点を指しおいる堎合 この写真では緑ずオレンゞの芁玠に぀いお話しおいる。 もちろん、このような重耇を完党に回避するこずはできたせん。リストの開始ず継続が同じである可胜性が高いためですが、䜜成䞭のリストの最初が完成したリストの最埌キュヌの先頭ではなくに達するず、単に「接着」するこずができたすこれにより、少しのメモリが節玄されたす。 残りのリストもちろん、この準備に察応に぀いおは、耇補する必芁がありたす。 したがっお、この最適化は、朚に長い盎線の枝がたくさんある堎合に最もよく珟れたす。



最埌に、第䞉に、定数で遊んでみるこずができたす。 たずえば、1぀の芁玠ではなく1぀のステップで完了するこずができたすが、2ず぀では、k <1/3秒で構築を開始するだけで十分です。 おそらく䜕らかの圢で圹立぀でしょう。



それを読んだすべおの人に感謝したす。



All Articles