哈希表

哈希表是一种数据结构,它可以提供快速的插入操作和查找操作。不论哈希表中有多少数据,插入和删除都只需要接近常O(1)的时间级。
缺点:它是基于数组的,数据创建后难以扩展。
高级计算机语言的编译器,通常用哈希表保留符号表,符号表记录了程序员声明的所有变量和函数名,以及它们在内存中的地址,程序需要快速地访问这些名字,所以哈希表是理想的数据结构。
例如,想在内存中存储50000个英文单词,考虑用数组来存储,每个单词占据一个数组单元。
如何把代表单个字母的数字(a=1,b=2,c=3,...,z=26)组合成代表整个单词的数字呢?有许多方法,下面看两种有代表性的方法:
1.把单词每个字符的代码求和,假设约定单词有十个字母,那么,第一个单词a的编码为1,字典最后一个可能的单词是10个z,所有的字符编码的和是26*10=260。因此,单词编码的范围是1~260,但词典中有5000个单词,所以没有足够数组下标来索引那么多的单词,每个数组数据项大概要存储192个单词。
可以考虑每个数组数据项包含一个子数组或链表,但这个办法降低了存取速度,插入数据项确实很快,但要在192个单词中找到其中一个,速度就很慢了。
所以说,这个方法没有把单词分得足够开,所以结果数组能表达的元素太少,需要扩展数组表达下标的空间。
2.哈希化:可以通过把单词每个字母乘以7的适当次幂,再相加,使单词成为一个巨大的数字,然后使用取余操作,把得到的巨大整数范围转换成要存储内容的数组下标范围。
哈希函数:arrayIndex = hugeNumber%arraySize
哈希表:把一个大范围的数字哈希成一个小范围的数字,这个小的范围对应数组的下标,使用哈希函数向数组插入数据后,这个数组称为哈希表。
冲突
把巨大的数字空间压缩成较小的数字空间,必然需付出代价,即不能保证每个单词都映射到数组的空白单元,所以必须通过另外的方式解决这个问题。
1.开放地址法:当冲突发生时,通过系统的方法找到数组的一个空位,并把这个单词填入,而不是用哈希函数得到数组下标。
2.链地址法:创建一个存储单词链表的数组,数组内不直接存储单词,这样,当发生冲突时,新的数据项直接到这个数据下标所指的链表中。

开发地址法程序实现:
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define NULLKEY 0  //无记录标志
#define N 10  //数组元素个数

typedef int ElemType;  //关键字为整型
int hashsize[]={11,19,29,37};  //哈希容量递增表,一个合适的素数序列
int m=0;   //哈希表表长,全局变量

typedef struct
{
    ElemType *elem;
    int count;
    int sizeindex;
}HashTable;

//构造一个空的哈希表
int InitHashTable(HashTable *H)
{
    int i;
    (*H).count=0;
    (*H).sizeindex=0;
    m=hashsize[0];
    (*H).elem=(ElemType*)malloc(m*sizeof(ElemType));
    if(!(*H).elem)
        exit(0);
    for(i=0;i<m;i++)
        (*H).elem[i]=NULLKEY;
    return 1;
}

//销毁哈希表
void DestroyHashTable(HashTable *H)
{
    free((*H).elem);
    (*H).elem=NULL;
    (*H).count=0;
    (*H).sizeindex=0;    
}

//一个简单的哈希函数(m为表长)
unsigned Hash(ElemType K)
{
    return K%m;
}

//开放定址法处理冲突
void collision(int *p,int d)  //线性检测再散列
{
    *p=(*p+d)%m;
}

//c用于计冲突次数
int SearchHash(HashTable H,ElemType K,int *p,int *c)
{
    *p=Hash(K);
    while((H.elem[*p]!=NULLKEY)&&(H.elem[*p] != K))
    {
        (*c)++;
        if(*c<m)
            collision(p,*c); 
        else
            break;
    }
    if (K == H.elem[*p])
        return SUCCESS;
    else
        return UNSUCCESS;
}

int InsertHash(HashTable *,ElemType);

//重建哈希表
void RecreateHashTable(HashTable *H)
{
    int i,count=(*H).count;
    ElemType *p,*elem=(ElemType*)malloc(count*sizeof(ElemType));
    p=elem;
    printf("recreate the hashtable\n");
    for(i=0;i<m;i++)
        if((*H).elem[i]!=NULLKEY)
{
*p=*((*H).elem+i);
p++;
}
            
    (*H).count=0;
    (*H).sizeindex++;
    m=hashsize[(*H).sizeindex];
    p=(ElemType*)realloc((*H).elem,m*sizeof(ElemType));
    if(!p)
        exit(0);
    (*H).elem=p;
    for(i=0;i<m;i++)
        (*H).elem[i]=NULLKEY;
    for(p=elem;p<elem+count;p++)
        InsertHash(H,*p);
}

//若冲突次数过多,则重建哈希表
int InsertHash(HashTable *H,ElemType e)
{
    int c,p;
    c=0;
    if(SearchHash(*H,e,&p,&c))
        return DUPLICATE;
    else if(c<hashsize[(*H).sizeindex]/2)
    {
        (*H).elem[p]=e;
        ++(*H).count;
        return 1;
    }
    else
{
RecreateHashTable(H);
InsertHash(H,e);
}
        
    return 0;
}

//按哈希地址的顺序遍历哈希表
void TraverseHash(HashTable H)
{  
    int i;
    printf("hash address:0~%d\n",m-1);
    for(i=0;i<m;i++)
        if(H.elem[i]!=NULLKEY)
printf("address=%d key=%d\n",i,H.elem[i]);
}

//查找关键码为K的元素
int Find(HashTable H,ElemType K,int *p)
{
    int c=0;
    *p=Hash(K);
    while((H.elem[*p]!=NULLKEY) && (H.elem[*p]!=K))
    {
c++;
        if(c<m)
            collision(p,c);
        else
            return UNSUCCESS;
    }
    if (K == H.elem[*p])
        return SUCCESS;
    else
        return UNSUCCESS;
}

int main()
{
    ElemType r[N] = {17,60,29,38,1,2,3,4,60,13};
    HashTable h;
    int i, j, p;
    ElemType k;
    
    InitHashTable(&h);
    for(i=0;i<N-1;i++)
    {
        j=InsertHash(&h,r[i]);
        if(j==DUPLICATE)
            printf("There has record %d,you cannot insert again.\n",r[i]);
    }
    printf("acorrd to the order of address ,the Hash table is: \n");
    TraverseHash(h);
    printf("search number:");
    scanf("%d",&k);
    j=Find(h,k,&p);
    if(j==SUCCESS)
printf("address=%d key=%d\n",p,h.elem[p]);
    else
        printf("cannot find\n");
printf("insert number:");
scanf("%d",&k);
    j=InsertHash(&h,k);
    if(j==DUPLICATE)      
        printf("There has record %d,you cannot insert again.\n",k);
    printf("the hashtable of the address order is:\n");
    TraverseHash(h);    
    DestroyHashTable(&h); 
    return 0; 
}





链地址法程序实现:
#define MAX_NUM 100     /*最大数据个数*/
#define PRIME 11        /*MAX_NUM的质数*/
#define NOTEXISTED NULL
/*定义数据结构*/
typedef struct Person
{
    long id;
    char name[21];
    struct Person *link;
} Student;
 
/*建立哈希表*/
Student *Hashtab[MAX_NUM];
 
/*函数原型声明*/
long hashfun(long);
void insert();
void del();
Student *search(Student *,Student *);
void query();
void show();
 
