std :: map insert หรือ std :: map find?


93

สมมติว่าเป็นแผนที่ที่คุณต้องการเก็บรักษารายการที่มีอยู่ 20% ของเวลารายการที่คุณกำลังแทรกเป็นข้อมูลใหม่ มีข้อได้เปรียบในการทำ std :: map :: find แล้ว std :: map :: insert โดยใช้ iterator ที่ส่งคืนหรือไม่? หรือมันเร็วกว่าที่จะพยายามแทรกแล้วดำเนินการโดยขึ้นอยู่กับว่าตัววนซ้ำระบุว่าบันทึกนั้นหรือไม่ได้ใส่หรือไม่?


4
ฉันได้รับการแก้ไขและตั้งใจจะใช้ std :: map :: lower_bound แทน std :: map :: find
Superpolock

คำตอบ:


148

คำตอบคือคุณไม่ทำเช่นกัน แต่คุณต้องการทำสิ่งที่แนะนำโดยข้อ 24 ของEffective STLโดยScott Meyers :

typedef map<int, int> MapType;    // Your map type may vary, just change the typedef

MapType mymap;
// Add elements to map here
int k = 4;   // assume we're searching for keys equal to 4
int v = 0;   // assume we want the value 0 associated with the key of 4

MapType::iterator lb = mymap.lower_bound(k);

if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first)))
{
    // key already exists
    // update lb->second if you care to
}
else
{
    // the key does not exist in the map
    // add it to the map
    mymap.insert(lb, MapType::value_type(k, v));    // Use lb as a hint to insert,
                                                    // so it can avoid another lookup
}

2
นี่เป็นวิธีการทำงานของการค้นหาเคล็ดลับคือการรวมการค้นหาที่จำเป็นโดยการค้นหาและแทรก แน่นอนเพียงแค่ใช้การแทรกแล้วดูที่ค่าส่งคืนที่สอง
puetzk

1
คำถามสองข้อ: 1) การใช้ lower_bound แตกต่างกับการใช้ find สำหรับแผนที่อย่างไร 2) สำหรับ 'แผนที่' ไม่ใช่กรณีที่ op ขวามือของ && จะเป็นจริงเสมอเมื่อ 'lb! = mymap.end ()'?
Richard Corden

12
@Richard: find () ส่งคืน end () หากไม่มีคีย์ lower_bound จะส่งคืนตำแหน่งที่ไอเท็มควรจะอยู่ (ซึ่งสามารถใช้เป็นคำใบ้การแทรกได้) @puetzek: จะไม่ "แทรก" ทับค่าอ้างอิงสำหรับคีย์ที่มีอยู่หรือไม่? ไม่แน่ใจว่า OP ต้องการหรือไม่
peterchen

2
มีใครรู้บ้างว่า unordered_map มีอะไรคล้ายกันบ้าง
Giovanni Funchal

3
@peterchen แผนที่ :: แทรกไม่ได้เขียนทับค่าที่มีอยู่ถ้ามันมีอยู่แล้วเห็นcplusplus.com/reference/map/map/insert
Chris Drew

11

คำตอบสำหรับคำถามนี้ยังขึ้นอยู่กับราคาแพงในการสร้างประเภทมูลค่าที่คุณจัดเก็บไว้ในแผนที่:

typedef std::map <int, int> MapOfInts;
typedef std::pair <MapOfInts::iterator, bool> IResult;

void foo (MapOfInts & m, int k, int v) {
  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.first->second = v;
  }
}

สำหรับประเภทค่าเช่น int ข้างต้นจะมีประสิทธิภาพมากกว่าการค้นหาตามด้วยส่วนแทรก (ในกรณีที่ไม่มีการปรับแต่งคอมไพลเลอร์ให้เหมาะสม) ตามที่ระบุไว้ข้างต้นเนื่องจากการค้นหาผ่านแผนที่จะเกิดขึ้นเพียงครั้งเดียว

อย่างไรก็ตามการเรียกเพื่อแทรกต้องการให้คุณสร้าง "ค่า" ใหม่ไว้แล้ว:

class LargeDataType { /* ... */ };
typedef std::map <int, LargeDataType> MapOfLargeDataType;
typedef std::pair <MapOfLargeDataType::iterator, bool> IResult;

void foo (MapOfLargeDataType & m, int k) {

  // This call is more expensive than a find through the map:
  LargeDataType const & v = VeryExpensiveCall ( /* ... */ );

  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.first->second = v;
  }
}

ในการเรียก 'แทรก' เราจะจ่ายค่าโทรแพงเพื่อสร้างประเภทมูลค่าของเรา - และจากสิ่งที่คุณพูดในคำถามคุณจะไม่ใช้ค่าใหม่นี้ 20% ของเวลา ในกรณีข้างต้นหากการเปลี่ยนประเภทค่าแผนที่ไม่ใช่ตัวเลือกการ 'ค้นหา' จะมีประสิทธิภาพมากกว่าก่อนเพื่อตรวจสอบว่าเราจำเป็นต้องสร้างองค์ประกอบหรือไม่

อีกวิธีหนึ่งคือสามารถเปลี่ยนประเภทค่าของแผนที่เพื่อจัดเก็บที่จับไปยังข้อมูลโดยใช้ประเภทตัวชี้อัจฉริยะที่คุณชื่นชอบ การเรียกเพื่อแทรกใช้ตัวชี้ค่าว่าง (ถูกมากในการสร้าง) และเฉพาะในกรณีที่จำเป็นเท่านั้นที่จะสร้างชนิดข้อมูลใหม่


8

แทบจะไม่มีความแตกต่างของความเร็วระหว่าง 2 ตัวค้นหาจะส่งคืนตัววนซ้ำแทรกทำแบบเดียวกันและจะค้นหาแผนที่ต่อไปเพื่อดูว่ามีรายการอยู่แล้วหรือไม่

ดังนั้น .. มันขึ้นอยู่กับความชอบส่วนบุคคล ฉันพยายามแทรกและอัปเดตเสมอหากจำเป็น แต่บางคนไม่ชอบจัดการคู่ที่ส่งคืน


5

ฉันคิดว่าถ้าคุณค้นหาแล้วแทรกค่าใช้จ่ายเพิ่มเติมจะเป็นเมื่อคุณไม่พบคีย์และทำการแทรกหลังจากนั้น มันเหมือนกับการดูหนังสือตามลำดับตัวอักษรและไม่พบหนังสือเล่มนั้นจากนั้นจึงดูหนังสืออีกครั้งเพื่อดูว่าจะแทรกที่ไหน มันทำให้คุณเข้าใจว่าคุณจะจัดการกับกุญแจอย่างไรและหากมีการเปลี่ยนแปลงอยู่ตลอดเวลา ตอนนี้มีความยืดหยุ่นบางอย่างที่หากคุณไม่พบคุณสามารถเข้าสู่ระบบยกเว้นทำอะไรก็ได้ที่คุณต้องการ ...


1

หากคุณมีความกังวลเกี่ยวกับประสิทธิภาพคุณอาจต้องการที่จะตรวจสอบhash_map <>

โดยทั่วไปแผนที่ <> จะถูกนำไปใช้เป็นต้นไม้ไบนารี ขึ้นอยู่กับความต้องการของคุณ hash_map อาจมีประสิทธิภาพมากกว่า


จะชอบ แต่ไม่มี hash_map ในไลบรารีมาตรฐาน C ++ และ PHB ไม่อนุญาตให้ใช้โค้ดนอกนั้น
Superpolock

1
std :: tr1 :: unordered_mapคือแผนที่แฮชที่เสนอให้เพิ่มในมาตรฐานถัดไปและควรมีอยู่ในการใช้งาน STL ปัจจุบันส่วนใหญ่
beldaz

1

