educoder数据结构 查找 第2关:实现散列查找

本文讲解如何通过ILH_InsKey和ILH_DelKey函数实现散列表的插入与删除操作,利用独立链表地址法解决散列冲突,以7个独立链表存储8个关键码实例演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

任务描述

相关知识

编程要求

测试说明

代码实现


任务描述

本关要求通过补全函数ILH_InsKeyILH_DelKey来分别实现插入和删除操作。

相关知识

本关讨论散列存储,散列函数使用除留余数法,冲突解决方法采用独立链表地址法。假设有 8 个关键码: 7 , 15 , 23 , 31 , 12 , 14 , 10 , 17 ,采用散列函数hash(key)=key%7,其存储结构图如图 1 所示,它由 7 个独立链表组成,散列值相同的关键码在同一个链表里,独立链表的头结点组成散列表,一共 7 行,编号 0 , 1 , … , 6 。独立链表的每个结点是一个 struct HNode 结构,其定义如下:

 
  1. struct HNode {
  2. int key; //假设关键码为整数
  3. HNode* next;
  4. };

在散列表中,如果表项的key字段等于 0 (假设有效的关键码值不等于 0 ),则表示该行是一条空链表,例如图 1 中编号为 4 和编号为 6 的行。

散列表的开始地址保存在pn中,散列表的行数为n(图 1 中,n=7),将pnn组织成结构:

 
  1. struct LHTable {
  2. HNode* pn; //指向散列表,每个表结点是独立链表的表头结点
  3. int n; //散列表的长度,一般取(小于等于数据个数的最大)质数
  4. };

定义如下操作,各操作函数的功能详见下面给出的代码文件 indLnkHash.cpp 的代码框架:

 
  1. LHTable* ILH_Create(int n);
  2. void ILH_Free(LHTable* pt);
  3. bool ILH_InsKey(LHTable* pt, int x);
  4. bool ILH_FindKey(LHTable* pt, int x);
  5. bool ILH_DelKey(LHTable* pt, int x);
  6. void ILH_Print(LHTable *pt);

编程要求

本关的编程任务是补全 step2/indLnkHash.cpp 文件中的ILH_InsKeyILH_DelKey函数来分别实现插入和删除操作。

  • 具体请参见后续测试样例。

本关涉及的代码文件 indLnkHash.cpp 的代码框架如下:

 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include "indLnkHash.h"
  5. LHTable* ILH_Create(int n)
  6. //创建散列表, n为表长度,最佳取值:n取小于等于数据个数的最大质数
  7. {
  8. HNode* pn=(HNode*)malloc(sizeof(HNode)*n);
  9. for (int i=0; i<n; i++) {
  10. pn[i].key=0;
  11. pn[i].next=NULL;
  12. }
  13. LHTable* pt=(LHTable*)malloc(sizeof(LHTable));
  14. pt-> pn=pn;
  15. pt->n=n;
  16. return pt;
  17. }
  18. void ILH_Free(LHTable* pt)
  19. //释放散列表
  20. {
  21. if (pt==NULL) return;
  22. for (int i=0; i<pt->n; i++) {
  23. HNode* curr=pt->pn[i].next;
  24. while (curr) {
  25. HNode* next=curr->next;
  26. free(curr);
  27. curr=next;
  28. }
  29. }
  30. free(pt->pn);
  31. free(pt);
  32. }
  33. bool ILH_InsKey(LHTable* pt, int x)
  34. //插入关键码x
  35. //返回true,表示插入成功
  36. //返回false,表示插入失败(关键码已经存在)
  37. {
  38. // 请在此添加代码,补全函数ILH_InsKey
  39. /********** Begin *********/
  40. /********** End **********/
  41. }
  42. bool ILH_FindKey(LHTable* pt, int x)
  43. //查找关键码x
  44. //返回true表示找到
  45. //返回false表示没找到
  46. {
  47. int d=x%pt->n;
  48. if (pt->pn[d].key==0) {
  49. return false;
  50. }
  51. else if (pt->pn[d].key==x)
  52. return true;
  53. HNode* curr=pt->pn[d].next;
  54. while (curr && curr->key!=x) curr=curr->next;
  55. if (curr) return true;
  56. else return false;
  57. }
  58. bool ILH_DelKey(LHTable* pt, int x)
  59. //删除关键码
  60. //返回true表示该关键码存在,且成功删除
  61. //返回false表示该关键码不存在
  62. {
  63. // 请在此添加代码,补全函数ILH_DelKey
  64. /********** Begin *********/
  65. /********** End **********/
  66. }
  67. void ILH_Print(LHTable *pt)
  68. {
  69. for (int i=0; i<pt->n; i++) {
  70. printf("%5d: ", i);
  71. if (pt->pn[i].key) {
  72. printf("%d ", pt->pn[i].key);
  73. HNode* curr=pt->pn[i].next;
  74. while (curr) {
  75. printf("-> %d ", curr->key);
  76. curr=curr->next;
  77. }
  78. printf("\n");
  79. }
  80. else
  81. printf("-\n");
  82. }
  83. }

测试说明

本关的测试文件是 step2/Main.cpp ,测试过程如下:

  1. 平台编译 step2/Main.cpp ,然后链接相关程序库并生成 exe 可执行文件;

  2. 平台运行该 exe 可执行文件,并以标准输入方式提供测试输入;

  3. 平台获取该 exe 可执行文件的输出,然后将其与预期输出对比,如果一致则测试通过;否则测试失败。

输入输出格式说明

输入格式: 首先输入一个正整数n,创建一个长n的散列表。 然后输入多个操作:如果输入 “insert” ,则后面跟一个数x,表示将x插入;如果输入 “delete” ,则后面跟一个数x,表示将x删除;如果输入 “end” ,表示输入结束。

输出格式: 输出n个独立链表。

以下是平台对 step2/Main.cpp 的样例测试集:

样例输入: 11 insert 54 insert 77 insert 94 insert 89 insert 14 insert 45 insert 76 insert 23 insert 43 insert 47 end

样例输出: 0: 77 1: 89 -> 45 -> 23 2: - 3: 14 -> 47 4: - 5: - 6: 94 7: - 8: - 9: - 10: 54 -> 76 -> 43

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "indLnkHash.h"

LHTable* ILH_Create(int n)
//创建散列表, n为表长度,最佳取值:n取小于等于数据个数的最大质数
{
    HNode* pn=(HNode*)malloc(sizeof(HNode)*n);
    for (int i=0; i<n; i++) {
        pn[i].key=0;
        pn[i].next=NULL;
    }
    LHTable* pt=(LHTable*)malloc(sizeof(LHTable));
    pt-> pn=pn;
    pt->n=n;
    return pt;
}

void ILH_Free(LHTable* pt)
//释放散列表
{
    if (pt==NULL) return;
    for (int i=0; i<pt->n; i++) {
        HNode* curr=pt->pn[i].next;
        while (curr) {
            HNode* next=curr->next;
            free(curr);
            curr=next;
        }
    }
    free(pt->pn);
    free(pt);
}

bool ILH_InsKey(LHTable* pt, int x)
//插入关键码x
//返回true,表示插入成功
//返回false,表示插入失败(关键码已经存在)
{
    /*请在BEGIN和END之间实现你的代码*/
    /*****BEGIN*****/
    int d=x%pt->n;
    if(pt->pn[d].key==0)
    {
        pt->pn[d].key=x;
        return true;
    }
    else if(pt->pn[d].key==x)
    {
        return false;
    }
    HNode* prev=&(pt->pn[d]);
    HNode* curr=pt->pn[d].next;
    while (curr && curr->key!=x) 
    {
        prev=curr; 
        curr=curr->next;
    }
    if (curr) 
    {
        return  false;
    }
    HNode* pnode=(HNode*)malloc(sizeof(HNode));
    pnode->key=x;
    pnode->next=NULL;//pt->pn[d].next;
    prev->next=pnode;
    return true;

    /******END******/
    /*请不要修改[BEGIN,END]区域外的代码*/
}

bool ILH_FindKey(LHTable* pt, int x)
//查找关键码x
//返回true表示找到
//返回false表示没找到
{
    int d=x%pt->n;
    if (pt->pn[d].key==0) {
        return false;
    }
    else if (pt->pn[d].key==x) 
        return true;

}


