데이터 구조: hashmap 원리
hash_map 는 hash table (해시 표) 를 기반 으로 합 니 다.해시 표 의 가장 큰 장점 은 데이터 의 저장 과 찾기 소모 시간 을 크게 줄 이 고 거의 상수 시간 으로 볼 수 있다 는 것 이다.대 가 는 비교적 많은 메모 리 를 소모 하 는 것 에 불과 하 다.그러나 현재 메모리 가 점점 많아 지 는 상황 에서 공간 으로 시간 을 바 꾸 는 것 은 가치 가 있다.또 인 코딩 이 쉬 운 것 도 특징 이다.
그 기본 원 리 는 아래 표 시 된 범위 가 비교적 큰 배열 로 요 소 를 저장 하 는 것 이다.하나의 함수 (해시 함수, 해시 함수 라 고도 함) 를 설계 하여 모든 요소 의 키 워드 를 하나의 함수 값 (즉, 배열 아래 표시, hash 값) 과 대응 하 게 할 수 있 습 니 다. 그래서 이 배열 유닛 으로 이 요 소 를 저장 합 니 다.키워드 에 따라 모든 요소 로 '분류' 한 다음 에 이 요 소 를 해당 하 는 '클래스' 에 저장 하여 통 이 라 고 부 르 는 것 으로 간단하게 이해 할 수 있다.
그러나 각 요소 의 키워드 와 함수 값 이 일일이 대응 하 는 것 을 보장 할 수 없 기 때문에 서로 다른 요소 에 대해 똑 같은 함수 값 을 계산 할 가능성 이 높다. 그러면 '충돌' 이 생 긴 다. 다시 말 하면 서로 다른 요 소 를 똑 같은 '클래스' 로 나 누 는 것 이다.전체적으로 말 하면 '직접 주소 지정' 과 '충돌 해결' 은 하 쉬 표 의 두 가지 특징 이다.
hash_map, 우선 큰 메모 리 를 분배 하여 많은 통 을 형성 합 니 다.hash 함 수 를 이용 하여 키 를 다른 구역 (통) 에 비 추어 저장 합 니 다.그 삽입 과정 은:
그 수치 추출 과정 은:
hash_map 에서 직접 주 소 는 hash 함수 로 생 성 되 고 충돌 을 해결 하 며 비교 함수 로 해결 합 니 다.각 통 내부 에 하나의 요소 만 있다 면 찾 을 때 한 번 만 비교 한 다 는 것 을 알 수 있다.많은 통 안에 값 이 없 을 때, 많은 조회 가 더욱 빨 라 질 것 이다.
이 를 통 해 알 수 있 듯 이 해시 표를 실현 하려 면 사용자 와 관련 된 것 은 hash 함수 와 비교 함수 입 니 다.이 두 매개 변 수 는 마침 우리 가 hash 를 사용 하고 있 는 것 이다.map 에서 지정 한 인자 가 필요 합 니 다.
2 hash_map 사용
2.1 간단 한 실례
서 두 르 지 마 세 요.map 에 따 르 면 우 리 는 먼저 간단 한 예 를 보 겠 습 니 다. 랜 덤 으로 ID 번호 와 ID 번호 에 해당 하 는 정 보 를 드 리 겠 습 니 다. ID 번호 의 범 위 는 1 ~ 2 의 31 번 입 니 다.검색 을 빠르게 저장 하 는 방법 입 니 다.
#include
#include
using namespace std;
int main(){
hash_map<int, string> mymap;
mymap[9527]=" ";
mymap[1000000]=" ";
mymap[10000]=" ";
...
if(mymap.find(10000) != mymap.end()){
...
}
够简单,和map使用方法一样。这时你或许会问?hash函数和比较函数呢?不是要指定么?你说对了,但是在你没有指定hash函数和比较函数的时候,你会有一个缺省的函数,看看hash_map的声明,你会更加明白。下面是SGI STL的声明:
template <class _Key, class _Tp, class _HashFcn = hash<_key>,
class _EqualKey = equal_to<_key>,
class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class hash_map
{
...
}
...
hash_map<int, string> mymap;
// :
hash_map<int, string, hash<int>, equal_to<int> > mymap;
2.2 hash_map 的hash函数
hash< int>到底是什么样子?看看源码:struct hash<int> {
size_t operator()(int __x) const { return __x; }
};
struct hash<char*>
struct hash<const char*>
struct hash<char>
struct hash<unsigned char>
struct hash<signed char>
struct hash<short>
struct hash<unsigned short>
struct hash<int>
struct hash<unsigned int>
struct hash<long>
struct hash<unsigned long>
struct str_hash{
size_t operator()(const string& str) const
{
unsigned long __h = 0;
for (size_t i = 0 ; i < str.size() ; i ++)
__h = 5*__h + str[i];
return size_t(__h);
}
};
// hash , :
struct str_hash{
size_t operator()(const string& str) const
{
return return __stl_hash_string(str.c_str());
}
};
- 使用struct,然后重载operator().
- 返回是size_t
- 参数是你要hash的key的类型。
- 函数是const类型的。
现在可以对开头的"岳不群"进行哈希化了 . 直接替换成下面的声明即可:
map namemap;
// :
hash_map namemap;
你或许会问:比较函数呢?别着急,这里就开始介绍hash_map中的比较函数。
2.3 hash_map 的比较函数
在map中的比较函数,需要提供less函数。如果没有提供,缺省的也是less< Key> 。在hash_map中,要比较桶内的数据和key是否相等,因此需要的是是否等于的函数:equal_to< Key> 。先看看equal_to的源码:// SGI STL
// binary_function , 。
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
// equal_to :
template <class _Tp>
struct equal_to : public binary_function<_tp>bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const { return __x == __y; }
};
struct mystruct{
int iID;
int len;
bool operator==(const mystruct & my) const{
return (iID==my.iID) && (len==my.len) ;
}
};
struct compare_str{
bool operator()(const char* p1, const char*p2) const{
return strcmp(p1,p2)==0;
}
};
typedef hash_map<const char*, string, hash<const char*>, compare_str> StrIntMap;
StrIntMap namemap;
namemap[" "]=" , ";
namemap[" "]=" , ";
namemap[" "]=" , ";
2.4 hash_map 函数
hash_map的函数和map的函数差不多。具体函数的参数和解释,请参看: STL 编程手册:Hash_map ,这里主要介绍几个常用函数。- hash_map(size_type n) 如果讲究效率,这个参数是必须要设置的。n 主要用来设置hash_map 容器中hash桶的个数。桶个数越多,hash函数发生冲突的概率就越小,重新申请内存的概率就越小。n越大,效率越高,但是内存消耗也越大。
- const_iterator find(const key_type& k) const. 用查找,输入为键值,返回为迭代器。
- data_type& operator[](const key_type& k) . 这是我最常用的一个函数。因为其特别方便,可像使用数组一样使用。不过需要注意的是,当你使用[key ]操作符时,如果容器中没有key元素,这就相当于自动增加了一个key元素。因此当你只是想知道容器中是否有key元素时,你可以使用find。如果你希望插入该元素时,你可以直接使用[]操作符。
- insert 函数。在容器中不包含key值时,insert函数和[]操作符的功能差不多。但是当容器中元素越来越多,每个桶中的元素会增加,为了保证效率,hash_map会自动申请更大的内存,以生成更多的桶。因此在insert以后,以前的iterator有可能是不可用的。
- erase 函数。在insert的过程中,当每个桶的元素太多时,hash_map可能会自动扩充容器的内存。但在sgi stl中是erase并不自动回收内存。因此你调用erase后,其他元素的iterator还是可用的。
3 相关hash容器
hash 容器除了hash_map之外,还有hash_set, hash_multimap, has_multiset, 这些容器使用起来和set, multimap, multiset的区别与hash_map和map的区别一样,我想不需要我一一细说了吧。4 其他
这里列几个常见问题,应该对你理解和使用hash_map比较有帮助。4.1 hash_map和map的区别在哪里?
- 构造函数。hash_map需要hash函数,等于函数;map只需要比较函数(小于函数).
- 存储结构。hash_map采用hash表存储,map一般采用红黑树(RB Tree)实现。因此其memory数据结构是不一样的。
4.2 什么时候需要用hash_map,什么时候需要用map?
总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别。并不一定常数就比log(n)小,hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑hash_map。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且hash_map的构造速度较慢。现在知道如何选择了吗?权衡三个因素: 查找速度, 数据量, 内存使用。
这里还有个关于hash_map和map的小故事,看看:http://dev.csdn.net/Develop/article/14/14019.shtm
4.3 如何在hash_map中加入自己定义的类型?
你只要做两件事, 定义hash函数,定义等于比较函数。下面的代码是一个例子:-bash-2.05b$ cat my.cpp
#include
#include
#include
using namespace std;
//define the class
class ClassA{
public:
ClassA(int a):c_a(a){}
int getvalue()const { return c_a;}
void setvalue(int a){c_a;}
private:
int c_a;
};
//1 define the hash function
struct hash_A{
size_t operator()(const class ClassA & A)const{
// return hash(classA.getvalue());
return A.getvalue();
}
};
//2 define the equal function
struct equal_A{
bool operator()(const class ClassA & a1, const class ClassA & a2)const{
return a1.getvalue() == a2.getvalue();
}
};
int main()
{
hash_map hmap;
ClassA a1(12);
hmap[a1]="I am 12";
ClassA a2(198877);
hmap[a2]="I am 198877";
cout<return 0;
}
-bash-2.05b$ make my
c++ -O -pipe -march=pentiumpro my.cpp -o my
-bash-2.05b$ ./my
I am 12
I am 198877
typedef map KeyMap;
当你希望使用hash_map来替换的时候,只需要修改:
typedef hash_map
KeyMap;
다른 것 은 기본적으로 변 하지 않 는 다.물론 키 형식의 hash 함수 와 비교 함수 가 있 는 지 주의해 야 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
STL 원자로 작동먼저 전연 두 갈래 나무의 정의를 살펴보자. 만약에 두 갈래 나무의 깊이를 h로 설정하면 h층을 제외한 다른 각 층(1~h-1)의 결점은 모두 최대 개수에 달하고 h층의 모든 결점은 연속적으로 맨 왼쪽에 집중된다. ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.