热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析数据结构:哈希表(HashTable)的应用与优化

哈希表(HashTable)是一种高效的查找算法,与传统的链表和树结构相比,其在查找过程中无需进行逐个元素的比较。本文将深入探讨哈希表的基本原理、应用场景以及优化策略,帮助读者全面理解其在实际开发中的优势和局限性。通过实例分析和代码示例,我们将展示如何有效利用哈希表提高数据处理效率,并解决常见的冲突问题。

 散列表(Hash table,也叫哈希表)是一种查找算法,与链表、树等算法不同的是,散列表算法在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据元素)的比较操作。

    散列表算法希望能尽量做到不经过任何比较,通过一次存取就能得到所查找的数据元素,因而必须要在数据元素的存储位置和它的关键字(可用key表示)之间建立一个确定的对应关系,使每个关键字和散列表中一个唯一的存储位置相对应。因此在查找时,只要根据这个对应关系找到给定关键字在散列表中的位置即可。这种对应关系被称为散列函数(可用h(key)表示)。

    根据设定的散列函数h(key)和处理冲突的方法将一组关键字key映像到一个有限的连续的地址区间上,并以关键字在地址区间中的像作为数据元素在表中的存储位置,这种表便被称为散列表,这一映像过程称为散列,所得存储位置称为散列地址。

    关键字、散列函数以及散列表的关系如下图所示:


    1、散列函数

    散列函数是从关键字到地址区间的映像。

    好的散列函数能够使得关键字经过散列后得到一个随机的地址,以便使一组关键字的散列地址均匀地分布在整个地址区间中,从而减少冲突。

    常用的构造散列函数的方法有:

    (1)、直接定址法

    取关键字或关键字的某个线性函数值为散列地址,即:

    h(key) = key   或 h(key) = a * key + b

    其中a和b为常数。

    (2)、数字分析法

    (3)、平方取值法

    取关键字平方后的中间几位为散列地址。

    (4)、折叠法

    将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为散列地址。

    (5)、除留余数法

    取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址,即:

    h(key) = key MOD p    p ≤ m

    (6)、随机数法

    选择一个随机函数,取关键字的随机函数值为它的散列地址,即:

    h(key) = random(key)

    其中random为随机函数。

    2、处理冲突

    对不同的关键字可能得到同一散列地址,即key1 ≠ key2,而h(key1)= h(key2),这种现象称为冲突。具有相同函数值的关键字对该散列函数来说称作同义词。

    在一般情况下,散列函数是一个压缩映像,这就不可避免地会产生冲突,因此,在创建散列表时不仅要设定一个好的散列函数,而且还要设定一种处理冲突的方法。

    常用的处理冲突的方法有:

    (1)、开放定址法

    hi =(h(key) + di) MOD m     i =1,2,…,k(k ≤ m-1)

    其中,h(key)为散列函数,m为散列表表长,di为增量序列,可有下列三种取法:

    1)、di = 1,2,3,…,m-1,称线性探测再散列;

    2)、di = 12,-12,22,-22,32,…,±k2 (k ≤m/2),称二次探测再散列;

    3)、di = 伪随机数序列,称伪随机探测再散列。

    (2)、再散列法

    hi = rhi(key)   i = 1,2,…,k

    rhi均是不同的散列函数。

    (3)、链地址法

    将所有关键字为同义词的数据元素存储在同一线性链表中。假设某散列函数产生的散列地址在区间[0,m-1]上,则设立一个指针型向量void *vec[m],其每个分量的初始状态都是空指针。凡散列地址为i的数据元素都插入到头指针为vec[i]的链表中。在链表中的插入位置可以在表头或表尾,也可以在表的中间,以保持同义词在同一线性链表中按关键字有序排列。

    (4)、建立一个公共溢出区

 

    例子以除留余数法和链地址法构造散列表,共用代码如下:

