数据结构与算法(哈希表)
哈希函数:在记录的关键字与记录的存储地址之间建立的一 种对应关系叫哈希函数。
哈希函数是一种映象,是从关键字空间到存储地址空间的一 种映象。可写成:addressi=H(keyi) ,其中i是表中某 个元素。
哈希表:应用哈希函数,由记录的关键字确定记录在表中的 地址,并将记录放入此地址,这样构成的表叫哈希
★哈希表的特点就是数据与其在表中的位置存在相关性,也就是有关系的,通过数据应该可以计算其位置,哈希表最大的特点是是可以快速实现查找,插入和删除。因为它独有的特点,Hash表经常用来解决大数据问题
1.哈希表的基本思想
数组的最大的特点是:寻址容易,插入和删除困难;而链表正好相反,寻址困难,而插入和删除操作容易。那么如果能够结合两者的优点。做出一种寻址,插入和删除操作同样快速的数据结构,这就是哈希表。哈希表是这样一个集查找,插入和删除操作于一身的数据结构
2.哈希表结构
3.基本操作
//创建结构体存储键值对
struct element {
int key;
int value;
};
//创建结构体作为一级结构
struct table;
//初始化哈希表存储空间,返回指向该空间的结构体指针变量
struct table *table_init();
void table_free(struct table *t);
void table_clear(struct table *t);
int table_isempty(struct table *t);
int table_count(struct table *t);
//将新的键值对存储到哈希表存储结构中
void table_put(struct table *t, int key, int value);
// void table_put(struct table *t, struct element e);
//根据键值将键值对从哈希表存储结构中移除
struct element table_remove(struct table *t, int key);
/* 如果找到了返回key的元素,如果没找到返回{-1,-1}*/
struct element table_get(struct table *t, int key);
4.存储结构
结构①
核心思想:使用结构体数组存储键值对作为哈希表的存储结构核心
结构②
核心思想:将链表连接到数组中,数组变量作为各链表的索引达到方便操作数据的目的
5.代码实现
table.h
#ifndef __TABLE_H__
#define __TABLE_H__
//定义结构体存储键值对
struct element {
int key;
int value;
};
struct table;
struct table *table_init();
void table_free(struct table *t);
void table_clear(struct table *t);
int table_isempty(struct table *t);
int table_count(struct table *t);
void table_put(struct table *t, int key, int value);
struct element table_remove(struct table *t, int key);
struct element table_get(struct table *t, int key);
#endif
main.c
//将数据元素按照键值关系存储到哈希表存储结构中
#include <stdio.h>
#include <stdlib.h>
#include "table.h"
int main(int argc, char *argv[])
{
//定义结构体指针变量t指向table结构体类型的数据
struct table *t = NULL;
int i;
//定义element结构体类型变量e
struct element e;
//t指向已开辟的内存空间
t = table_init();
table_put(t,0,55);
table_put(t, 1, 100);
table_put(t, 2, 200);
table_put(t, 3, 300);
table_put(t, 5, 500);
//键相同会产生"冲突",键不变,值覆盖
table_put(t, 3, 333);
//循环遍历,将键值取出
for (i=0; i<10; i++) {
e = table_get(t, i);
printf("the element is <%d, %d>
", e.key, e.value);
}
table_free(t);
system("PAUSE");
return 0;
}
table.c
结构⑴
#include "table.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
//定义数组的初始长度
#define TABLE_INIT_SIZE 1
//定义数组每次扩容增加的长度
#define TABLE_INCR_SIZE 1
//定义table结构体类型作为一级结构体,指针变量elements指向在.h文件中定义的element结构体类型数据元素
//count记录结构体数组中数据元素的个数,size记录结构体数组的长度
struct table {
struct element *elements;
int count;
int size;
};
//创建以结构体数组为核心的哈希表存储结构
struct table *table_init()
{
struct table *t = NULL;
t = (struct table *)malloc(sizeof(struct table));
if (t == NULL) return NULL;
assert(t != NULL);
t->elements = NULL;
t->size = 0;
t->count = 0;
t->elements = (struct element *)malloc(sizeof(struct element) * TABLE_INIT_SIZE);
if (t->elements == NULL) {
free(t);
return NULL;
}
assert(t->elements != NULL);
memset(t->elements, 0, sizeof(struct element) * TABLE_INIT_SIZE);
t->size = TABLE_INIT_SIZE;
return t;
}
//通过table_free()释放内存空间
void table_free(struct table *t)
{
assert(t != NULL);
assert(t->elements != NULL);
free(t->elements);
free(t);
return;
}
//清空哈希表存储结构中的数据元素
void table_clear(struct table *t)
{
assert(t != NULL);
assert(t->elements != NULL);
t->count = 0;
return;
}
//查看哈希表存储结构中是否为空
int table_isempty(struct table *t)
{
assert(t != NULL);
assert(t->elements != NULL);
return (t->count == 0);
}
//计算哈希表存储结构中数据元素的个数
int table_count(struct table *t)
{
assert(t != NULL);
assert(t->elements != NULL);
return t->count;
}
//将键值对push到哈希表存储结构中
void table_put(struct table *t, int key, int value)
{
//如果已存储的数据元素等于已定义的数组的长度,进行扩容操作
if (t->count == t->size) {
t->elements = (struct element *)realloc(t->elements, sizeof(struct element) * (t->size + TABLE_INCR_SIZE));
if (t->elements == NULL) {
perror("struct table_put realloc error");
exit(1);
}
t->size += TABLE_INCR_SIZE;
}
struct element e = {key, value};
int i=0;
//如果产生"冲突",键不变,值覆盖
for (; i<t->count; i++) {
if (t->elements[i].key == e.key) {
t->elements[i].value = e.value;
return;
}
}
t->elements[i] = e; // t->count
t->count++;
return;
}
// void table_put(struct table *t, struct element e);
//将制定键的键值对移除
struct element table_remove(struct table *t, int key)
{
struct element e = {-1, -1};
int i=0;
//定义pos标识键相同与制定键相同的键值对所在结构体中的位置
int pos = -1;
for (; i<t->count; i++) {
e=t->elements[key];
if (t->elements[i].key == key) {
pos = i;
break;
}
}
//后面的数据元素往前移位
if (pos != -1) {
e = t->elements[pos];
for (; pos < t->count-1; pos++) {
t->elements[pos] = t->elements[pos+1];
}
t->count--;
}
return e;
}
struct element table_get(struct table *t, int key)
{
int i;
struct element e = {-1, -1}; /*未找到返回的值*/
for (i=0;i<t->count; i++) {
if (t->elements[i].key == key) return t->elements[i];
}
return e;
}
结构⑵
#include "table.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
//定义作为索引的数组的长度
#define TABLE_SLOTS_INIT_SIZE 10
//定义table_node的结构体类型作为链表的数据节点,e为element结构体类型的数据元素(element结构体在.h文件中已定义)
//next指向下一个table_node数据类型的数据
struct table_node {
struct element e;
struct table_node *next;
};
//table结构体类型作为一级结构,数据节点地址存储在数组中,数组的首地址存放在slots指针变量中
struct table {
struct table_node **slots;
int count;
int size;
};
struct table *table_init()
{
struct table *t = NULL;
t = (struct table *)malloc(sizeof(struct table));
if (t == NULL) return NULL;
assert(t != NULL);
t->slots = NULL;
t->count = 0;
t->size = 0;
t->slots = (struct table_node **)malloc(sizeof(struct table_node *) * TABLE_SLOTS_INIT_SIZE);
if (t->slots == NULL) {
free(t);
return NULL;
}
assert(t->slots != NULL);
memset(t->slots, 0, sizeof(struct table_node *) * TABLE_SLOTS_INIT_SIZE);
t->size = TABLE_SLOTS_INIT_SIZE;
return t;
}
void table_free(struct table *t)
{
struct table_node *node = NULL;
int i;
for (i=0; i<t->count; i++) {
while (t->slots[i] != NULL) {
node = t->slots[i];
t->slots[i] = node->next;
free(node);
}
assert(t->slots[i] == NULL);
}
free(t->slots);
free(t);
return;
}
void table_clear(struct table *t)
{
struct table_node *node = NULL;
int i;
assert(t != NULL);
assert(t->slots != NULL);
for (i = 0; i < t->count; i++) {
while (t->slots[i] != NULL) {
node = t->slots[i];
t->slots[i] = node->next;
free(node);
}
assert(t->slots[i] == NULL);
}
t->count = 0;
return;
}
int table_isempty(struct table *t)
{
assert(t != NULL);
assert(t->slots != NULL);
return (t->count == 0);
}
int table_count(struct table *t)
{
assert(t != NULL);
assert(t->slots != NULL);
return (t->count);
}
void table_put(struct table *t, int key, int value)
{
struct table_node *p = NULL;
struct table_node *node = NULL;
p = t->slots[key % t->size];
while (p != NULL) {
if (p->e.key == key) {
p->e.value = value;
return;
}
p = p->next;
}
node = (struct table_node *)malloc(sizeof(struct table_node));
if (node == NULL) {
perror("table_put malloc error");
exit(1);
}
assert(node != NULL);
node->e.key = key;
node->e.value = value;
/* 把链上结点挂在node结点之后,注意,链上没有结点也没关系*/
node->next = t->slots[key % t->size];
t->slots[key % t->size] = node;
t->count++;
return;
}
// void table_put(struct table *t, struct element e);
struct element table_remove(struct table *t, int key)
{
struct table_node *p = NULL;
struct table_node *node = NULL;
struct element e = {-1, -1};
p = t->slots[key % t->size];
if (p->e.key == key) {
node = p;
t->slots[key % t->size] = node->next;
e = node->e;
free(node);
t->count--;
return e;
}
while (p != NULL && p->next != NULL) {
if (p->next->e.key == key) {
node = p->next;
p->next = node->next;
e = node->e;
t->count--;
free(node);
return e;
}
p = p->next;
}
return e;
}
/* 如果找到了返回key的元素,如果没找到返回{-1,-1}*/
struct element table_get(struct table *t, int key)
{
struct table_node *p = NULL;
struct element e = {-1, -1};
p = t->slots[key % t->size];
while (p != NULL) {
if (p->e.key == key) {
e = p->e;
break;
}
p = p->next;
}
return e;
}
6.编译结果