void main()
{
    char *menu_prompt=
        "=== Hashing Table Program==\n"
        "  1. Insert\n"
        "  2. Delete\n"
        "  3. Show\n"
        "  4. Search\n"
        "  5. Quit\n"
        "Please input a number: ";
    char menusele;
    int i;
 
    /*起始哈希表,将各链表指向NULL*/
    for(i=0;i<MAX_NUM;i++)
        Hashtab[i] = NULL; 
    do
    {
        printf("%s",menu_prompt);
        menusele=toupper(getche());
        puts("");
        switch (menusele)
        {
            case '1' :
                insert();
                break;
            case '2' :
                del();
                break;
            case '3' :
                show();
                break;
            case '4' :
                query();
                break;
            case '5' :
                puts("Bye Bye ^_^");
                break;
            default :
                puts("Invalid choice !!");
        }
    } while(menusele !='5');
}
 
/*哈希函数:*/
/*以除法运算求出记录应存储的位置*/
long hashfun(long key)
{
    return(key % PRIME);
}
 
void insert()
{
    Student  *newnode;
    long index;
 
    /*输入记录*/
    newnode=(Student *)malloc(sizeof(Student));
    newnode->link=NULL;
    printf("Enter id : ");
    scanf("%ld",&newnode->id);
    printf("Enter Name : ");
    scanf("%s",newnode->name);
 
    /*利用哈希函数求得记录地址*/
    index=hashfun(newnode->id);
    /*判断该链表是否为空,若为空则建立链接表*/
    if(Hashtab[index]==NULL )
    {
        Hashtab[index]=newnode;
        printf("Node insert ok!\n");
    }
    else
    {
        /*查找节点是否已存在表中,如未存在,则将此节点插入表前端*/
        if((search(Hashtab[index],newnode))==NOTEXISTED)
        {
            newnode->link=Hashtab[index];
            Hashtab[index]=newnode;
            printf("Node insert ok!\n");
        }
        else
            printf("Record existed...\n");
    }
}
 
/*删除节点函数*/
void del()
{
    Student *node,*node_parent;
    long index;
 
    node=(Student *)malloc(sizeof(Student));
    printf("Enter ID : ");
    scanf("%ld",&node->id);
    /*利用哈希函数转换记录地址*/
    index=hashfun(node->id);
 
    /*搜索节点是否存在,并返回指向该节点的指针*/
    node=search(Hashtab[index],node);
 
    if(node==NOTEXISTED)
        printf("Record not existed...\n");
    else
    {
        /*如节点为表首,则将表指向NULL,否则找到其父节点,并将父节点link指向节点后端*/
        printf("ID : %ld   Name : %s\n",node->id,node->name);
        printf("Deleting record...\n");
        if(node==Hashtab[index])
            Hashtab[index]=NULL;
        else
        {
            node_parent=Hashtab[index];
            while(node_parent->link->id !=node->id)
                node_parent=node_parent->link;
            node_parent->link=node->link;
        }
        free(node);
    }
}
 
/*查找节点函数,如找到节点则返回指向该节点的指针,否则返回NULL*/
Student *search(Student *linklist,Student *Node)
{
    Student *ptr=linklist;
    while (ptr->id !=Node->id && ptr->link !=NULL)
        ptr=ptr->link;
    if(ptr->link == NULL)
        return NOTEXISTED;
else
return ptr;
}
 
/*查询节点函数*/
void query()
{
    Student *query_node;
    long index;
 
    query_node=(Student *)malloc(sizeof(Student));
    printf("Enter ID : ");
    scanf("%ld",&query_node->id);
 
    index=hashfun(query_node->id);
    /*搜索节点*/
    query_node=search(Hashtab[index],query_node);
 
    if(query_node==NOTEXISTED )
        printf("Record not existed...\n");
    else
    {
        printf("ID : %ld  Name : %s\n",
        query_node->id,query_node->name);
    }
}
 
/*显示节点函数,从哈希表一一寻找是否有节点存在*/
void show()
{
    int i;
    Student *ptr;
 
    puts("ID                  NAME");
    puts("------------------------");
    for(i=0;i<MAX_NUM;i++)
    {
        /*表不为空,则显示整张表*/
        if(Hashtab[i] !=NULL)
        {
            ptr=Hashtab[i];
            while(ptr)
{
                printf("%-5ld    %15s\n",ptr->id,ptr->name);
                ptr=ptr->link;
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值