【数据结构实训】哈希表设计

[问题描述]

针对某个集体中人名设计一个哈希表,使得平均查找长度不超过2,并完成相应的建表和查表程序。

[基本要求]

假设人名为中国人姓名的汉语拼音形式。待填入哈希表的人名共有30个,取平均查找长度的上限为2。哈希函数用除留余数法构造,用线性探测再散列法或链地址法处理冲突。

[测试数据]

取自己周围较熟悉的30个人名。

[选作内容]

(1) 从教科书上介绍的集中哈希函数构造方法中选出适用者并设计几个不同的哈希函数,比较他们的地址冲突率(可以用更大的名字集合作实验)。
(2) 研究这30个人名的特点,努力找一个哈希函数,使得对于不同的拼音名一定不发生地址冲突。
(3)在哈希函数确定的前提下尝试各种不同处理冲突的方法,考察平均查找长度的变化和造好的哈希表中关键字的聚集性。

[代码实现]

/*
0!='0'
*/
#include<queue>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define scnaf scanf
#define ll long long
const int N=1000;
int n=30,l=0x3f3f3f3f,r=0,flag,cut=0;
char s[N][20],x[20];
int hashnum1(int k)//1.hash函数|取余法
{
    r=max(r,k%31+1);
    l=min(l,k%31+1);
    return k%31+1;
}
int hashnum2(int k)//2.hash函数|数字分析法
{
    int p,i=1,t=0;
    while(k)
    {
        p=k%10;
        if(i<3)
            t+=p*i++;
        else t+=p*i--;
        k/=10;
    }
    r=max(r,t);//
    l=min(l,t);
    return t;//
}
int hashnum3(char asc[])//直接定址法,该法针对所给30个人名样本不产生冲突
{
    int p=0,i,j;
    for(i=0,j=0;asc[i];i++,j++)
        p+=asc[i]*i;
    p=(p/1000*10+p/100%10)*(p/10%10*10+p%10);
    p/=10;
    r=max(r,p);
    l=min(l,p);
    return p;
}
int slove(char asc[],int f)//分配哈希函数
{
    int ans=0,i;
    for(i=0;asc[i];i++)
        ans+=asc[i];
    switch(f)
    {
        case 0:return ans;
        case 1:return hashnum1(ans);
        case 2:return hashnum2(ans);
        case 3:return hashnum3(asc);
    }
}
/*****以下链地址法解决冲突******/
struct Node
{
    char key[20];//名字
    struct Node *next;
};
struct Hash
{
    int sum;//冲突次数
    struct Node *data;//结点
}h[N];
void init()//malloc->free
{
    struct Node *q,*p;
    for(int i=l;i<=r;i++)
    {
        h[i].sum=0;
        p=h[i].data;
        while(p!=NULL)
        {
            q=(*p).next;
            free(p);
            p=q;
        }
    }
    for(int i=l;i<=r;i++)
        h[i].data=NULL;
    l=0x3f3f3f3f,r=0;
}
void inserthash(int k,char asc[])//插入
{
    struct Node *q;
    q=(struct Node *)malloc(sizeof(struct Node));
    strcpy((*q).key,asc);
    (*q).next=h[k].data;
    h[k].data=q;
    h[k].sum++;
}
int searchhash(int k,char asc[])//查找
{
    struct Node *p=h[k].data;
    int cut=0;
    while(p!=NULL)
    {
        cut++;
        if(strcmp(asc,(*p).key)==0)
            return cut;
        p=(*p).next;
    }
    return 0;
}
void printff()//打印哈希表
{
    printf("哈希表如下所示:\n");
    for(int i=l;i<=r;i++)
    {
        printf("%d",i);
        if(h[i].data!=NULL)
        {
            struct Node *p=h[i].data;
            while(p!=NULL)
            {
                printf(" ->%s",(*p).key);
                p=(*p).next;
            }
        }
        printf("\n");
    }
}
void chongtu()//平均查找长度
{
    int i,k=0;
    for(i=1;i<=n;i++)
        k+=searchhash(slove(s[i],flag),s[i]);
    switch(flag)
    {
        case 1:
        {
            printf("您选择的是取余法,平均查找长度为:%.2lf\n",k*1.0/n);
            return ;
        }
        case 2:
        {
            printf("您选择的是数字分析法,平均查找长度为:%.2lf\n",k*1.0/n);
            return ;
        }
        case 3:
        {
            printf("您选择的是直接定址法,平均查找长度为:%.2lf\n",k*1.0/n);
        }
    }
}
/**********************************/
/*****以下开放地址法解决冲突******/
struct ss
{
    char name[20];
    int length;
    int emp;
}H[N];
void init2()//清零
{
    memset(H,0,sizeof(H));
    l=0x3f3f3f3f,r=0;
}
void inserthash2(int k,char asc[])//插入
{
    int cnt=1,p=1;
    while(H[k].emp)
    {
        if(cnt&1)
            k=(k+p*p)%r;
        else k=(k-p*p)%r,p++;
        cnt++;
    }
    strcpy(H[k].name,asc);
    H[k].length=cnt;
    H[k].emp=1;
}
int searchhash2(int k,char asc[])//查找
{
    int cnt=0,p=1;
    while(H[k].emp)
    {
        cnt++;
        if(strcmp(asc,H[k].name)==0)
            return cnt;
        if(cnt&1)
            k=(k+p*p)%r;
        else k=(k-p*p)%r,p++;
    }
    if(!H[k].emp)
        return 0;
}
void printff2()//打印哈希表
{
    printf("哈希表如下所示:\n");
    for(int i=l;i<=r;i++)
    {
        printf("%d",i);
        if(H[i].emp)
            printf(" ->%s %d",H[i].name,H[i].length);
        printf("\n");
    }
}
void chongtu2()//平均查找长度
{
    int i,k=0;
    for(i=1;i<=n;i++)
        k+=searchhash2(slove(s[i],flag),s[i]);
    switch(flag)
    {
        case 1:
        {
            printf("您选择的是取余法,平均查找长度为:%.2lf\n",k*1.0/n);
            return ;
        }
        case 2:
        {
            printf("您选择的是数字分析法,平均查找长度为:%.2lf\n",k*1.0/n);
            return ;
        }
        case 3:
        {
            printf("您选择的是直接定址法,平均查找长度为:%.2lf\n",k*1.0/n);
        }
    }
}
/*********************************/