bool ILH_DelKey(LHTable* pt, int x)
//删除关键码
//返回true表示该关键码存在,且成功删除
//返回false表示该关键码不存在
{
    /*请在BEGIN和END之间实现你的代码*/
    /*****BEGIN*****/
    int d=x%pt->n;
    if (pt->pn[d].key==0) {
        return false;
    }
    else if (pt->pn[d].key==x)
    {
        if(pt->pn[d].key==NULL)
        {
            pt->pn[d].key=0;
        }
        else
        {
            HNode* first=pt->pn[d].next;
            pt->pn[d].key=first->key;
            pt->pn[d].next=first->next;
            free(first);

        }
        return true;
    }
    HNode* prev=&(pt->pn[d]);
    HNode* curr=pt->pn[d].next;
    while (curr && curr->key!=x) 
    {
        prev=curr; 
        curr=curr->next;
    }
    if (curr==NULL) 
    {
        return  false;
    }
    prev->next=curr->next;
    free(curr);
    return true;


    /******END******/
    /*请不要修改[BEGIN,END]区域外的代码*/
}

void ILH_Print(LHTable *pt)
{
    for (int i=0; i<pt->n; i++) {
        printf("%5d:", i);
        if (pt->pn[i].key) {
            printf("%d", pt->pn[i].key);
            HNode* curr=pt->pn[i].next;
            while (curr) {
                printf("->%d", curr->key);
                curr=curr->next;
            }
            printf("\n");
        }
        else 
            printf("-\n");
    }
}

### 实现哈希查找算法 #### 哈希查找的基本概念 哈希查找是一种高效的查找方法,利用散列表(Hash Table)实现。通过特定的散列函数将键字映射到表中的位置,从而加速查找过程。这种方式使得查找操作的时间复杂度接近于常数 O(1)[^1]。 #### C++ 中的哈希查找实现 下面展示了一个简单的C++程序用于说明如何创建并查询一个基于整型键值对的哈希表: ```cpp #include <iostream> #include <unordered_map> using namespace std; int findElement(const unordered_map<int, int>& hashTable, const int& target){ auto it = hashTable.find(target); if (it != hashTable.end()){ return (*it).second; // 返回索引 }else{ return -1; } } void buildHashTable(unordered_map<int,int> &hashTable,const vector<int>& elements){ for(int i=0;i<elements.size();i++){ hashTable.insert({elements[i],i}); } } ``` 这段代码定义了两个主要功能:`buildHashTable()`负责构建哈希表;`findElement()`接受待查元素作为参数,在已建立好的哈希表内寻找对应项,并返回其原始数组下标或-1表示不存在[^2]。 #### Java 中的哈希查找实现 对于Java而言,可以借助内置类库 `HashMap<K,V>` 来轻松完成相同的工作流程: ```java import java.util.HashMap; public class HashSearch { public static void main(String[] args) { HashMap<Integer,Integer> map=new HashMap<>(); Integer[] nums={10,20,30}; for(int i=0;i<nums.length;i++) map.put(nums[i],i); // 将数值及其对应的索引入栈 System.out.println("Index of element '20': "+map.getOrDefault(20,-1)); // 输出指定元素的位置(-1代表找不到) } } ``` 此段代码展示了如何使用 `put()` 方法向 `HashMap` 添加条目以及怎样运用 `getOrDefault()` 安全地获取某个键联的值[^3]。 #### C# 中的哈希查找实现 而在C#, 则推荐采用 `Dictionary<TKey,TValue>` 类型来处理类似的任务: ```csharp using System; using System.Collections.Generic; class Program { static void Main() { Dictionary<int,int> dict = new Dictionary<int,int>(); List<int> list = new List<int>{1,2,3}; foreach(var item in list){ dict[item]=list.IndexOf(item); } Console.WriteLine($"The index of number {2} is {(dict.ContainsKey(2)?dict[2].ToString():"-1")}"); } } ``` 上述例子中,循环遍历列表并将每项连同它们各自的索引一起存入字典对象里。最后打印出给定数字所在的位置信息[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值