散列之我见

我是自动化专业的应届研究生,最终拿到了tplink、华为、vivo等公司的ssp的offer,分享自己学习过的计算机基础知识(C语言+操作系统+计算机网络+linux)以及数据结构与算法的相关知识,保证看完让你有所成长。
欢迎关注我,学习资料免费分享给你哦!还有其他超多学习资源,都是我自己学习过的,经过过滤之后的资源,免去你还在因为拥有大量资源不知如何入手的纠结,让你体系化学习。
在这里插入图片描述

散列

散列就是利用一段存储空间存储数据(大小为Tablesize),然后根据关键词可以生成一个数字,这个数字是在范围在0和Tablesize之间。这个将关键字映射到数字的办法叫做散列函数。其实散列就是一个线性表,只不过它的下标可以不是直接给出的,而是通过一个运算从关键字得出的。理论上是希望每一个关键字都独自占有一个下标的数字,但是不论是什么散列函数,都是有可能将不同的关键字映射为一个数字的,所以还必须解决关键字映射为数字的冲突问题。

在这里插入图片描述

散列函数

如果关键字是整数,一般散列函数采取的办法是直接取余返回“key mod Tablesize".

如果关键字是字符串,一种简单的方法是将逐个按照字符的ASCII相加,将得到的结果最后求余。

int Hash(int Tablesize,char *key)
{
    unsigned int Hashval=0;
    while(*key!='\0')
    {
        Hashval+=*key;
        key++;
    }
    return Hashval%Tablesize;
}

还有一种更好的散列函数

int Hash(int Tablesize,char *key)
{
    unsigned int Hashval=0;
    while(*key!='\0')
    {
        Hashval=*key+( Hashval<<5);
        key++;
    }
    return Hashval%Tablesize;
}

这些散列函数当然也存在着冲突的问题,下面学习两种解决冲突的方法。

分离链接法

这是一种采用链表的方法来解决冲突的问题的,因为是采用链表,所以当出现冲突时,在相应的位置申请一个节点,然后与链表的插入操作相同,可以插入到链表的首部或者尾部。
在这里插入图片描述

如图所示,就是一个简单的分离链接法的示意图,每个散列表的元素都自己组成了一链表,如下标为3的元素,构成了53与103的链表。假设此时插入一个元素101,经过散列函数的运算,得到的下标为1,此时在1处已经有一个元素了,但因为采用了链表的方式处理冲突,只需要申请一个结点的内存,插入到链表的表头后面就可以了,完美解决冲突。当需要访问101时,首先经过散列函数运算得到下标为1,然后沿着链表遍历,如果找到101这个值,将这个节点返回即可,找不到就说明不存在。

在这里插入图片描述

//节点元素定义
typedef struct Node
{
  unsigned int num;
  struct Node * next;
};
typedef struct Node * list;
typedef struct HashNode
{
  int hashsize;
  list* thelist;  //指向指针的指针,指向散列数组的首地址
};
typedef struct HashNode * Hashtable;
//散列创建函数
Hashtable creatHashtable(int hashsize)
{
  Hashtable hash;
  int i;
  hash=(Hashtable)malloc(sizeof(struct HashNode));//申请散列表管理结构,都是指针
  if(hash==NULL)
  {
    printf("内存不足");
    return NULL;
  }
  hash->hashsize=hashsize;
  hash->thelist=(list*)malloc(sizeof(list)*hashsize);//申请散列表的数组(图中的表头部分)
  if(hash->thelist==NULL)
  {
     printf("内存不足");
     return NULL;
  }
  for(i=0;i<hashsize;i++)
  {
    hash->thelist[i]=(list)malloc(sizeof(struct Node));//(为每一个散列表头申请一个结点方便管理)
    hash->thelist[i]->next=NULL;
  }
  return hash;
}

在这里插入图片描述

图中就是构建的一个大小为4的散列,每个指针与后面的一个结点构成了前面图中所示的表头,这两个一起作为散列表的管理结构,申请这个结点是为了后面管理链表的方便,相当于链表中的头结点。

//散列函数
unsigned int Hash(Hashtable hash,unsigned int num)//采用最简单的求余比较
{
	return num%hash->hashsize;
}
list find(Hashtable hash,unsigned int num)//查找函数,返回值如果不为NULL,就是查找到的节点;为NULL就是表示没有查找到
{
   unsigned int data;
   list p;
   data=Hash(hash,num);
   p=hash->thelist[data]->next;//将p指向data所在的链表的首个节点。比如上面图中的101或者53
   while((p!=NULL)&&(p->num!=num))//比较关键字是否相等
   {
       p=p->next;
   }
   return p;
}

对于散列的插入操作的基本思路:根据关键字输入到散列函数,得到在散列表中的下标,然后查找是否已经存在于散列表中,如果存在,什么也不做;如果不存在,申请新的节点,插入到表头的链表之中。前面已经举例子不在赘述。看代码

//0表示之前已经存在,不需要插入;1表示插入成功;-1表示内存不足,插入失败
int insert(Hashtable hash,unsigned int num)
{
   unsigned int data;
   list p;
   p= find(hash,num);
   if(p==NULL)
   {
       data=Hash(hash,num);
	   p=(list)malloc(sizeof(struct Node));
	   if(p==NULL)
	   {
		   printf("内存不足");
		   return -1;
	   }
	   p->next=hash->thelist[data]->next;
	   hash->thelist[data]->next=p;
	   p->num=num;
	   return 1;
	   
   }
   else
   {
	   return 0;
   }
}
//0表示删除的元素不在散列表中;1表示删除成功
int delete(Hashtable hash,unsigned int num)
{
   unsigned int data;
   list p,k;
    
   data=Hash(hash,num);
   p=hash->thelist[data];//将p指向data所在的链表的表头。
   while((p->next!=NULL)&&(p->next->num!=num))//比较关键字是否相等
   {
       p=p->next;
   }
   if(p->next==NULL)
   {
       return 0;
   }
   else
   {
       k=p->next;
       p->next=k->next;
       free(k);
       return 1;
   }
}
开放定址法

开放定址法采用的是发生冲突,就去尝试寻找另外的空闲单元来插入。hash(num)=(hash(num)+F(i))mod Tablesize,且F(0)=0.函数F就是解决冲突的函数,定义了如何寻找空的单元,因为需要插入所有的单元,所以开放定址散列法需要的表比分离链接散列表大。

F函数由三种实现的方法:

线性探测法

线性探测法就是函数F是线性的,F(i)=i。这相当于逐个探测每个单元是否为空。但是线性探测的问题是容易导致在某一个范围内的数据聚集。

平方探测法

平方探测法用来消除线性探测中的一次聚集问题的方法。F(i)=i*i.

双散列

双散列法就是F也是一个散列函数。F(i)=i*hash2(x)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值