ฉันรู้ว่า STL มี HashMap API แต่ฉันไม่สามารถหาเอกสารที่ดีและละเอียดพร้อมตัวอย่างที่ดีเกี่ยวกับเรื่องนี้
ตัวอย่างที่ดีใด ๆ ที่จะได้รับการชื่นชม
ฉันรู้ว่า STL มี HashMap API แต่ฉันไม่สามารถหาเอกสารที่ดีและละเอียดพร้อมตัวอย่างที่ดีเกี่ยวกับเรื่องนี้
ตัวอย่างที่ดีใด ๆ ที่จะได้รับการชื่นชม
คำตอบ:
ไลบรารีมาตรฐานประกอบด้วยคอนเทนเนอร์ที่สั่งซื้อและแผนที่ที่ไม่เรียงลำดับ ( std::map
และstd::unordered_map
) ในการสั่งซื้อ map องค์ประกอบจะเรียงตามคีย์แทรกและการเข้าถึงอยู่ในO (log n) โดยปกติแล้วห้องสมุดมาตรฐานภายในจะใช้ต้นไม้สีดำสีแดงสำหรับแผนที่ที่สั่งซื้อ แต่นี่เป็นเพียงรายละเอียดการดำเนินการ ในการแทรกแผนที่ที่ไม่ได้เรียงลำดับและการเข้าถึงอยู่ใน O (1) มันเป็นเพียงชื่ออื่นสำหรับ hashtable
ตัวอย่างที่มี (สั่งซื้อ) std::map
:
#include <map>
#include <iostream>
#include <cassert>
int main(int argc, char **argv)
{
std::map<std::string, int> m;
m["hello"] = 23;
// check if key is present
if (m.find("world") != m.end())
std::cout << "map contains key world!\n";
// retrieve
std::cout << m["hello"] << '\n';
std::map<std::string, int>::iterator i = m.find("hello");
assert(i != m.end());
std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
return 0;
}
เอาท์พุท:
23 รหัส: สวัสดีค่า: 23
หากคุณจำเป็นต้องสั่งซื้อในภาชนะของคุณและจะมีการปรับกับ O (log n) std::map
รันไทม์แล้วเพียงแค่ใช้
มิฉะนั้นหากคุณต้องการแฮชตาราง (O (1) การแทรก / การเข้าถึง) ให้ตรวจสอบstd::unordered_map
ซึ่งมีstd::map
API คล้ายกัน(เช่นในตัวอย่างข้างต้นคุณเพียงแค่ค้นหาและแทนที่map
ด้วยunordered_map
)
unordered_map
ภาชนะที่ถูกนำมาใช้กับC ++ 11 มาตรฐานการแก้ไข ดังนั้นขึ้นอยู่กับคอมไพเลอร์ของคุณคุณต้องเปิดใช้งานคุณสมบัติ C ++ 11 (เช่นเมื่อใช้ GCC 4.8 คุณต้องเพิ่มลง-std=c++11
ใน CXXFLAGS)
แม้กระทั่งก่อนที่ C ++ 11 ปล่อย GCC สนับสนุนunordered_map
- ในการ std::tr1
namespace ดังนั้นสำหรับคอมไพเลอร์ GCC แบบเก่าคุณสามารถลองใช้มันได้เช่นนี้:
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
มันยังเป็นส่วนหนึ่งของการเพิ่มพลังเช่นคุณสามารถใช้บูสต์ส่วนหัวที่สอดคล้องกันเพื่อการพกพาที่ดีขึ้น
hash_map
SGI STL ในบางรูปแบบหรืออย่างอื่น
unordered_map
ที่ไม่ได้ให้ hash_map
ดังนั้นจึงมีเหตุผลที่จะต้องพิจารณาที่ไม่ได้มาตรฐานไม่มี
A hash_map
คือเวอร์ชันที่เก่ากว่าและไม่ได้มาตรฐานสำหรับสิ่งที่เรียกว่ามาตรฐานunordered_map
(แต่เดิมใน TR1 และรวมอยู่ในมาตรฐานตั้งแต่ C ++ 11) ตามชื่อหมายถึงมันแตกต่างจากstd::map
การไม่มีการเรียงลำดับหลัก - ตัวอย่างเช่นถ้าคุณวนซ้ำแผนที่จากbegin()
ไปยังend()
คุณจะได้รับรายการตามคีย์1แต่ถ้าคุณวนซ้ำunordered_map
จากจากbegin()
เป็นend()
คุณจะได้รับรายการใน คำสั่งโดยพลการมากหรือน้อย
unordered_map
คาดว่าปกติจะมีความซับซ้อนอย่างต่อเนื่อง นั่นคือการแทรกการค้นหา ฯลฯ โดยทั่วไปแล้วจะใช้เวลาในการแก้ไขไม่ว่าจะมีกี่รายการในตาราง A std::map
มีความซับซ้อนที่ลอการิทึมเกี่ยวกับจำนวนของรายการที่จัดเก็บ - ซึ่งหมายถึงเวลาที่จะแทรกหรือดึงรายการที่เพิ่มขึ้น แต่ค่อนข้างช้าเมื่อแผนที่ขยายใหญ่ขึ้น ตัวอย่างเช่นหากใช้เวลา 1 microsecond ในการค้นหาหนึ่งใน 1 ล้านรายการคุณสามารถคาดหวังได้ว่าจะใช้เวลาประมาณ 2 microseconds เพื่อค้นหาหนึ่งใน 2 ล้านรายการและ 3 microseconds หนึ่งใน 4 ล้านรายการหนึ่ง 4 microseconds หนึ่งใน 8 ล้านรายการ รายการ ฯลฯ
จากมุมมองของภาคปฏิบัตินั่นไม่ใช่เรื่องจริงทั้งหมด โดยธรรมชาติตารางแฮชที่เรียบง่ายมีขนาดคงที่ การปรับให้เข้ากับความต้องการขนาดตัวแปรสำหรับคอนเทนเนอร์วัตถุประสงค์ทั่วไปค่อนข้างไม่สำคัญ เป็นผลให้การดำเนินการที่ (อาจ) ขยายตาราง (เช่นการแทรก) อาจจะค่อนข้างช้า (นั่นคือส่วนใหญ่จะค่อนข้างเร็ว แต่ระยะหนึ่งจะช้ากว่ามาก) การค้นหาซึ่งไม่สามารถเปลี่ยนขนาดของตารางได้โดยทั่วไปจะเร็วกว่ามาก เป็นผลให้ตารางแฮชส่วนใหญ่มักจะดีที่สุดเมื่อคุณทำการค้นหาจำนวนมากเมื่อเทียบกับจำนวนการแทรก สำหรับสถานการณ์ที่คุณแทรกข้อมูลจำนวนมากจากนั้นวนซ้ำผ่านตารางหนึ่งครั้งเพื่อดึงผลลัพธ์ (เช่นการนับจำนวนคำที่ไม่ซ้ำกันในไฟล์) โอกาสที่std::map
จะเร็วและอาจเร็วกว่า (แต่อีกครั้งความซับซ้อนในการคำนวณแตกต่างกันดังนั้นจึงสามารถขึ้นอยู่กับจำนวนคำที่ไม่ซ้ำกันในไฟล์)
1ตำแหน่งที่ใบสั่งถูกกำหนดโดยพารามิเตอร์เทมเพลตที่สามเมื่อคุณสร้างแผนที่std::less<T>
ตามค่าเริ่มต้น
rehash
ไม่หดตัวยกเว้นในการตอบสนองต่อการเรียก เมื่อคุณโทรrehash
คุณจะระบุขนาดสำหรับตาราง ขนาดนั้นจะถูกนำมาใช้เว้นแต่ว่าการทำเช่นนั้นจะเกินตัวประกอบภาระสูงสุดที่กำหนดไว้สำหรับตาราง (ในกรณีนี้ขนาดจะเพิ่มขึ้นโดยอัตโนมัติเพื่อให้ตัวประกอบโหลดอยู่ภายในขีด จำกัด )
นี่คือตัวอย่างที่สมบูรณ์และยืดหยุ่นมากขึ้นซึ่งไม่จำเป็นต้องรวมถึงการสร้างข้อผิดพลาดในการรวบรวม:
#include <iostream>
#include <unordered_map>
class Hashtable {
std::unordered_map<const void *, const void *> htmap;
public:
void put(const void *key, const void *value) {
htmap[key] = value;
}
const void *get(const void *key) {
return htmap[key];
}
};
int main() {
Hashtable ht;
ht.put("Bob", "Dylan");
int one = 1;
ht.put("one", &one);
std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}
ยังคงไม่เป็นประโยชน์โดยเฉพาะอย่างยิ่งสำหรับกุญแจเว้นแต่พวกเขาจะกำหนดไว้ล่วงหน้าเป็นตัวชี้เพราะค่าการจับคู่จะไม่ทำ! (อย่างไรก็ตามเนื่องจากปกติฉันจะใช้สตริงสำหรับคีย์การแทนที่ "string" สำหรับ "const void *" ในการประกาศคีย์ควรแก้ไขปัญหานี้)
void*
คุณกำลังใช้ภาษาพิมพ์มั่นและทำลายมันโดยใช้ สำหรับผู้เริ่มไม่มีเหตุผลที่จะห่อunordered_map
เนื่องจากเป็นส่วนหนึ่งของมาตรฐานและลดการบำรุงรักษาโค้ด templates
ต่อไปถ้ายืนยันในห่อไว้ใช้ นั่นคือสิ่งที่พวกเขามีไว้เพื่อ
หลักฐานที่std::unordered_map
ใช้แผนที่แฮชใน GCC stdlibc ++ 6.4
สิ่งนี้ถูกกล่าวถึงที่: https://stackoverflow.com/a/3578247/895245แต่ในคำตอบต่อไปนี้: โครงสร้างข้อมูลใดอยู่ภายใน std :: map ใน C ++? ฉันได้ให้หลักฐานเพิ่มเติมสำหรับการใช้ GCC stdlibc ++ 6.4 โดย:
นี่คือตัวอย่างของกราฟลักษณะประสิทธิภาพที่อธิบายไว้ในคำตอบนั้น:
วิธีใช้คลาสที่กำหนดเองและฟังก์ชันแฮชด้วย unordered_map
คำตอบนี้ตอกย้ำ: C ++ unordered_map โดยใช้ประเภทคลาสที่กำหนดเองเป็นคีย์
ตัดตอนมา: เท่าเทียมกัน:
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
ฟังก์ชั่นแฮช:
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
}
สำหรับพวกเราที่พยายามคิดวิธีแฮชคลาสของเราเองในขณะที่ยังใช้เทมเพลตมาตรฐานอยู่นั้นมีวิธีแก้ไขง่ายๆดังนี้:
==
ในชั้นเรียนของคุณคุณจะต้องกำหนดเกินความเสมอภาคผู้ประกอบการ หากคุณไม่ทราบวิธีการทำเช่นนี้ GeeksforGeeks มีบทช่วยสอนที่ยอดเยี่ยมhttps://www.geeksforgeeks.org/operator-overloading-c/
ภายใต้เนมสเปซมาตรฐานให้ประกาศโครงสร้างเทมเพลตที่เรียกว่าแฮชพร้อมกับชื่อคลาสของคุณเป็นประเภท (ดูด้านล่าง) ฉันพบ blogpost ที่ยอดเยี่ยมซึ่งแสดงตัวอย่างของการคำนวณแฮชโดยใช้ XOR และ bitshifting แต่มันอยู่นอกขอบเขตของคำถามนี้ แต่ก็ยังมีคำแนะนำโดยละเอียดเกี่ยวกับวิธีทำให้สำเร็จโดยใช้ฟังก์ชันแฮชด้วยเช่นกันhttps://prateekvjoshi.com/ 2014 / 05/06 / ใช้แฮชฟังก์ชั่นในคสำหรับผู้ใช้กำหนดเรียน /
namespace std {
template<>
struct hash<my_type> {
size_t operator()(const my_type& k) {
// Do your hash function here
...
}
};
}
std::map
หรือstd::unordered_map
เหมือนกับที่คุณทำตามปกติและใช้my_type
เป็นคีย์ไลบรารีมาตรฐานจะใช้ฟังก์ชันแฮชที่คุณกำหนดไว้ก่อน (ในขั้นตอนที่ 2) โดยอัตโนมัติเพื่อแฮช กุญแจของคุณ#include <unordered_map>
int main() {
std::unordered_map<my_type, other_type> my_map;
}