ฉันดูเหมือนจะไม่มีคะแนนมากพอที่จะแสดงความคิดเห็น แต่ดูเหมือนว่าคำตอบที่ทำเครื่องหมายไว้จะยืดยาวสำหรับฉัน - เมื่อคุณพิจารณาว่าส่วนแทรกนั้นจะส่งคืนตัววนซ้ำไปทำไมต้องค้นหา lower_bound ในเมื่อคุณสามารถใช้ตัววนซ้ำที่ส่งคืนได้ แปลก.


1
เนื่องจาก (แน่นอนก่อน C ++ 11) การใช้การแทรกหมายความว่าคุณยังคงต้องสร้างstd::map::value_typeวัตถุคำตอบที่ยอมรับจึงหลีกเลี่ยงสิ่งนั้น
KillianDS

-1

คำตอบใด ๆ เกี่ยวกับประสิทธิภาพจะขึ้นอยู่กับการใช้งาน STL ของคุณ วิธีเดียวที่จะรู้ได้อย่างแน่นอนคือการเปรียบเทียบทั้งสองวิธี ฉันเดาว่าความแตกต่างไม่น่าจะมีนัยสำคัญดังนั้นให้ตัดสินใจตามสไตล์ที่คุณต้องการ


1
นี่ไม่เป็นความจริงอย่างแน่นอน STL แตกต่างจากไลบรารีอื่น ๆ ส่วนใหญ่ตรงที่มีข้อกำหนด big-O ที่ชัดเจนสำหรับการดำเนินการส่วนใหญ่ มีการรับประกันความแตกต่างระหว่าง 2 * O (log n) และ 1 * O (log n) ไม่ว่าจะใช้ฟังก์ชันใดเพื่อให้บรรลุพฤติกรรม O (log n) นั้น ความแตกต่างนั้นสำคัญหรือไม่ในแพลตฟอร์มของคุณเป็นคำถามที่แตกต่างกัน แต่ความแตกต่างจะอยู่ที่นั่นเสมอ
srm

@srm การกำหนดข้อกำหนด big-O ยังไม่ได้บอกคุณว่าการดำเนินการจะใช้เวลานานเท่าใด ไม่มีการรับประกันความแตกต่างที่คุณพูดถึง
มาร์คค่าไถ่

-2

แผนที่ [คีย์] - ให้ stl จัดเรียงออก นั่นเป็นการสื่อสารความตั้งใจของคุณอย่างมีประสิทธิภาพมากที่สุด

ใช่ยุติธรรมพอ

หากคุณทำการค้นหาแล้วแทรกคุณกำลังแสดง 2 x O (บันทึก N) เมื่อคุณพลาดเนื่องจากการค้นหาจะแจ้งให้คุณทราบหากคุณต้องการแทรกไม่ใช่ตำแหน่งที่ควรจะแทรก (lower_bound อาจช่วยคุณได้) . เพียงแค่แทรกตรงแล้วตรวจสอบผลลัพธ์ก็เป็นวิธีที่ฉันจะไป


ไม่ถ้ามีรายการอยู่จะส่งคืนการอ้างอิงไปยังรายการที่มีอยู่
Kris Kumler

2
-1 สำหรับคำตอบนี้ ดังที่ Kris K กล่าวไว้การใช้ map [key] = value จะเขียนทับรายการที่มีอยู่ไม่ใช่ "เก็บรักษา" ตามที่ต้องการในคำถาม คุณไม่สามารถทดสอบการมีอยู่โดยใช้แผนที่ [คีย์] ได้เนื่องจากจะส่งคืนอ็อบเจ็กต์ที่สร้างขึ้นเริ่มต้นหากไม่มีคีย์และสร้างสิ่งนั้นเป็นรายการสำหรับคีย์
netjeff

ประเด็นคือการทดสอบว่ามีการเติมข้อมูลแผนที่แล้วหรือไม่และเพิ่ม / เขียนทับหากไม่มีอยู่ การใช้แผนที่ [คีย์] จะถือว่ามีค่าอยู่แล้วเสมอ
srm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.