簡體   English   中英

比O(n)時間更好地找到鏈表中條目的索引

[英]Finding the index of an entry in a linked list in better than O(n) time

我遇到了將變更列表推送到另一個系統的情況。 每個列表包含零個或多個插入,更新或刪除的通知。

插入很容易; 該通知包含目標索引和指向該項目的指針。 更新很容易; 我將指針傳遞給該項目。

刪除似乎很簡單。 我需要傳遞要刪除的項目的索引,但是我怎么知道索引? 索引從零開始,並且必須是連續的,但是我在插入時將它們補足。 因此,我需要跟蹤我為每個項目組成的索引。

我可以使用例如map: std::map<item*, int>來做到這一點,但是當我刪除一個項目時,我必須重新編號過去的所有內容,即O(N)。

這些項目列表將變得很大,以至於O(N)迭代是不可接受的。 我敢肯定這個問題已經解決了,我只是不知道該解決方案將被稱為什么。 搜索與“鏈接列表”相關的任何內容都會產生大量噪音。

一種可能的解決方案是跳過列表,其中子列表中的每個節點都知道它要跳過主列表中的多少個節點,並且由於搜索跳過列表為O(log N),因此我們可以隨時跟蹤並在其中找到索引O(log N),並刪除O(log N)中的項目。

但是,在這里實現跳過列表似乎有些矯kill過正……是否有更簡單的解決方案?

編輯:

感謝所有您的建議,但我想我已經說服了跳過列表是解決此問題的正確方法。

參見Hinze和Paterson的《 手指樹:一種簡單的通用數據結構》

另請參見MarkCC的有關手指樹博客文章中的精美插圖。

編輯:我以前的解決方案是有缺陷的std :: vector :: erase()在移動元素時是線性的。 我的新建議將我先前的評論擴展到您的問題。

如果僅使用列表中的指針,則可以在對指針調用delete之后將指針設置為0,以保持索引/鍵有效。 然后,您應該能夠使用越來越大的索引,直到下一個索引超出std::numeric_limits<int>::max()為止。 然后,當列表的較大部分包含設置為零的未使用的指針元素時,請在通信通道的兩側進行同步清除零指針,然后重新計算索引。 我不知道如何對此進行很好的啟發,但是您可以跟蹤零指針的數量,並將其與整個列表大小進行比較。

用更少的詞來說,因為索引的計算是O(n),所以將其延遲到絕對必要為止。

您無法保留刪除歷史記錄並在std::map<item*, int>進行查找時對此進行補償嗎?

我的意思是說std::map的索引代表該項目的原始索引,然后您有一個輔助映射std::map<int, int> ,其中存儲了給定索引已刪除了多少次?

item* todelete; //from somewhere
std::map<int, int> history; //stored somewhere
std::map<item*, int> itemIndices; //stored somewhere
const int originalIndex = itemIndices[todelete]; //index of item at insert time
int index = originalIndex;
for (std::map<int, int>::const_iterator it = history.begin(); it != history.end() && it->first < originalIndex; ++it) {
    index -= it->second;
}
// index has now been compensated for previous deletes
// ... send the delete request with 'index'
// update the history with this delete request
std::map<int, int>::iterator it = history.find(index);
if (history.end() == it) {
    history[index] = 1;
} else {
    ++it->second;
}

當然,其速度取決於歷史的長短。

/ AB

刪除多久發生一次? 我正在考慮使用std::map<item*, int>保留您的解決方案,而不是在刪除后更新地圖,而是使用“ NULL”項目替換鏈接列表中的項目,以確保lookupmap中的索引仍然有效。 如果您經常看到刪除內容,並且有可能耗盡內存,那么這可能不是一個好的解決方案。 另外,您可以執行此操作,並具有reindex()方法,該方法將從鏈接列表中刪除任何NULL項目,並為所有項目分配新索引。

旁注1:不能像在更新中一樣將指針傳遞給要刪除的項目嗎? 如果執行此操作並使用雙向鏈接列表,則可以在O(1)中輕松執行刪除操作。

旁注2:考慮在std::map使用boost::unordered_map

跳過列表是正確的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM