簡體   English   中英

快速插入STL映射

[英]Fast Insertions into STL map

以下代碼來自cplusplus.com 它有兩個標記為有效和無效的插入物。 我認為高效的人應該給我的提示是mymap.begin() + 1 ,因為*(mymap.begin() + 1)'z'並且z將跟隨b

如果位置指向將要插入的元素之后的元素(或最后一個,如果指向末尾),則該函數將優化其插入時間。

插入'c'的最佳提示是*(mymap.begin() + 2) ,因為它必須傳遞'a''b'
對還是錯? 我嘗試對建議的代碼進行計時,並將其與此處的“高效”代碼進行比較,但我發現沒有區別。 可能是因為我打開了100萬個標簽並且還播放了音樂,並且這是一個簡單的例子。

  std::map<char,int> mymap;

  // first insert function version (single parameter):
  mymap.insert ( std::pair<char,int>('a',100) );
  mymap.insert ( std::pair<char,int>('z',200) );

  // second insert function version (with hint position):
  std::map<char,int>::iterator it = mymap.begin();
  mymap.insert (it, std::pair<char,int>('b',300));  // max efficiency inserting
  mymap.insert (it, std::pair<char,int>('c',400));  // no max efficiency inserting

“高效”版本只有在您提供了很好的提示后才有效。 您的提示( .begin )錯誤。 現在,在只有兩個元素的容器中,您不會錯,因此損壞是有限的。

提示插入的語義規范隨C ++ 11更改(如本答案所示 )。 有關決議,請參閱DR 233 ,有關導致該決議的部分討論,請參見N1780

缺陷報告和討論文件主要是關於std::multimapstd::multiset ,其中允許重復的密鑰。 在那種情況下,如果“提示”指的是具有與插入的鍵相同的鍵的元素,則可以在提示之前或之后插入新元素,並且C ++ 11之前的標准不明確。 DR233決定性地做出決定,但也可以理解為影響std::mapstd::set行為規范。

在原始規范(C + 11之前的版本)中,該標准僅表示“迭代器p是指向插入物應開始搜索的位置的提示”,它對提示應該指向插入點之前還是之后的位置不是很明確。 (也沒有說明在提示錯誤的情況下如何進行搜索,因為無論提示如何都必須將新元素插入正確的位置。)但是,該操作的復雜性被記錄為“通常是對數的” ,但如果t緊接在p “之后插入t則為攤余常數。

該復雜性規范顯然有兩個方面的錯誤:首先,如果未插入 t ,它就不堅持恆定時間插入(因為提示指向鍵比較相等的元素),但是任何合理的實現都很難使它不變-在這種情況下的時間。 其次,如果要在容器的開頭插入新元素,則無法在插入點之前指定提示。

實際上,標准庫的主要實現實際上希望該提示指向插入點之后,盡管大多數還檢查該提示是否剛好位於插入點之后。 因此,現有做法是在標准不需要的情況下(當然是允許的)提供攤銷的恆定時間復雜度,至少有一種廣泛使用的實現未能提供所需的復雜度。

因此, cplusplus.com中的代碼充其量是不精確的,並且絕對不能描述提示插入的正常用例。

假設為給定鍵構造映射值很昂貴。 (也許地圖記住了一個昂貴的函數,並且映射的值沒有便宜的默認構造函數。)在這種情況下,您可能想在麻煩計算相應值之前先檢查地圖是否已經包含鍵。這將需要插入。 天真的實現將是這樣的:

if (mymap.find(key) == mymap.end())
   mymap[key] = expensive_function(key);
// See Note 1 for another slightly more efficient variant

結果是,如果鍵不存在,則相同的對數搜索將執行兩次。 當然,不必要的搜索所產生的額外費用與expensive_function function相比可能是微不足道的,但是,似乎仍然有更好的解決方案。 這是什么:我們使用std::map::lower_bound進行第一次搜索,從而導致僅有略微復雜的代碼:

auto where = mymap.lower_bound(key);
if (where == mymap.end() || where->first != key) 
  where = mymap.emplace_hint(where, key, expensive_function(key));
/* Here, 'where' points to the element with the specified key */

(我使用了std::map::emplace_hint自C ++ 11起可用-而不是部分insert以試圖避免不必要的復制,並避免使用std::make_pair造成代碼混亂。)

筆記

  1. 該代碼的實例很容易找到。 許多人繼續引用mymap[key]來使用存儲的值,並添加了另一個不必要的對數搜索; 更好的代碼是:

     auto where = mymap.find(key); if (where == mymap.end()) where = mymap.emplace(key, expensive_function(key)).first; 

暫無
暫無

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

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