散列查找的查找插入及冲突处理方法

处理冲突的方法

  1.换个位置:开放地址法

  2.同一位置的冲突对象组织在一起:链地址法

开放地址法(Open Addressing):

一旦产生了冲突(该地址已有其他元素),就按某种规则去寻找另一空地址

若发生了第i次冲突,试探的下一个地址将增加di, 基本公式:

hi(key) = (h(key)+di) mod TableSize (1≤i<TableSize)

di决定了不同解决冲突方案:线性探测、平方探测、双散列

  线性探测:di = i  +1 +2 +3

  平方探测:di = ±i^2  +1^2, -1^2, +2^2, -2^2

  双散列:di = i*h2(key)

线性探测(Linear Probing):

  以增量序列1, 2, ..., (TableSize-1)循环试探下一个存储地址

  [例] 设关键词序列为 {47,7,29,11,9,84,54,20,30},

    散列表表长TableSize =13 (装填因子 α = 9/13 ≈ 0.69);

      散列函数为:h(key) = key mod 11。

    用线性探测法处理冲突,列出依次插入后的散列表,并估算查找性能

散列表查找性能分析

  成功平均查找长度(ASLs)

  不成功平均查找长度(ASLu)

ASLs:平均查找比较次数(冲突次数加1)

   ASL s = (1+7+1+1+2+1+4+2+4)/9 = 23/9 ≈2.56

ASLu:不在散列表中的关键词的平均查找次数(不成功)

   一般方法:将不在散列表中的关键词分若干类。 如根据H(key)值分类

   ASL u = (3+2+1+2+1+1+1+9+8+7+6)/11 = 41/11 ≈ 3.73

平方探测法(Quadratic Probing) —二次探索

平方探测法:以增量序列1^2, -1^2, 2^2, -2^2, ..., q^2, -q^2

      且q<=[TableSize/2]循环试探下一个存储地址

定理:如果散列表长度TableSize是某个4k+3(k是正整数)形式的素数时,平方探测法

   就可以探查到整个散列表空间。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define MAXTABLESIZE 100000 //最大散列表长度
typedef int ElementType;   //关键词类型
typedef int Index;  //散列地址类型
typedef Index Position; //数据所在位置
//散列单元状态类型,分别对应:有合法元素、空单元、有已删除元素
typedef enum{Legitimate, Empty, Deleted} EntryType;

typedef struct HashEntry Cell;  //散列表单元类型
struct HashEntry
{
    ElementType Data;
    EntryType info;
};

typedef struct TblNode *HashTable; //散列表类型
struct TblNode
{
    int TableSize;
    Cell *Cells;
};

int NextPrime(int N) //返回大于N且不超过MAXTABLESIZE的最小素数
{
    int i, p = (N % 2) ? N+2 : N+1; //从大于N的下一个奇数开始

    while (p <= MAXTABLESIZE) {
        for (i = (int)sqrt(p); i > 2; i--)  //p已经是奇数
            if (!(p % i)) break;   //p不是素数
        if (i == 2) break;  //for正常结束,说明p是素数
        else p += 2;
    }
    return p;
}

HashTable CreatTable(int TableSize)
{
    HashTable H;
    int i;
    H = (HashTable)malloc(sizeof(struct TblNode));
    H->TableSize = NextPrime(TableSize);  //保证散列表最大长度是素数
    H->Cells = (Cell *)malloc(H->TableSize * sizeof(Cell));
    //初始化单元状态为空单元
    for (i = 0; i < H->TableSize; i++)
        H->Cells[i].Info = Empty;
    return H;
}

Position Find(HashTable H, ElementType Key)
{
    Position CurrentPos, NewPos;
    int CNum = 0; //记录冲突次数

    NewPos = CurrentPos = Hash(Key, H->TableSize); //初始散列位置
    while (H->Cells[NewPos].Info != Empty && H->Cells[NewPos].Data != Key)
    {
        //字符串类型的关键词需要strcmp函数
        //统计一次冲突,判断奇偶性
        if (++CNum%2) //奇数次冲突
        {
            NewPos = CurrentPos + (CNum+1)*(CNum+1)/4; //增量为+[(CNum+1)/2]^2
            if (NewPos >= H->TableSize)
                NewPos %= H->TableSize;
        }
        else {  //偶数次冲突
            NewPos = CurrentPos - CNum*CNum/4;   //增量为-(CNum/2)^2
            while (NewPos < 0)
                NewPos += H->TableSize;
        }
    }
    return NewPos;   //此时NewPos是Key的位置或者是一个空位置(没找到)
}

