数据结构——哈希表知识总结

处理冲突方法

我们学习的处理方法包括线性探测再散列二次探测再散列链地址法(头插、尾插)

线性探测再散列

基本思想:当关键码key的哈希地址H0 = hash(key)出现冲突时,以H0为基础,往后一个地址,直到找出一个不冲突的哈希地址Hi (可以循环遍历哈希表),将相应元素存入其中。
题目描述:
定义哈希函数为H(key) = key%11,输入表长(大于、等于11)。输入关键字集合,用线性探测再散列构建哈希表,并查找给定关键字。
输入
测试次数t
每组测试数据为:
哈希表长m、关键字个数n
n个关键字
查找次数k
k个待查关键字
输出
对每组测试数据,输出以下信息:
构造的哈希表信息,数组中没有关键字的位置输出NULL
对k个待查关键字,分别输出:0或1(0—不成功,1—成功)、比较次数、查找成功的位置(从1开始)
样例输入:
1
12 10
22 19 21 8 9 30 33 4 15 14
4
22
56
30
17
样例输出
22 30 33 14 4 15 NULL NULL 19 8 21 9
1 1 1
0 6
1 6 2
0 1

#include<iostream>
using namespace std;
#define INF 0x3f3f3f
///搜索函数
void Find(int* list, int len) 
{
    int val; //查找元素
    cin >> val;
    int count = 0, i=0; //计数器
    while (true)
    {
        count++;
        ///向后逐一循环遍历哈希表
        if (list[(val % 11 + i) % len] == val)  
        {
            cout << "1" << " " << count << " " << (val % 11 + i) % len + 1 << endl;
            break;
        }
        ///若遍历到空位,则说明该元素不在哈希表中
        else if (i == len || list[(val % 11 + i) % len] == INF) 
        {
            cout << "0" << " " << count << endl;
            break;
        }
        i++;
    }
}
int main()
{
    int len, n; ///表长、元素个数
    cin >> len >> n;
    int* list = new int[len];
    memset(list, INF, len * sizeof(int)); ///哈希表初始化
    for (int i = 0; i < n; i++)
    {
        int num;
        cin >> num;
        if (list[num % 11] == INF)  //若插入元素与表中元素不冲突,直接插入表中
            list[num % 11] = num;
        else  //冲突则向后逐一搜索(循环哈希表),直到找到空位
        {
            int j = 0;
            while (list[(num % 11 + j) % len] != INF){ j++; }
            list[(num % 11 + j) % len] = num;
        }
    }
    ///搜索元素
    Find(list, len); 
    delete[]list;
    return 0;
}

二次探测再散列

**基本思想:**若当前key与原来key产生相同的哈希地址,则当前key存在该地址后偏移量为(1,2,3…)的二次方地址处:
key1:hash(key)+0
key2:hash(key)+1^2
key2:hash(key)-1^2
key3:hash(key)+2^2
key3:hash(key)-2^2

题目描述
定义哈希函数为H(key) = key%11。输入表长(大于、等于11),输入关键字集合,用二次探测再散列构建哈希表,并查找给定关键字。
输入
测试次数t
每组测试数据格式如下:
哈希表长m、关键字个数n
n个关键字
查找次数k
k个待查关键字
输出
对每组测试数据,输出以下信息:
构造的哈希表信息,数组中没有关键字的位置输出NULL
对k个待查关键字,分别输出:
0或1(0—不成功,1—成功)、比较次数、查找成功的位置(从1开始)
样例输入
1
12 10
22 19 21 8 9 30 33 4 41 13
4
22
15
30
41
样例输出
22 9 13 NULL 4 41 NULL 30 19 8 21 33
1 1 1
0 3
1 3 8
1 6 6

#include<iostream>
using namespace std;
#define INF -9999
void Find(int* list, int len)
{
   
    int i = 0;
    int oper1 = 0,oper2 = 0;
    int val; ///查找元素
    cin >> val;
    int count = 1; //计算查找次数
    if (list[(val % 11) % len] == val)
    {
        cout << "1" << " " << count << " " << (val % 11) % len + 1 << endl;
        return;
    }
    while (true)
    {
        ///计算正偏移量和负偏移量
        oper1 = i * i;
        oper2 = -i * i;
        注意:要确保0<=oper<len
        while (oper1 > len)
            oper1 -= len;
        while (oper2 < 0)
            oper2 += len;
        count++;
        if (list[(val % 11 + oper1) % len] == val)
        {
            cout << "1" << " " << count << " " << (val % 11 + oper1) % len + 1 << endl;
            break;
        }
        count++;
        if (list[(val % 11 + oper2) % len] == val)
        {
            cout << "1" << " " << count << " " << (val % 11 + oper2) % len + 1 << endl;
            break;
        }
        如果偏移+i^2和-i^2都为空位,则说明元素不在哈希表中
        if (i == len || list[(val % 11 + oper1) % len] == INF || list[(val % 11 + oper2) % len] == INF)
        {
            cout << "0" << " " << count << endl;
            break;
        }
        i++;
    }
    
}
int main()
{
    
    int len, n;  ///哈希表长度,表中元素个数
    cin >> len >> n;
    int* list = new int[len];
    ///初始化哈希表
    for (int i = 0; i < len; i++)
        list[i] = INF;
    for (int i = 0; i < n; i++) //插入元素
    {
        int num;
        cin >> num;
        ///若插入元素与表中元素不冲突,直接插入表中
        if (list[num % 11] == INF)  list[num % 11] = num;
        ///否则计算偏移量 +i^2,-i^2
        else
        {
            int i = 0;
            int oper1 = 0;
            int oper2 = 0;
            while (true)
            {
                ///计算正度偏移量
                oper1 = i * i;
                oper2 = -i * i;
                注意:要确保0<=oper<len
                while (oper1 > len)
                    oper1 -= len;
                while (oper2 < 0)
                    oper2 += len;
                if (list[(num % 11 + oper1) % len] == INF)
                {
                    list[(num % 11 + oper1) % len] = num;
                    break;
                }
                else if (list[(num % 11 + oper2) % len] == INF)
                {
                    list[(num % 11 + oper2) % len] = num;
                    break;
                }
                i++;
            }
        }
    }
    ///查找哈希表中元素
    Find(list, len);
    delete[]list;
    return 0;
}