int main()
{
    int i,j,k,p,v;
    printf("请输入姓名:\n");
    for(i=1;i<=n;i++)
        scnaf("%s",s[i]);
    printf("\n请选择冲入处理方式:\n 1.链地址法\n 2.开放定址法\n");
    scanf("%d",&v);
    if(v&1)
    {
        printf("\n您选择的是链地址法\n");
        printf("\n请选择哈希函数:\n 1.取余法\n 2.数字分析法\n 3.直接定址法\n");
        scanf("%d",&flag);
        init();
        for(i=1;i<=n;i++)
        {
            k=slove(s[i],flag);
            inserthash(k,s[i]);
        }
        switch(flag)
        {
            case 1:printf("\n您选择的是取余法\n");break;
            case 2:printf("\n您选择的是数字分析法\n");break;
            case 3:printf("\n您选择的是直接定址法\n");break;
        }
        while(1)
        {
            printf("\n请输入:\n 0.退出\n 1.插入名字\n 2.查找名字\n 3.打印哈希表\n 4.平均查找长度\n");
            scnaf("%d",&p);
            if(!p)
            {
                init();
                break;
            }
            switch(p)
            {
                case 1:
                {
                    printf("\n请输入您要添加的名字:\n");
                    scnaf("%s",s[++n]);
                    k=slove(s[n],flag);
                    inserthash(k,s[n]);
                    break;
                }
                case 2:
                {
                    printf("\n请输入您要查找的名字:\n");
                    scnaf("%s",x);
                    k=slove(x,flag);
                    j=searchhash(k,x);
                    if(!j)
                        printf("\n查找失败,请重新输入\n");
                    else printf("\n查找成功,查找长度为:%d\n",j);
                    break;
                }
                case 3:printff();break;
                case 4:chongtu();break;
            }
        }
    }
    else
    {
        printf("\n您选择的是开放定址法\n");
        printf("\n请选择哈希函数:\n 1.取余法\n 2.数字分析法\n 3.直接定址法\n");
        scanf("%d",&flag);
        init2();
        for(i=1;i<=n;i++)
        {
            k=slove(s[i],flag);
            inserthash2(k,s[i]);
        }
        switch(flag)
        {
            case 1:printf("\n您选择的是取余法\n");break;
            case 2:printf("\n您选择的是数字分析法\n");break;
            case 3:printf("\n您选择的是直接定址法\n");break;
        }
        while(1)
        {
            printf("\n请输入:\n 0.退出\n 1.插入名字\n 2.查找名字\n 3.打印哈希表\n 4.平均查找长度\n");
            scnaf("%d",&p);
            if(!p)
            {
                init2();
                break;
            }
            switch(p)
            {
                case 1:
                {
                    printf("\n请输入您要添加的名字:\n");
                    scnaf("%s",s[++n]);
                    k=slove(s[n],flag);
                    inserthash2(k,s[n]);
                    break;
                }
                case 2:
                {
                    printf("\n请输入您要查找的名字:\n");
                    scnaf("%s",x);
                    k=slove(x,flag);
                    j=searchhash2(k,x);
                    if(!j)
                        printf("\n查找失败,请重新输入\n");
                    else printf("\n查找成功,查找长度为:%d\n",j);
                    break;
                }
                case 3:printff2();break;
                case 4:chongtu2();break;
            }
        }
    }
    return 0;
}
/*
int main()
{
    char v[35][20];
    int a[35];
    for(int i=1;i<=n;i++)
        scnaf("%s",v[i]),a[i]=slove(v[i],1);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
        printf("%d\n",a[i]);
    return 0;
}

/
//29|22-3 31|22-2
/

700~140
yangxinyi
lihaoyu
xuhaidong
songzongzui
wangzongyao
zhangwenqi
liudengtian
huoyuqing
fangyuanyuan
wangweiyi
wangyan
zhoumeichen
chenshuai
hanmeng
liyaran
yuxiaolei
lujiale
shichenghong
liuwanyou
chaiwenli
wanghaozhi
zhaobowen
gaoruiying
lijinfang
sunpeng
sunshanpeng
wanghongyang
xinghaora
qinyichen
xiuyuhang
*/

  • 13
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
哈希表是一种基于哈希函数进行快速查找的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。哈希表设计思路如下: 1. 哈希函数的设计:哈希函数是哈希表的核心,它将关键字映射到哈希表中的位置。一个好的哈希函数应该具有以下特点: - 映射范围广:哈希函数应该将关键字均匀地映射到哈希表中的位置,避免出现大量的哈希冲突。 - 计算速度快:哈希函数的计算速度应该尽可能快,以提高哈希表的访问速度。 - 低冲突率:哈希函数应该尽可能地避免哈希冲突,以提高哈希表的访问效率。 2. 哈希冲突的解决:由于哈希函数的映射范围是有限的,所以不同的关键字可能会映射到同一个位置,这就是哈希冲突。哈希冲突的解决方法有以下两种: - 链地址法:将哈希表中的每个位置都连接一个链表,当发生哈希冲突时,将新的关键字插入到链表的末尾。 - 开放地址法:当发生哈希冲突时,通过某种算法找到哈希表中的下一个空位置,将新的关键字插入到该位置。 3. 哈希表的增删查改操作:哈希表的增删查改操作都需要先通过哈希函数找到关键字在哈希表中的位置,然后再进行相应的操作。具体操作如下: - 插入操作:将新的关键字插入到哈希表中的对应位置,如果发生哈希冲突,则按照链地址法或开放地址法进行解决。 - 删除操作:将关键字从哈希表中对应位置删除,如果该位置上有链表,则需要遍历链表找到对应的关键字进行删除。 - 查找操作:通过哈希函数找到关键字在哈希表中的位置,如果该位置上有链表,则需要遍历链表找到对应的关键字进行查找。 - 修改操作:通过哈希函数找到关键字在哈希表中的位置,如果该位置上有链表,则需要遍历链表找到对应的关键字进行修改。 下面是一个使用链地址法实现的哈希表的Python代码示例: ```python class ListNode: def __init__(self, key=None, value=None): self.key = key self.value = value self.next = None class MyHashMap: def __init__(self): self.size = 1000 self.table = [None] * self.size def _hash(self, key): return key % self.size def put(self, key, value): index = self._hash(key) if not self.table[index]: self.table[index] = ListNode(key, value) else: node = self.table[index] while node: if node.key == key: node.value = value return if not node.next: break node = node.next node.next = ListNode(key, value) def get(self, key): index = self._hash(key) node = self.table[index] while node: if node.key == key: return node.value node = node.next return -1 def remove(self, key): index = self._hash(key) node = prev = self.table[index] if not node: return if node.key == key: self.table[index] = node.next else: node = node.next while node: if node.key == key: prev.next = node.next break node, prev = node.next, prev.next ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值