bool Insert(HashTable H, ElementType)
{
    Position Pos = Find(H, Key);
    if (H->Cells[Pos].Info != Legitimate) {
        H->Cells[Pos].Info = Legitimate;
        H->Cells[Pos].Data = Key;
        //字符串类型关键词需要strcpy函数
        return true;
    }
    else {
        printf("键值已存在");
        return false;
    }
}

3.双散列探测法(Double Hashing)

4.再散列(Rehashing)

分离链接法(Separate Chaining)

分离链接法:将相应位置上冲突的所有关键词存储在同一个链表中

#include <stdlib.h>
#include <string>
#define KEYLENTH 15 //关键词字符串的最大长度
typedef char ElementType[KEYLENTH+1]; //关键词类型用字符串
typedef int Index;   //散列地址类型
//单链表定义
typedef struct LNode *PtrToLNode;
struct LNode
{
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
//以上是单链表定义

typedef struct TblNode *HashTable;  //散列表类型
struct TblNode       //散列表结点定义
{
    int TableSize;
    List Heads;    //指向链表头结点的数组
};

HashTable CreatTable (int TableSize)
{
    HashTable H;
    int i;

    H = (HashTable)malloc(sizeof(struct TblNode));
    H->TableSize = NextPrime(TableSize);
    H->Heads = (List)malloc(H->TableSize*sizeof(struct LNode));
    //初始化表头结点
    for (i = 0; i < H->TableSize; i++) {
        H->Heads[i].Data[0] = ‘\0‘;
        H->Heads[i].Next = NULL;
    }
    return H;
}

Position Find(HashTable H, ElementType Key)
{
    Position P;
    Index Pos;

    Pos = Hash(Key, H->TableSize);
    P = H->Heads[Pos].Next;  //从该链表的第一个结点
    //当未到末尾,并且Key未找到时
    while (P && strcmp(P->Data, Key))
        P = P->Next;

    return P;
}

bool Insert(HashTable H, ElementType Key)
{
    Position P, NewCell;
    Index Pos;

    P = Find(H, Key);
    if (!P) {
        NewCell = (Position)malloc(sizeof(struct LNode));
        strcmp(NewCell->Data, Key);
        Pos = Hash(Key, H->TableSize);
        //将NewCell插入为H->Heads[Pos]链表的第一个结点
        NewCell->Next = H->Heads[Pos].Next;
        H->Heads[Pos].Next = NewCell;
        return true;
    }
    else {
        printf("键值已存在");
        return false;
    }
}

void DestroyTable(HashTable H)
{
    int i;
    Position P, Tmp;
    //释放每个链表的结点
    for (i = 0; i < H->TableSize; i++) {
        P = H->Heads[i].Next;
        while (P) {
            Tmp = P->Next;
            free(P);
            P = Tmp;
        }
    }
    free(H->Heads);
    free(H);
}

时间: 2024-10-12 07:35:09

散列查找的查找插入及冲突处理方法的相关文章

数据结构--散列(分离链接法解决冲突)

散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存入到此存储单元中.检索时,用同样的方法计算地址,然后到相应的 单元里去取要找的结点.通过散列方法可以对结点进行快速检索.散列(hash,也称"哈希")是一种重要的存储方式,也是一种常见的检索方法. 因此,散列函数更像是一种映射,散列函数的选择有很多种,下面以散列函数为关键值对10取余为例,说明散列的插入关键

查找之散列查找(哈希表)

本学习笔记部分内容来自网易云课堂浙江大学数据结构课程,谢谢! 1.散列表(哈希表) 已知的几种查找方法: 顺序查找  O(N) 二分查找(静态查找)  O(logN) 二叉搜索树      O(h)  h为二叉树高度   (动态查找:有插入有删除有查找) 平衡二叉树      O(logN) 查找的本质:已知对象找位置 1.有序安排对象:全序或半序: 2.直接算出对象位置:散列. 散列查找法的两项基本工作: 1.计算位置:构造散列函数确定关键词存储位置: 2.解决冲突:应用某种策略解决多个关键词

数据结构-散列查找

判断题 1.将M个元素存入用长度为S的数组表示的散列表,则该表的装填因子为M/S. T      F 2.在散列中,函数"插入"和"查找"具有同样的时间复杂度. T      F 3.在散列表中,所谓同义词就是被不同散列函数映射到同一地址的两个元素. T      F 4.采用平方探测冲突解决策略(hi(k)=(H(k)+i2)%11, 注意:不是±i2),将一批散列值均等于2的对象连续插入一个大小为11的散列表中,那么第4个对象一定位于下标为0的位置. T    

散列函数之单散列算法解决冲突问题

1. 问题 问题同<简单散列函数算法> 设有10个非负整数,用不多于20个的储存单元来存放,如何存放这10个数,使得搜索其中的某一个数时,在储存单元中查找的次数最少? 问题类似于,有10个带号码的球,放到编号为{0, 1, 2, -, 19}共20个盒子中,每个盒子最多放一个,问如何放,使能够用最少的次数打开盒子,知道任一个球所在的盒子编号? 2. 分析 <简单散列函数算法>中,已经分析得出,只要能解决冲突问题,就能将查找时间降为常量范围内. 思路:当一个数发生冲突时,再找一个没有

Java散列和散列码的实现

转自:https://blog.csdn.net/al_assad/article/details/52989525 散列和散列码 ※正确的equals方法应该满足的的条件: ①自反性:x.equals(x) 一定返回true: ②对称性:y.euqlas(x)为true,那么x.equals(y)一定为true: ③传递性:x.equals(y)为true,y.euqlas(z)为true,则z.equals(x)为true: ④一致性:如果x,y中用于等价比较的信息没有变化,那么无论调用y.

散列&#183;跳房子散列

跳房子散列 1.定义 前言: ? 线性探测法是在散列位置的相邻点开始探测,这会引起很多问题,于是各种优化版本例如平方探测.双散列等被提出来改进其中的聚集问题.但是探测相邻位置和第二次散列相比,显然探测相邻位置更有优势,所以线性探测仍然是实用的,甚至是最佳选择. 1.1 描述 ? 跳房子散列的思路:用事先确定的,对计算机底层体系结构而言最优的一个常数,给探测序列的最大长度加个上界.这样做可以给出常数级的最坏查询时间,并且与布谷鸟散列一样,查询可以并行化,以同时检查可用位置 的有限集. 布谷鸟散列:

Redis数据类型之散列(hash)

1. 什么是散列 散列类似于一个字典,是一个<K, V>对的集合,不过这个key和value都只能是字符串类型的,不能嵌套,可以看做Java中的Map<String, String>. 2. 基本操作 赋值 散列操作不区分插入和更新,当设置一个field的时候如果不存在的话表示新增,如果已经存在的话则表示更新,之前的值会被覆盖掉.当设置值的时候如果field在之前不存在的话则返回1,视为新增,如果field已经存在的话,返回0,视为更新. hset <key> <

非对称算法,散列(Hash)以及证书的那些事

转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/35389657 作者:小马 这几个概念在金融电子支付领域用得比较多,我忽然觉得把它们串起来一起讲,层层引入,可能更好理解一些.希望能以最简单朴实的方式讲明白他们之间的关系. 一非对称算法 关于非对称算法,你只要知道下面这些就行了,密钥是一对,一个叫公钥,一个叫私钥,前者公开,后者保密.假设你有一对公私钥,给你一串数据,你可以用私钥加密,然后把密文和公钥都放出去,别人可以用这个公钥解

散列查找

编译处理时,涉及变量及属性的管理 :插入(新变量的定义),查找(变量的引用). 顺序查找  O(N)    二分查找 O(logN)    二叉树查找O(H)     平衡二叉树 O(logN) 如何快速查找? 查找的本质:已知对象找位置 有序的安排对象-> 全序:顺序查找  半序:二叉树 直接算出位置-> 散列查找 散列查找:1.计算位置.2.解决冲突. 1计算位置 构造散列函数.  要求:计算简单:地址分布均匀. 数字关键词:1 直接定值.2 除留余数 h(key)= key mod p,