完成hashtable
This commit is contained in:
@@ -19,21 +19,15 @@
|
|||||||
###二叉树
|
###二叉树
|
||||||
|
|
||||||
* 二叉树
|
* 二叉树
|
||||||
* 二叉查找树
|
* 二叉查找树
|
||||||
|
|
||||||
|
|
||||||
* 伸展树(splay tree 分裂树)
|
* 伸展树(splay tree 分裂树)
|
||||||
* 平衡二叉树AVL
|
* 平衡二叉树AVL
|
||||||
* 红黑树
|
* 红黑树
|
||||||
* B树,B+,B*
|
* B树,B+,B*
|
||||||
* R树
|
* R树
|
||||||
|
|
||||||
|
|
||||||
* Trie树(前缀树)
|
* Trie树(前缀树)
|
||||||
* 后缀树
|
* 后缀树
|
||||||
* 最优二叉树(赫夫曼树)
|
* 最优二叉树(赫夫曼树)
|
||||||
|
|
||||||
|
|
||||||
* 二叉堆 (大根堆,小根堆)
|
* 二叉堆 (大根堆,小根堆)
|
||||||
* 二项树
|
* 二项树
|
||||||
* 二项堆
|
* 二项堆
|
||||||
@@ -196,7 +190,7 @@ http://openjudge.cn/ POJ。这有个POJ上的ACM训练方案 http://www.java3z
|
|||||||
http://ac.jobdu.com/index.php 九度OJ
|
http://ac.jobdu.com/index.php 九度OJ
|
||||||
|
|
||||||
|
|
||||||
## C语言代码规范
|
## 补充:C语言代码规范
|
||||||
|
|
||||||
这里面的算法代码均使用C语言完成,养成良好的代码规范习惯,不但可以写出优质的代码,也可以更快的阅读其他优秀开源代码。代码规范主要有:
|
这里面的算法代码均使用C语言完成,养成良好的代码规范习惯,不但可以写出优质的代码,也可以更快的阅读其他优秀开源代码。代码规范主要有:
|
||||||
|
|
||||||
|
|||||||
+180
@@ -0,0 +1,180 @@
|
|||||||
|
|
||||||
|
## 散列表
|
||||||
|
|
||||||
|
散列表使用某种算法操作(散列函数)将键转化为数组的索引来访问数组中的数据,这样可以通过Key-value的方式来访问数据,达到常数级别的存取效率。现在的nosql数据库都是采用key-value的方式来访问存储数据。
|
||||||
|
|
||||||
|
散列表是算法在时间和空间上做出权衡的经典例子。如果没有内存限制,我们可以直接将键作为数组的索引,所有的操作操作只需要一次访问内存就可以完成。但这种情况不太现实。
|
||||||
|
|
||||||
|
|
||||||
|
### 散列函数
|
||||||
|
|
||||||
|
散列函数就是将键转化为数组索引的过程。且这个函数应该易于计算且能够均与分布所有的键。
|
||||||
|
|
||||||
|
散列函数最常用的方法是`除留余数法`。这时候被除数应该选用`素数`,这样才能保证键值的均匀散步。
|
||||||
|
|
||||||
|
散列函数和键的类型有关,每种数据类型都需要相应的散列函数;比如键的类型是整数,那我们可以直接使用`除留余数法`;这里特别说明下,大多数情况下,键的类型都是字符串,这个时候我们任然可以使用`除留余数法`,将字符串当做一个特别大的整数。
|
||||||
|
|
||||||
|
```
|
||||||
|
int hash = 0;
|
||||||
|
for (int i=0;i<s.length();i++){
|
||||||
|
hash = (R*hash +s.charAt(i)%M);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
当然,还有其他的散列函数,如`平方取中法`, `随机数法`等。
|
||||||
|
|
||||||
|
### 碰撞解决
|
||||||
|
|
||||||
|
散列函数另外一个要处理的问题就是碰撞问题,也就是多个键的散列值相同的情况。常见的处理方法有:
|
||||||
|
|
||||||
|
1. 拉链法
|
||||||
|
2. 线性探测法
|
||||||
|
|
||||||
|
|
||||||
|
#### 拉链法
|
||||||
|
|
||||||
|
将大小为M的数组中的每个元素指向一条链表,链表中的每个节点都存储了散列值为该元素索引的键值对。每条链表的平均长度是N/M,N是键值对的总个数。
|
||||||
|
|
||||||
|
添加操作:
|
||||||
|
1. 通过hash函数得到hashCode
|
||||||
|
2. 通过hashcode得到index
|
||||||
|
3. 如果index处没有链表,建立好新结点,作为新链表的首结点
|
||||||
|
4. 如果index处已经有链表,先要遍历看key是否已经存在,如果存在直接返回,如果不存在,加入链表头部
|
||||||
|
|
||||||
|
|
||||||
|
删除操作:
|
||||||
|
1. 通过hash函数得到hashCode
|
||||||
|
2. 通过hashcode得到index
|
||||||
|
3. 遍历链表,删除结点
|
||||||
|
|
||||||
|
|
||||||
|
#### 线性探测法
|
||||||
|
|
||||||
|
使用大小为M的数组保存N个键值对,当碰撞发生时,直接检查散列表中的下一个位置。
|
||||||
|
|
||||||
|
|
||||||
|
### 数据结构和算法
|
||||||
|
|
||||||
|
这里给出拉链法构造的hashmap的算法,表示如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
typedef char * Key;
|
||||||
|
typedef int value;
|
||||||
|
typedef unsigned int Hash;
|
||||||
|
|
||||||
|
/*每个节点表示*/
|
||||||
|
typedef struct Entry{
|
||||||
|
Hash hash;
|
||||||
|
Key key;
|
||||||
|
Value value;
|
||||||
|
Entry *next;
|
||||||
|
}Entry;
|
||||||
|
|
||||||
|
typedef struct HashMap{
|
||||||
|
Entry **heads;
|
||||||
|
unsigned int size; /* 数组大小*/
|
||||||
|
unsigned int usage;/* 键值对的个数*/
|
||||||
|
}HashMap;
|
||||||
|
|
||||||
|
Hash hashCode(Key);
|
||||||
|
HashMap *create(unsigned int size);
|
||||||
|
HashMap *put(HashMap *,Key ,Value);
|
||||||
|
int get(HashMap *,Key);
|
||||||
|
|
||||||
|
HashMap *_putInHead(HashMap *,int index,Key,Value);
|
||||||
|
HashMap *_putInList(HashMap *,int index,Key,Value);
|
||||||
|
|
||||||
|
Hash hashCode(char *key){
|
||||||
|
int offset = 5;
|
||||||
|
Hash hashCode = 0;
|
||||||
|
while(*key){
|
||||||
|
hashCode = (hashCode << offset) + *key++;
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *create(unsigned int size){
|
||||||
|
HashMap *hashMap = malloc(sizeof(HashMap));
|
||||||
|
hashMap->size = size;
|
||||||
|
hashMap->usage = 0;
|
||||||
|
hashMap->heads = calloc(size,sizeof(Entry *));
|
||||||
|
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *put(HashMap *hashMap,Key key,Value value){
|
||||||
|
if (key == NULL){
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
Hash hash = hashCode(key);
|
||||||
|
int index = hash & (size-1);/* */
|
||||||
|
if (hashMap->heads[index] == NULL){
|
||||||
|
_putInHead(hashMap,index,key,value);
|
||||||
|
}else{
|
||||||
|
_putInList(hashMap,index,key,value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value get(HashMap hashMap*,Key key){
|
||||||
|
if (key == NULL){
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
Hash hash = hashCode(key);
|
||||||
|
int index = hash & (size-1);/* */
|
||||||
|
|
||||||
|
Entry *entry = hashMap->heads[index];
|
||||||
|
while(entry != NULL){
|
||||||
|
if (entry->hash == hash){
|
||||||
|
return entry->value;
|
||||||
|
}
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *_putInHead(HashMap *hashMap,int index,Key key,Value value){
|
||||||
|
Entry *newHead = malloc(sizeof(Entry));
|
||||||
|
newHead->hash = hash;
|
||||||
|
newHead->key = key;
|
||||||
|
newHead->value = value;
|
||||||
|
|
||||||
|
hashMap->heads[index] = newHead;
|
||||||
|
(hashMap->usage)++;
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *_putInList(HashMap *hashMap,int index,Key key,Value value){
|
||||||
|
Entry *lastEntry = hashMap->heads[index];
|
||||||
|
while(lastEntry != NULL){
|
||||||
|
if (lastEntry->hash == hash){
|
||||||
|
return hashMap;
|
||||||
|
}else{
|
||||||
|
lastEntry = lastEntry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastEntry = malloc(sizeof(Entry));
|
||||||
|
lastEntry->hash = hash;
|
||||||
|
lastEntry->key = key;
|
||||||
|
lastEntry->value = value;
|
||||||
|
lastEntry->next = hashMap->heads[index];
|
||||||
|
|
||||||
|
hashMap->heads[index] = lastEntry;
|
||||||
|
(hashMap->usage)++;
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
《Algorithms》
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user