#include
#include #define LEN 13struct hash_node {int count;struct hash_node *next;
};static int hash(int num)
{return num % LEN;
}static void collision(struct hash_node *vec[], int elem, struct hash_node *new)
{if (vec[elem] == NULL)vec[elem] = new;else{new -> next = vec[elem];vec[elem] = new;}
}static void ord_num_print(int i)
{if (i == 1)printf("the 1st element: ");else if (i == 2)printf("the 2nd element: ");else if (i == 3)printf("the 3rd element: ");else printf("the %dth element: ", i);
}static void print_hash(struct hash_node *vec[])
{int i;struct hash_node *tmp;for (i = 0; i count);}while ((tmp = tmp->next) && tmp != NULL);printf("\n");}
}static void create_hash(struct hash_node *vec[], int num)
{FILE *fp;int i, tmp, arr[num];struct hash_node *p;fp = fopen("./hash", "r");for (i = 0; i count = arr[i];p -> next = NULL;tmp = hash(arr[i]);collision(vec, tmp, p);}
}


 其中,hash是散列函数,collision函数用于处理冲突。

    create_hash函数通过读取./hash文件中的num个关键字来构建一个散列表。例子中hash文件的内容如下: 

19 14 23 01 68 20 84 27 55 11 10 79

 

    3、元素插入 

void insert_hash_node(struct hash_node *vec[], int data)
{ int tmp; struct hash_node *p = malloc(sizeof(struct hash_node)); p -> count = data; p -> next = NULL; tmp = hash(data); collision(vec, tmp, p);
}




    4、元素删除 

void delete_hash_node(struct hash_node *vec[], int data)
{ int elem; struct hash_node *p, *tmp; elem = hash(data); if (vec[elem] == NULL) { fprintf(stderr, "vec[%d] is NULL\n", elem); exit(-2); } else { tmp = vec[elem]; while (tmp -> count != data) { if (tmp -> next == NULL) { fprintf(stderr, "not found %d\n", data); exit(-3); } p = tmp; tmp = tmp -> next; } p -> next = tmp -> next; free(tmp); }
}




    在main函数中,通过三步来验证上述所列的各种函数,第一步调用create_hash函数创建一个具有12个关键字的散列表(见下图),第二步插入关键字29,第三步删除关键字1。 

