数据结构 c —查找

  用平均查找长度衡量算法性能。 A S L = ∑ i = 1 n P i C i A S L=\sum_{i=1}^{n} P_{i} C_{i} ASL=i=1nPiCi    其中, P i P_i Pi为查找第i个记录的概率, C i C_i Ci为找到表中其关键字与给定值相等的第i个记录时,和给定值已进行过比较的关键字个数。为了提高效率,可以在每个记录中附设一个访问频度阈。
  提高检索效率的方法:
    1.预排序;
    2.建立索引;(检索时充分利用辅助索引信息,牺牲一点空间从而提高效率)
    3.散列技术(把数据组织到一个表里,根据关键码确定表里记录位置。缺点:不适合范围查找,不允许重复关键字。)
    4.当散列技术不适合基于磁盘的应用程序时,我们可以采用B数的方法。

顺序表的查找
   在0位置设置哨兵,从表的最后一个记录开始,逐个进行记录的关键字和给定值进行比较。

有序表的查找

 1.折半查找 A S L k = log ⁡ 2 ( n + 1 ) − 1 A S L_{k}=\log _{2}(n+1)-1 ASLk=log2(n+1)1 以有序表表示静态查找表时,进行查找的方法除了折半查找之外,还有斐波那契查找和插值查找。
优点:平均与最大检索长度相近,检索速度快。
缺点:要排序,顺序储存,不易更新

 2.分块检索(块与块之间有序,快内无序,块与块之间二分法检索,块内之间顺序检索)
 优点:插入,删除相对容易,没有大量的记录移动。
 缺点:增加一个辅助数组的储存空间,初始线性表分块排序,当大量插入删除时,或节点分布不均匀时,速度下降。
 3.集合检索
 集合:有若干个确定的,相异的对象构成的整体。
 集合的检索:确定一个值是不是某个集合的元素。
 用位向量的方式来表示集合(leecode -242)
在这里插入图片描述在这里插入图片描述散列表 α = n / m \alpha=n / m α=n/m 散列表中的空间大小为m,填入表中的节点数为n。
 冲突:某个散列函数对于不同的关键码,计算出来相同的散列地址,在实际应用中,不产生冲突的散列极少存在。
 同义词:发生冲突的两个关键码
 散列函数:把关键码值映射到存储位置的函数,
 散列函数选取原则:
  1.运算尽可能的简单
  2.函数的阈值在表长范围内
  3.尽可能使关键码不同,其散列函数值不相同。
 哈希表构建方法:
  1.直接定址法: H ( k e y ) = k e y 或 H ( k e y ) = a ⋅ k e y + b H(k e y)=k ey 或H(k e y)=a \cdot k e y+b H(key)=keyH(key)=akey+b  取关键字或者某个线性函数值为哈希地址。
  2.除余法:通常选取一个质数作M值,增大均匀分布的可能。(潜在缺点:连续关键码,映射出连续的散列值,虽然能保证连续的关键码不发生冲突,但也意味着占据连续的数组单元,可能导致散列性能降低。)
  3.数字分析法:假如关键字是以r为基数(如:以10为基的十进制),并且哈希表中可能出现的关键字都是事先知道的,则可去关键字的若干位组成哈希地址。例如有80个会议记录,其关键字为8位十进制,假如哈希表的表长为 10 0 1 00 100_100 100100,则可取两位十进制数组成哈希地址,取哪两位?原则是使得到的哈希地址尽量避免产生冲突,则需要从分析这80个关键字着手,假设这80个关键字的一部分如下:
在这里插入图片描述对关键字全体的分析中可以发现,第1、2位都是“8 1”,第3位置可能取1、2、3或者4,第8位只可能取2、5或7,因此这4位都不可取,由于中间的4位可看成近乎随机的,因此可取其中任意两位,或取其中两位与另一位叠加求和后舍去进位作为哈希地址。

  4.平方取中法:取关键字平方后的中间几位数为哈希地址,这是一种较常用的哈希函数的方法,通常在选定哈希函数时不一定能知道关键字的全部情况,取其中那几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使得随机分布的关键字的带的哈希地址也是随机的。取得位数由表长决定。
  5.折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和,作为哈希地址。这个方法称为折叠地址。
  6.随机数法。

