算法导论 第11章 散列表
11.1 直接寻址表
如果某应用要用到一个动态集合,其中每个元素都是全域U={0,1….,m}中的一个关键字
为表示动态集合,使用数组。称为直接寻址表,记为T[m],其中每个位置称为一个槽slot,对应于全域中的一个关键字
槽k指向集合中一个关键字为k的元素。如果该集合中没有关键字为k的元素,则T[k]=NIL;
程序实现思路:
- 可以用静态数组实现,因为数组的下标等于关键字,所以可以用数组存放对应的元素。有几个缺点:首先难以确定NULL,由于数组元素的类型是确定的,所以如何用某个数来表示NULL,表示数组中没有此元素,是一个问题。如果具体问题中,用不上其某个元素,可以使用该元素作为数组的NULL。
- 用指针数组实现,该方法普适程度高,数组中的元素都是指针,指向结构体,该结构体有两个参量,一个是关键字key,一个是卫星数据satellite。如果没有该关键字,则可以为指针赋值NULL,较容易实现。
下面给出指针数组的C语言实现
```
#include<stdio.h>
typedef int Elemtype ; //定义结构体中存储的元素类型 注:若要改变其元素类型,在print_hash()函数中对应的printf函数也要改变
define MAX 100 //定义全集大小
typedef struct hash //定义结构体
{
int key; //关键字
Elemtype satellite; //元素类型
}Hash; //结构体名称为Hash
//查找一个给定关键字为key的元素
Elemtype Direct_address_search(Hash* a[],int key)
{
if(a[key]==NULL) //判断关键字是否在
{
printf("the key element is not in list\n");
exit(0);
}
return a[key]->satellite; //返回该关键字对应的数据
}
//插入一个元素
void Direct_address_insert(Hash* a[],Hash* x)
{
a[x->key]=x;
}
//删除一个元素
void Direct_address_delete(Hash* a[],Hash* x)
{
a[x->key]=NULL;
}
//打印直接寻址表
void print_hash(Hash* a[])
{
int i=0;
while(i<MAX)
{
if(a[i]!=NULL)
{
printf("%d %d\n",a[i]->key,a[i]->satellite);
}
i++;
}
}
//测试函数
int main()
{
Hash* data[MAX];
int i;
int x;
//创建直接寻址表,并初始化为空
for(i=0;i<MAX;i++)
{
data[i]=(Hash*)malloc(sizeof(Hash));
// printf("%d\t",data[i]);
data[i]=NULL;
// printf("%d\n",data[i]);
}
//给出元素(5,100),(2,200),(0,300)进行测试
Hash* a=(Hash*)malloc(sizeof(Hash));
a->key=5;
a->satellite=100;
Hash* b=(Hash*)malloc(sizeof(Hash));
b->key=2;
b->satellite=200;
Hash* c=(Hash*)malloc(sizeof(Hash));
c->key=0;
c->satellite=300;
Direct_address_insert(data,a);
Direct_address_insert(data,b);
Direct_address_insert(data,c);
x=Direct_address_search(data,5);
Direct_address_delete(data,a);
print_hash(data);
//释放内存
for(i=0;i<MAX;i++)
free(data[i]);
free(a);
free(b);
free(c);
return 0;
}
```
练习11.1-1
查找S中最大的数,需要遍历整个表,所以复杂度为O(n)
下面给出其代码:
```
Elemtype direct_address_Maxsearch(Hash* t[],int m)
{
int i;
Elemtype max;
for(i=0;i<m;i++)
{
if(a[i]!=NULL)
{
max=a[i]->satellite;
break;
}
}
if(i==m)
{
printf("no element in the hast table\n");
exit(0);
}
for(;i<m;i++)
{
if(a[i]!=NULL&&a[i]->satellite>max)
max=a[i]->satellite;
}
return max;
}
```
习题11.1-2 实现位向量
分析:题目中说用位向量来表示一个包含不同元素的动态集合,并且不需要卫星数据,所以就是给定一个集合,判断整型关键字是否在里面,位向量是0和1的数组。为了节省空间,用一个整型的32位来存储32个数,从0-31,并且该数应该是连续的。不连续的存储需要特定的hash函数来映射。对于给定总数大小n,如果要存储其n个数,用位向量来存储只需(n/32+1)个整型量,主要的操作是插入一个数,删除一个数,搜索一个数。
具体代码如下:
```
#include<stdio.h>
#include<stdlib.h>
#define BIT_INT 32
//定义该结构体
typedef struct bit_vector
{
unsigned int* table;
int size;
} Bitvector;
//初始化
Bitvector* bit_create(int size)
{
Bitvector* bit=(Bitvector*)malloc(sizeof(Bitvector));
bit->size=size/BIT_INT+1;
bit->table=(unsigned int*)calloc(sizeof(int),bit->size);
return bit;
}
//插入key
void Bit_insert(Bitvector* bit,int key)
{
bit->table[key/BIT_INT]|=(1<<(key%BIT_INT));
}
//删除操作
void Bit_delete(Bitvector* bit,int key)
{
bit->table[key/BIT_INT]&=~(1<<(key%BIT_INT));
}
//返回值为1说明存在,为0说明不存在
int Bit_search(Bitvector* bit,int key)
{
return (bit->table[key/BIT_INT]&(1<<(key%BIT_INT)))!=0;
}
//打印元素值
void print_bit(Bitvector* bit)
{
int i;
int size=bit->size*BIT_INT;
for(i=0;i<size;i++)
if(Bit_search(bit,i)==1)
printf("%d\n",i);
printf("\n");
}
//释放内存
void Bit_destory(Bitvector* bit)
{
free(bit->table);
free(bit);
}
int main()
{
Bitvector* bit=bit_create(1024);
int i,x;
Bit_insert(bit,15);
Bit_insert(bit,250);
Bit_insert(bit,900);
print_bit(bit);
Bit_delete(bit,250);
print_bit(bit);
printf("------------------\n");
x=Bit_search(bit,2);
printf("sousuo : %d\n",x);
x=Bit_search(bit,900);
printf("sousuo: %d\n",x);
Bit_destory(bit);
return 0;
}
```
习题11.1-3
分析:该题目可以用链表来解决。对于有相同key值的数,在该key值处用链表链接。
习题11.1-4
想法:
由于大数组太大,不能初始化,我们也就等于不知道到底哪里有真正的数据,于是乎数据不能存储在大数组中,因为你根本不知道到底哪里才是数据。
这里方式是:将数据存储到栈上,栈上的增删查都可以实现O(1),然后在大数组上,对应key的位置的元素,存放栈上对应的下标,这样根据key到大数组中找到栈的下标,然后根据栈的下标又可以找到那个key值对应的数据元素了。
然后,还需要解决如何判断数据是否有效的问题,这个也很简单,经过上面的查找过程,不难发现,如果该数据是有效的,需要满足以下几个条件:
1. key值对应到大数组中位置的值,必须位于[0,栈的栈顶位置]之间,否则肯定不是数据
2. 满足第1条之后,我们到栈上对应的位置,找到那个元素数据,它的key值要反过来等于我们原始的key值,否则表示这个数据也是不存在的.
参考来源
代码如下:
```
#include<stdio.h>
#define MAX 100
#define large_number 10000
int Hash[large_number];
struct
{
int Stack[MAX];
int top;
} stack;
void init(stack* s)
{
s->top=-1;
}
//search the element k
int search(int* hash,stack* s,int value)
{
int num=hash[value];
if(num>0&&num<=s->top&&s.Stack[num]==value)
return num;
return 0;
}
void insert(int* hash,stack* s,int x)
{
if(search(hash,s,x)!=0)
{
printf("already exist\n");
return ;
}
s->top++;
s.Stack[s->top]=value;
hash[value]=s->top;
}
void delete(int* hash,stack* s,int value)
{
int num=search(hash,s,value);
if(num)
{
int val=s->Stack[s->top];
s->Stack[num]=val;
s->top--;
hash[val]=num;
printf("delete successfully\n");
}
else
printf("delete failed\n");
}
```