链地址法

基本思想:在hash冲突产生时,将相同具有相同hash值的对象以链表的形式存储,更直白的表述就是数组中的每个元素不在是具体的每个要存储的对象了,每个元素代表具有相同hash值对象组成的链表,通过对象内部的指针可以查询到下一个具有相同hash值的对象。 简单总结:将产生冲突的值以链表的形式连起来。

题目描述
给出一个数据序列,建立哈希表,采用求余法作为哈希函数,模数为11,哈希冲突用链地址法和表头插入,如果首次查找失败,就把数据插入到相应的位置中;实现哈希查找功能。
输入
第一行输入n,表示有n个数据
第二行输入n个数据,都是自然数且互不相同,数据之间用空格隔开
第三行输入t,表示要查找t个数据
从第四行起,每行输入一个要查找的数据,都是正整数
输出
每行输出对应数据的查找结果
样例输入
6
11 23 39 48 75 62
6
39
52
52
63
63
52
样例输出
6 1
error
8 1
error
8 1
8 2
头插法构建链表

#include<iostream>
using namespace std;
#define INF 0x3f3f3f
class CNode
{
public:
    int data;
    CNode* next;
    CNode() :data(INF), next(NULL) {}
};
///查找哈希表中是否存在目标元素
int findhash(CNode* ptr[15], int val)
{
    int tag = 0; //查找状态标志
    int count = 0; ///计算查找次数
    CNode* p = ptr[val % 11];
    while (p)
    {
        count++;
        if (p->data == val)
        {
            tag = 1;
            break;
        }
        p = p->next;
    }
    if (tag == 1)
        cout << val % 11 << " " << count << endl;
    else
        cout << "error" << endl;
    return tag;
}

int main()
{
    int n;
    cin >> n;
    CNode* ptr[15] = { NULL };
    ///初始化链表头插法
    for (int i = 0; i < n; i++)
    {
        int num;
        cin >> num;
        CNode* p = new CNode();
        p->data = num;
        p->next = ptr[num % 11];
        ptr[num % 11] = p;
    }
    int T;
    cin >> T;
    while (T--)
    {
        int val;
        cin >> val;
        if (findhash(ptr, val) == 0) //若未查找到,则头插法插入新节点
        {
            CNode* p = new CNode();
            p->data = val;
            p->next = ptr[val % 11];
            ptr[val % 11] = p;
        }
    }
    return 0;
}

尾插法构建链表

#include<iostream>
using namespace std;
#define INF 0x3f3f3f
class CNode
{
public:
    int data;
    CNode* next;
    CNode() :data(INF), next(NULL) {}
};
///查找哈希表中是否存在目标元素
int findhash(CNode* hptr[15], int val)
{
    int tag = 0; //查找状态标志
    int count = 0; ///计算查找次数
    CNode* p = hptr[val % 11];
    while (p)
    {
        count++;
        if (p->data == val)
        {
            tag = 1;
            break;
        }
        p = p->next;
    }
    if (tag == 1)
        cout << val % 11 << " " << count << endl;
    else
        cout << "error" << endl;
    return tag;
}

int main()
{
    int n;
    cin >> n;
    CNode* hptr[15] = { NULL }; //头指针数组
    CNode* tptr[15] = { NULL }; ///尾指针数组
    ///初始化链表尾插法
    for (int i = 0; i < n; i++)
    {
        int num;
        cin >> num;
        CNode* p = new CNode();
        p->data = num;
        ///若为空链表,则需特殊插入
        if (hptr[num % 11] == NULL)
        {
            hptr[num % 11] = p;
            tptr[num % 11] = p;
            continue;
        }
        tptr[num % 11]->next = p;
        tptr[num % 11] = p;

    }
    int T;
    cin >> T;
    while (T--)
    {
        int val;
        cin >> val;
        if (findhash(hptr, val) == 0) //若未查找到,则尾插发插入新节点
        {
            CNode* p = new CNode();
            p->data = val;
            if (hptr[val % 11] == NULL)
            {
                hptr[val % 11] = p;
                tptr[val % 11] = p;
                continue;
            }
            tptr[val % 11]->next= p;
            tptr[val % 11] = p;
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值