冲突处理办法
 1. 开放地址法 H i = ( H ( k e y ) + d i )  MOD  m i = 1 , 2 , ⋯   , k ( k ⩽ m − 1 ) H_{i}=\left(H(k e y)+d_{i}\right) \text { MOD } m \quad i=1,2, \cdots, k(k \leqslant m-1) Hi=(H(key)+di) MOD mi=1,2,,k(km1) H ( k e y ) H(k e y) H(key)为哈希函数,m为哈希表长, d i d_i di为增量序列。 d i = 1 , 2 , 3 , ⋯   , m − 1 d_{i}=1,2,3, \cdots, m-1 di=1,2,3,,m1(最常见的取法,也可以有其他取法)
 2. 再哈希法 H i = R H i ( k e y ) i = 1 , 2 , ⋯   , k H_{i}=R H_{i}(k e y) \quad i=1,2, \cdots, k Hi=RHi(key)i=1,2,,k   R H i RH_i RHi均是不同的哈希函数。
 3. 链地址法
  将所有关键字为同义词的记录储存在同一线性链表中,假设某哈希函数产生的哈希地址在区间[0.m-1]上,则设立一盒指针型向量 C h a i n C h a i n H a s h [ m ] Chain ChainHash[m] ChainChainHash[m]每个向量的初始状态都是空指针,凡哈希地址为i的记录都插入到头指针为 C h a i n C h a i n H a s h [ i ] Chain ChainHash[i] ChainChainHash[i]的链表中,在链表中的插入位置可以是在表头或者表尾;也可以是在中间,以保持同义词在同一线性表中按关键字有序。
在这里插入图片描述 4. 建立一个公共溢出区

哈希表创建(未验证源码)

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>

#define MaxSize 100      //定义最大哈希表长度
#define NULLKEY -1       //定义空关键字值
#define DELKEY -2        //定义被删关键字值
typedef int KeyType;     //关键字类型
typedef char *InfoType;  //其他数据类型
typedef struct{
    KeyType key;        //关键字域
    InfoType data;      //其他数据域
    int count;          //探查次数域
}HashTable[MaxSize];    //哈希表类型

//将关键字k插入到哈希表中
int InsertHT(HashTable ha,int &n,KeyType k,int p){
    int i,adr;
    adr = k%p;
    if (ha[adr].key==NULLKEY || ha[adr].key==DELKEY)  //x[j]可以直接放在哈希表中
    {
        ha[adr].key=k;
        ha[adr].count=1;
    } 
    else      //发生冲突时,采用线性探查法解决冲突
    {
        i=1;  //i记录x[j]发生冲突的次数
        do 
        {
            adr = (adr+1)%p;
            i++;
        } while (ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY);
        ha[adr].key=k;
        ha[adr].count=i;
    }
    n++;
    return 0; 
}

//创建哈希表
void CreateHT(HashTable ha,KeyType x[],int n,int m,int p){
    int i,n1=0;
    for (i=0;i<m;i++)  //哈希表置初值
    {
        ha[i].key=NULLKEY;
        ha[i].count=0;
    }
    for(i=0;i<n;i++)
        InsertHT(ha,n1,x[i],p);
}

//在哈希表中查找关键字k
int SearchHT(HashTable ha,int p,KeyType k){
    int i=0,adr;
    adr = k%p;
    while (ha[adr].key!=NULLKEY && ha[adr].key!=k)
    {
        i++;             //采用线性探查法找下一个地址
        adr = (adr+1)%p;
    }
    if(ha[adr].key==k)  //查找成功
        return adr;
    else                //查找失败
        return -1;

}

//删除哈希表中关键字k
int DeleteHT(HashTable ha,int p,int k,int &n){
    int adr;
    adr = SearchHT(ha,p,k);
    if (adr!=-1)   //在哈希表中找到关键字
    {
        ha[adr].key=DELKEY;
        n--;       //哈希表长度减1
        return 1;
    }
    else            //在哈希表中未找到该关键字
        return 0;

}
//查找成功时,平均查找长度
void CompASL(HashTable ha,int m){
    int i;
    int s=0,n=0;
    for (i=0;i<m;i++)
        if(ha[i].key!=DELKEY && ha[i].key!=NULLKEY)
        {
            s=s+ha[i].count;
            n++;
        }
    printf(" 查找成功的ASL=%.3g\n",s*1.0/n);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值