int main(int argc, char *argv[])
{ int i, num; struct hash_node *vec[LEN]; /* num, the number of integers in the ./hash file */ if (argc <2) { fprintf(stderr, "Usage: %s num\n", argv[0]); exit(-1); } for (i &#61; 0; i }





    执行和输出结果&#xff1a; 

$ ./hash_list 12

the first times
the 1st element: NULL
the 2nd element: 79 27 1 14
the 3rd element: NULL
the 4th element: 55 68
the 5th element: NULL
the 6th element: NULL
the 7th element: 84 19
the 8th element: 20
the 9th element: NULL
the 10th element: NULL
the 11th element: 10 23
the 12th element: 11
the 13th element: NULLthe second times
the 1st element: NULL
the 2nd element: 79 27 1 14
the 3rd element: NULL
the 4th element: 29 55 68
the 5th element: NULL
the 6th element: NULL
the 7th element: 84 19
the 8th element: 20
the 9th element: NULL
the 10th element: NULL
the 11th element: 10 23
the 12th element: 11
the 13th element: NULLthe third times
the 1st element: NULL
the 2nd element: 79 27 14
the 3rd element: NULL
the 4th element: 29 55 68
the 5th element: NULL
the 6th element: NULL
the 7th element: 84 19
the 8th element: 20
the 9th element: NULL
the 10th element: NULL
the 11th element: 10 23
the 12th element: 11
the 13th element: NULL







推荐阅读
  • Java集合框架特性详解与开发实践笔记
    Java集合框架特性详解与开发实践笔记 ... [详细]
  • 在MFC框架中,存在多个全局函数,用于在不同对象间获取信息或创建新对象。其中,`afxGetApp`函数尤为关键,它能够帮助开发者轻松获取当前应用程序的实例指针。本文将详细解析`afxGetApp`函数的内部机制及其在MFC应用程序中的具体应用场景,探讨其在提升代码可维护性和灵活性方面的优势。此外,还将介绍其他常用全局函数如`AfxWinInit()`和`AfxBeginThread()`的功能和使用方法,为开发者提供全面的参考。 ... [详细]
  • 探索聚类分析中的K-Means与DBSCAN算法及其应用
    聚类分析是一种用于解决样本或特征分类问题的统计分析方法,也是数据挖掘领域的重要算法之一。本文主要探讨了K-Means和DBSCAN两种聚类算法的原理及其应用场景。K-Means算法通过迭代优化簇中心来实现数据点的划分,适用于球形分布的数据集;而DBSCAN算法则基于密度进行聚类,能够有效识别任意形状的簇,并且对噪声数据具有较好的鲁棒性。通过对这两种算法的对比分析,本文旨在为实际应用中选择合适的聚类方法提供参考。 ... [详细]
  • 在过去,我曾使用过自建MySQL服务器中的MyISAM和InnoDB存储引擎(也曾尝试过Memory引擎)。今年初,我开始转向阿里云的关系型数据库服务,并深入研究了其高效的压缩存储引擎TokuDB。TokuDB在数据压缩和处理大规模数据集方面表现出色,显著提升了存储效率和查询性能。通过实际应用,我发现TokuDB不仅能够有效减少存储成本,还能显著提高数据处理速度,特别适用于高并发和大数据量的场景。 ... [详细]
  • 优化后的标题:深入解析09版Jedis客户端
    深入解析09版Jedis客户端,本文将详细介绍如何在Java项目中正确配置Jedis以操作Redis。首先,确保项目的JDK版本和编译器设置正确。接着,通过Maven或Gradle导入必要的依赖项,如 `redis.clients:jedis`。此外,文章还将探讨Jedis连接池的配置与优化,以及常见问题的解决方案,帮助开发者高效使用Jedis进行Redis操作。 ... [详细]
  • 在CentOS 6.5环境中,本文详细介绍了如何配置SSH无密钥登录,并成功执行PSSH命令。首先,确保系统已安装PSSH工具,可使用 `yum install pssh` 进行安装。若未配置免密钥登录,PSSH命令将无法正常执行,例如尝试运行 `pssh -H root@192.168.245.129 -i uptime` 时会失败。通过生成并分发SSH公钥,可以实现无密码登录,从而顺利执行PSSH命令。此外,本文还提供了详细的步骤和常见问题的解决方案,帮助用户顺利完成配置。 ... [详细]
  • 本文深入解析了Python在处理HTML过滤时的实现方法及其应用场景。通过具体实例,详细介绍了如何利用Python代码去除HTML字符串中的标签和其他无关信息,确保内容的纯净与安全。此外,文章还探讨了该技术在网页抓取、数据清洗等领域的实际应用,为开发者提供了宝贵的参考。 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 深入解析 Android TextView 中 getImeActionLabel() 方法的使用与代码示例 ... [详细]
  • 本文探讨了基于点集估算图像区域的Alpha形状算法在Python中的应用。通过改进传统的Delaunay三角剖分方法,该算法能够生成更加灵活和精确的形状轮廓,避免了单纯使用Delaunay三角剖分时可能出现的过大三角形问题。这种“模糊Delaunay三角剖分”技术不仅提高了形状的准确性,还增强了对复杂图像区域的适应能力。 ... [详细]
  • 在数据表中,我需要触发一个操作来刷新特定列的数据。例如,对于以下表格:| ID | Name | IsDeleted ||----|-------|-----------|| 1 | test | True || 2 | test2 | False |我希望在点击“更新”按钮时,能够仅刷新选定行的“IsDeleted”列。这将有助于确保数据的实时性和准确性。 ... [详细]
  • 本文详细介绍了使用C++实现插入排序算法的方法,并对其进行了优化。通过具体的代码示例,解释了插入排序的基本原理和优化技巧,包括交换两个元素的函数 `SwapTwo` 的实现。此外,文章还探讨了插入排序的时间复杂度和适用场景,为读者提供了深入理解该算法的全面指南。 ... [详细]
  • 在2021-2022 ACM集训队月度编程挑战赛第二轮中,题目“最大值与最小值的选择”要求参赛者处理一个包含n个元素的数组,并给定一个整数k。任务是通过选择特定的子数组,计算并返回这些子数组的最大值和最小值之间的差值。该问题考验了选手对数组操作和优化算法的理解与应用能力。 ... [详细]
  • Eclipse JFace Text框架中IDocument接口的getNumberOfLines方法详解与编程实例 ... [详细]
  • 本文详细介绍了在 Python 中使用 OpenCV 进行图像处理的各种方法和技巧,重点讲解了腐蚀(erode)和膨胀(dilate)操作,以及开运算和闭运算的应用。腐蚀操作可以去除前景物体的边缘部分,而膨胀操作则可以扩展前景物体的边界。开运算和闭运算则是结合这两种基本操作,用于消除图像中的噪声和填充空洞,提高图像处理的效果。通过具体的代码示例和实际应用案例,读者可以深入理解这些技术在图像处理中的重要作用。 ... [详细]
author-avatar
手机用户2502931803
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有