以有序链表构成的开放式散列表

字节跳动面经全解析:从算法到系统设计的一线避坑指南 在技术面试中,算法与系统设计始终是衡量工程师核心能力的标尺。无论是字节跳动还是其他一线互联网公司,面试官都希望通过算法题考察候选人的逻辑拆解与代码实现能力,通过系统设计题评估其在复杂场景下的架构权衡与工程落地水平。理解HashMap的优化细节、动态规划的转移方程、LRU缓存的数据结构组合,本质上都是在训练同一种思维——把问题分解、抽象、建模,再给出可验证的解决方案。这种能力不仅服务于面试,更直接应用于日常开发中的性能优化与高并发场景设计。对于准备跳槽或校招的工程师而言,掌握算法题的刷题方法论、熟悉系统设计的 阅读详情

1. 前言

在复杂编程中,常常遇到查找问题,如何能更快更简单的找到我们需要的数据是一个经典问题。在各种查找方法中最简单的是使用数组或者链表,进行线性查找,但是往往效率比较低。因为如果要查找的数据恰好在线性表前面那么自然很快就能得到结果,而目标处在最后则要耗费很多时间去挨个查找。另一方面,大量的线性查找也意味着我们的程序当中要嵌套大量的循环语句,这本身对于程序可读性和运行效率都是不利的。我个人不建议在程序中循环或者分支语句超过三层,多了就会让人感觉代码像一团浆糊,单单辨别划分程序块的括号就很困难了。

    为了能更快找到数据产生了很多更高级的数据结构,比如BST,跳跃表,散列表等等。这些数据结构都采用非线性方式查询数据,可以说各有优缺点。本文就是探讨如何使用开放式散列表来加速查找。

2. 原理分析

在各种查找问题中遇到最普遍的问题往往是这样的:给你一个键值(key),在一堆的数据当中到底存不存在与其相关的记录,如果有的话记录的具体值是多少?我们可以想象,使用线性表的话,随着记录数目增多查找变得越发困难,散列表(Hash Table)也就应运而生。

散列表本身不像BST或者跳跃表那样属于有序结构。因此你的数据可以不必事先排序,散乱的堆放到表中。查找的核心在于散列函数(Hash Function),通过将键值带入散列函数而计算得到一个索引值(Index),这个索引值或者我们称之为槽号(Slot Number)就是数据记录的位置。因此理想的情况下我们根本不需要去轮询数据,只需要一次简单的计算就能得到数据的准确位置。

既然如此,整个算法的核心就在于这个散列函数。最简单的一个散列函数就是取模,也就是key % M。其中M代表散列表的大小,也就是槽的总数。不同的键值取得的模不同,数据也就通过运算被“分散”到一个线性空间里。到这里,你可能会发现并非所有的键值都会产生不同的散列值。比如,M为10的话,key如果为1,11,21都会取得余数为1,这样不同元素的散列就会发生碰撞,称之为散列冲突(Hash Collision)。那么,我们衡量一个好的散列函数就是通过尽可能少的计算,将键值更平均的分散到线性地址空间中,尽可能的减小冲突发生的可能性。显然,上面提到的这个例子并不是一个很好的解决方案。

这里有一个典型例子是UNIX系统中使用的著名的ELFHash函数,就是经典的字符串散列函数。该函数被用在UNIX的ELF文件系统中因此而得名。它能很好的实现任意长字符串的平均分布。

unsigned long Hashfn(char* pKey)

{

    unsigned long h = 0, g;

    while(*pKey)

    {

       h = (h << 4) + *pKey++;

       g = h & 0xF0000000L;

       if (g) h^= g >> 24;

       h &= ~g;

    }

    return h % M;

}

然而即便如此,在一个散列表中冲突发生还是不可避免的。我们几乎很难找到一个具有完美散列(Perfect Hashing)性质的函数。那么要想构建一个散列表就不得不考率冲突解决方案。由此,也引出了下面将要讨论的开放式散列表。

所谓的解决散列冲突就是将已经被占用的槽进行二次分配。一般的解决方案可以分为两类:一种是地址开放式的散列表,冲突的数据可以被放在表外,称为开散列(Open Hashing)。另外一种是将冲突数据按照一定的探查序列放在表内,为闭合散列。这里我选择开散列的原因在于其实现简单,而且更加灵活,利于内存操作。而闭合散列需要额外的探查函数(Probe Function)甚至二次散列函数进行二次探查,比较复杂。另外最重要的一点,闭合散列在进行删除操作时必须保留该槽曾经被占用的信息,有的书中将其形象称为“墓碑”,否则将会中断探查序列。然而长时间的增删操作之后,表内的墓碑就会越来越多,查询速度也就会越来越慢,为了提升效率只能重建探查序列甚至重建整个散列表,实在是麻烦。

3. 开放式散列

开放式散列实现的典型办法是在槽外挂接链表。建立时,键值被散列后查询该槽,如果发生冲突则在槽外挂接一个节点,由此实现了冲突解决。查询同理,发生冲突则根据后继指针遍历整个链表,非常方便。

由于开放式散列比闭合散列存在查询速度的劣势,为了提高查询效率,加入了自适应查询的功能。将链表改为双链表,并且在节点当中记录访问次数,每次访问后根据查询次数沿着前驱指针查找,将被访问最多的节点置换到更靠近槽的位置。换句话说,可以理解为根据槽内的访问次数实时排序。

4. 数据结构

目前的demo中将散列表封装成类CHashTable。为了节省内存空间,散列表内只记录节点指针,数据被加入表中时再动态申请空间。因此那些未被占用的槽就不会造成太严重的浪费。在CHashTable内部同样只记录了表指针,这样你可以根据自己的需要通过Create函数定制任意大小的散列表了。结构如下:

class CHashTable

{

protected:

    unsigned long m_nSize;

    PHASHTBLELEM* m_pHashTbl;

    void   ReSort(PHASHTBLELEM pElem);

    void   Remove(PHASHTBLELEM pElem);

public:

    CHashTable(void);

    ~CHashTable(void);

    void Create(unsigned long nHashMaxSize);

    unsigned long GetSize();

    unsigned long Hashfn(LPCTSTR pKey);

    void HashElem(LPWSTR pKey, LPVOID pData);

    BOOL UnHashElem(LPWSTR pKey);

    LPVOID FetchElem(LPWSTR pFetchKey);

};

 

链表节点被封装为结构体HASHTBLELEM,具体结构如下:

typedef struct _HASHTBLELEM

{

    LPWSTR pKey;

    LPVOID pData;

    UINT   nAccessCount;

    _HASHTBLELEM* pNextElem;

    _HASHTBLELEM* pPrevElem;

} HASHTBLELEM, *PHASHTBLELEM;

pKey中记录键值信息。pData被声明为无类型指针,你可以根据实际需要将任何东西放到节点当中,可以是一个变量,一个数组,一个结构体,甚至一个对象。这样做的好处在于给使用者更大的灵活性,缺点在于你必须自己手动强制转换成需要的类型,而且要特别小心接下来的指针操作,以免发生问题。在demo中我向pData中存放的是一个字符串。nAccessCount记录的是访问计数。最后两个成员是链表需要的前驱指针和后继指针。

5. 关于函数的说明

Create函数可以帮助你建立一个理想大小的散列表。

void CHashTable::Create(unsigned long nHashMaxSize)

{

    m_nSize = nHashMaxSize;

    m_pHashTbl = new PHASHTBLELEM[m_nSize];

    memset(m_pHashTbl, NULL,     sizeof(PHASHTBLELEM)*m_nSize);

}

 

HashElem可以向表内增加一个元素。当然你需要指定键值和要存储的数据。

void CHashTable::HashElem(LPWSTR pKey, LPVOID pData)

{

    UINT Index = Hashfn(pKey);

    PHASHTBLELEM* pSlot = &m_pHashTbl[Index];

    PHASHTBLELEM pElem = *pSlot;

    if (!pElem)

    {

       *pSlot = pElem = new HASHTBLELEM;

       pElem->pKey = pKey;

       pElem->pData = pData;

       pElem->nAccessCount = 0;

       pElem->pNextElem = NULL;

       pElem->pPrevElem = NULL;

    }

    else

    {

       while(pElem->pNextElem)

       {

           pElem = pElem->pNextElem;

       }

       pElem->pNextElem = new HASHTBLELEM;

       PHASHTBLELEM pPrevElem = pElem;

       pElem = pElem->pNextElem;

       pElem->pKey = pKey;

       pElem->pData = pData;

       pElem->nAccessCount = 0;

       pElem->pPrevElem = pPrevElem;

       pElem->pNextElem = NULL;

    }

}

 

 

 

 

UnHashElem是删除函数,可以删除相关键值的节点。如果成功返回TRUE,失败则返回FALSE。

BOOL CHashTable::UnHashElem(LPWSTR pKey)

{

    UINT Index = Hashfn(pKey);

    PHASHTBLELEM* pSlot = &m_pHashTbl[Index];

    PHASHTBLELEM pElem = *pSlot;

    if (pElem)

    {

       PHASHTBLELEM pNextElem = pElem->pNextElem;

       do

       {

           if (!StrCmp(pKey, pElem->pKey))

           {

              Remove(pElem);

              if (!pNextElem || !pNextElem->pPrevElem) *pSlot = pNextElem;

              return TRUE;

           }

           else

           {

               pElem = pElem->pNextElem;

           }

       } while (pElem);

    }

    return FALSE;

}

 

FetchElem是查询数据,只需要将键值带入就会返回相关数据的内容。需要注意的是返回值仍然是LPVOID注意类型转换。如果查询失败,函数会返回NULL。

LPVOID CHashTable::FetchElem(LPWSTR pFetchKey)

{

    unsigned long Index = Hashfn(pFetchKey);

    PHASHTBLELEM* pSlot = &m_pHashTbl[Index];

    PHASHTBLELEM pElem = *pSlot;

    if (pElem)

    {

       do

       {

           if (!StrCmp(pFetchKey, pElem->pKey))

           {

              pElem->nAccessCount++;

              ReSort(pElem);

              if (!pElem->pPrevElem) *pSlot = pElem;

              return pElem->pData;

           }

           else

           {

              pElem = pElem->pNextElem;

           }

       } while (pElem);

    }

    return NULL;

}

 

需要了解的一点是:这里UnHashElem和FetchElem分别调用了另外两个保护成员函数,Remove和Resort。前者的作用是删除并重新挂接节点,后者则是根据访问次数进行链表排序。代码如下:

void CHashTable::Remove(PHASHTBLELEM pElem)

{

    PHASHTBLELEM pPrevElem = pElem->pPrevElem;

    PHASHTBLELEM pNextElem = pElem->pNextElem;

    if (!pPrevElem)

    {

       delete[] pElem->pKey;

       delete[] pElem->pData;

       delete[] pElem;

       if (pNextElem) pNextElem->pPrevElem = NULL;

    }

    else

    {

       delete[] pElem->pKey;

       delete[] pElem->pData;

       delete[] pElem;

       pPrevElem->pNextElem = pNextElem;

       if (pNextElem) pNextElem->pPrevElem = pPrevElem;    

    }

}

 void CHashTable::ReSort(PHASHTBLELEM pElem)

{

if (pElem->pPrevElem && pElem->pPrevElem->nAccessCount < pElem->nAccessCount)

    {

       PHASHTBLELEM pPrevElem = pElem->pPrevElem;

       PHASHTBLELEM pPrevPrevElem = pPrevElem->pPrevElem;

       PHASHTBLELEM pNextElem = pElem->pNextElem;

       if (pPrevPrevElem) pPrevPrevElem->pNextElem = pElem;

       pElem->pPrevElem = pPrevPrevElem;

       pElem->pNextElem = pPrevElem;

       pPrevElem->pPrevElem = pElem;

       pPrevElem->pNextElem = pNextElem;

       if (pNextElem) pNextElem->pPrevElem = pPrevElem;

       ReSort(pElem);

    }

    else

    {

       return;

    }

}

 

 

 

6. 总结

关于开放式散列表我写了一个demo程序,可以作为参考。总的来说中心思想就是通过牺牲一点存储空间来大幅加快访问速度,而且不需要手动对内部元素做排序处理。

另外,还存在一种比较坏的访问情况,就是反复不断的交替访问某两个元素,而恰恰它们又都位于一个槽当中。这会导致链表当中的元素不断的互相挤占被访问的有利位置而造成程序效率下降。虽然可能不常见,但是想想任何数据结构都没有十全十美的解决方案吧。

 

 

附demo下载地址:http://download.youkuaiyun.com/source/3126408

快手工程B卷复盘:校招笔试如何考察工程能力 校招笔试是大厂选拔应届生的重要关卡,尤其对于后端、客户端等工程岗位,笔试重点往往不在竞赛级算法难度,而在于候选人是否具备直接解决实际工程问题的能力。数据结构、操作系统、数据库与Linux等基础知识,必须结合真实业务场景灵活运用,才能应对场景设计题和开放式问题。本文从笔试定位、考点分布、答题时间分配、避坑经验等维度展开,帮助你快速定位自身在工程能力上的短板,建立一套可复用的备考与答题框架。无论你正在准备秋招还是实习转正,掌握这些核心思路,都能有效提升笔试通过率。 阅读详情

相关推荐

京东Linux后台开发面经汇总:高频考点与题解思路

Linux后台开发岗位的核心能力在于对操作系统原理、网络编程与高并发场景的深刻理解。从进程线程调度、内存管理到IO模型,每一个基础概念都直接决定线上问题排查的效率和系统设计的可靠性。掌握这些原理不仅能应对技术面试中的深度追问,也是构建高性能服务的关键。在实际工程中,如何利用文本处理工具分析海量日志、如何通过epoll模型支撑高并发连接、如何通过MySQL索引优化查询性能,都是后台工程师的必备技能。本文汇总京东Linux后台开发面试中的高频考点,覆盖命令实战、系统原理、数据库与中间件、算法与系统设计等,帮助读

weixin_33831196的博客 302

检索(散列)

检索算法分类: 顺序表和线性表法 根据关键码值直接访问方法(散列表) 树索引方法 顺序检索(关键码值按大小排序) 最基础的检索算法,但是遇到重复查找的庞大记录集合,顺序检索会慢到令人无法忍受。一般常用的索引方式是二分查找自组织线性表由于线性表大多数情况下是使用关键码进行顺序排序的,但是这对于一些特定情况将会减慢搜索速度,例如某一关键码时常出现,但是它又在线性表的末端,这样每次都得执行到末端才能访问

mumusan2016的博客 1543

2023前端面试复盘:快手三面+HR面完整记录与避坑指南

前端面试的核心不仅是对知识点的记忆,更是对原理理解深度与工程实践能力的综合检验。从事件循环、浏览器缓存到React Fiber架构与并发模式,再到性能优化、工程化与系统设计,大厂面试官越来越倾向于围绕候选人的项目经历层层追问,考察其是否具备闭环的问题拆解能力。深入理解框架设计原理、掌握虚拟滚动与代码分割等性能优化手段、熟悉微前端与监控平台等工程化实践,已成为前端开发者应对高阶面试的关键竞争力。在求职窗口期,系统性地进行项目复盘、算法题训练和模拟面试,是提升面试表现的有效路径。本文以2023年快手前端面试为样

weixin_33889665的博客 327

哈希表 - 闭散列

闭散列主要就是需要注意一下“墓碑”的操作,其他的其实都简单。ASL的计算忘了,找了两道题都没算对,这才是我不写的真正原因…代码里是装13用的… #include&lt;bits/stdc++.h&gt; using namespace std; /* 哈希表: 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也...

isjinhao的博客 828

解决哈希冲突——闭散列和开散列(数据结构)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、闭散列二、开散列1.引入库2.读入数据三、再散列(了解) 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、闭散列 当发生哈希冲突时,从发生哈希冲突的位置开始按照某种方式找“下一个”空位置。 1.通过哈希函数计算哈希地址 2.插入元素----注意:可能

m0_51405559的博客 2024

数据结构课程设计--线性开型寻址散列表

数据结构课程设计--线性开型寻址散列表,使用qt做的界面

哈希(Hash) - 开散列/闭散列

哈希的基本认识,哈希函数的设计,以及开散列和闭散列的实现

qq_61939403的博客 2246

偏最小二乘结构方程(PLS-SEM)_TomatoSCI分析日记

①模型整体拟合指标(Goodness-of-Fit, GOF),这个可以看做整体的R方了,上限是1;②指标载荷的具体值;然而,当数据中包含潜变量(即由多个观测指标组合而成的综合概念)时,贝叶斯结构方程不太适用。指标过少(1-2 个)会削弱潜变量的解释力,而指标过多可能增加噪声,影响模型稳定性。如果指标较多,可考虑进一步划分潜变量,但应确保每个指标有意义。掌握这两类方法后,在处理多变量、因果或路径分析研究时,可以根据数据特点与研究需求灵活选择。对于一般的多变量研究,这两类结构方程均为常用且有效的分析方案。

qq_44684643的博客 2012

关于HashMap的一些按位与计算的问题

关于HashMap的一些按位与计算的问题,HashMap.hash()

蔡梦缘的博客 5511

robomimic实验复现全流程:从数据集下载到训练视频生成

本文详细介绍了机器人模仿学习项目RoboMimic的完整实验复现流程。内容涵盖从使用Conda创建Python 3.8隔离环境、安装RoboSuite仿真器和MuJoCo物理引擎,到下载并探索数据集、配置与启动模型训练,最后进行模型评估和生成策略执行视频。该指南旨在帮助研究者和开发者高效搭建实战环境,规避常见问题,快速验证算法或应用于实际场景。

weixin_29089057的博客 518

Android 车载应用开发与优化(1) - CarLauncher 核心功能与动态集成探秘

本文深入探讨了Android车载应用开发中CarLauncher的核心功能与实现原理。作为车载系统的“脸面”,CarLauncher不仅提供应用入口,更通过动态集成技术将地图、音乐等关键信息嵌入桌面,以保障驾驶安全与体验。文章详细解析了其源码结构、权限配置、应用启动机制以及基于Activity嵌入的动态集成黑魔法,为开发者提供了从基础认识到高级优化的完整指南。

weixin_29239279的博客 284

哈希 散列函数以及碰撞处理_散列| 公开地址以进行碰撞处理

哈希 散列函数以及碰撞处理Prerequisite: Hashing data structure 先决条件: 哈希数据结构 开放式寻址 (Open addressing) In open addressing, all the keys will be stored in the hash table itself, not by using any additional memory o...

827

基于强化学习的工业SCR脱硝系统控制算法设计与实现

本文提出了一种基于强化学习的工业SCR脱硝系统智能控制方法。针对SCR系统存在的非线性、大滞后和多变量耦合等控制难点,研究采用混合建模方法构建了精度在5%以内的系统模型,并设计了专门的强化学习环境。该方法通过定义合理的动作空间和状态空间,结合物理约束设计损失函数,利用深度强化学习算法优化氨喷射控制策略。相比传统PID控制,该智能控制器能更好地适应烟气参数波动,实现NOx排放的稳定控制,为工业环保设施提供了更优的自动化解决方案。

huanghm88的专栏 1459

C/C++面试核心:内存管理、OOP、STL与系统编程深度解析

在计算机编程领域,内存管理和面向对象编程是构建高效、可靠软件系统的基石。内存管理涉及程序运行时数据的组织与访问,其核心原理在于理解堆、栈等内存区域的分配与释放机制,这直接关系到程序的性能与稳定性。C++通过引入智能指针等现代特性,将资源获取即初始化的思想融入语言层面,显著提升了内存管理的安全性与便捷性。面向对象编程则通过封装、继承和多态三大特性,为代码复用和模块化设计提供了强大支持,其中虚函数表机制是实现运行时多态的关键技术。这些基础概念与原理,在操作系统、游戏引擎、高频交易等对性能和控制力要求极高的应用场

weixin_34095889的博客 459

把机器学习“搬进”数据库:SAP HANA PAL 快速上手与 ABAP AMDP 实战

摘要:本文探讨了SAP HANA PAL(Predictive Analysis Library)在企业数据分析中的实践应用。PAL通过将机器学习算法下推到数据库内部执行,有效解决了大数据量场景下的数据搬运瓶颈和治理成本问题。文章详细介绍了PAL的核心功能(包括聚类、分类、回归等10类算法)、使用前提(版本、组件和权限要求)以及通过AMDP实现ABAP与PAL集成的技术方案。重点阐述了参数表设计理念和两个典型应用场景:利用PageRank算法分析企业权限网络关系,以及通过PAL_SCALE进行特征预处理。文

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 5739

TSN新技术,让您的设备网络“更实时、更确定、更安全”

近日,工业和信息化部发布了2022年第23号公告,批准发布行业标准YD/T 4134-2022《工业互联网时间敏感网络需求及场景》。该标准是国内首个时间敏感网络(TSN)技术标准,标志着我国TSN技术标准体系建设迈出了坚实的一步,对构建工业互联网网络标准体系具有重要意义。TSN源于Time-Sensitive Networking的缩写,中文译名为时间敏感网络,因其具备的确定性和微秒级交互特性,受到了对实时性要求较高的工业控制领域的关注。TSN与工业数智化。

创龙,嵌入式一体化解决方案商 771

全方位Java面试准备指南:从基础原理到实战表达

技术面试不仅是知识点的堆砌,更是思维方式的验证。在Java生态中,从JVM内存模型到并发编程的锁机制,从HashMap底层原理到Spring框架的自动装配,每个高频考点都需要从原理层面理解,而非单纯背诵。掌握一套系统性的准备路径,能帮助候选人在基础功底、进阶原理、项目实战和软性素质四个维度均衡发力。通过STAR法则表达项目经历,结合手写算法与源码解读,将知识内化为解决实际问题的能力,是提升面试通过率的关键。本文围绕Java技术栈的全面复盘,从高频考点梳理到临场表达策略,为校招与社招提供可落地的行动指南。

weixin_33851604的博客 305

YOLO26优化:注意力魔改 | 用于微小目标检测的上下文增强和特征细化网络ContextAggregation,助力小目标检测,暴力涨点

用于微小目标检测的上下文增强和特征细化网络ContextAggregation,助力小目标检测

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 492

Python开发实习全攻略:从小白到转正的18周实战之旅

Python以其简洁的语法、丰富的生态和广泛的应用场景(Web开发、数据分析、人工智能、自动化运维等),成为众多企业招聘实习生的热门技术栈。本文将围绕“Python开发工程师”实习岗位,系统性地解答这三个核心问题,帮助你理清思路,更有准备地开启第一段职业旅程。无论你是大二/大三正在寻找第一份实习,还是已经拿到Offer准备入职,这篇文章都能为你提供有价值的参考。这是你的“基础版”简历,需要清晰展示你的技术栈、项目经历和教育背景。理解这两者的区别,能帮助你更好地适应实习,并最大化实习价值。

热爱技术与前沿创新,深耕科技领域,在科创中精进自我;探索技术乐趣,分享技术干货与成长心得,以技术为伴,热爱生活,在科创与生活中双向成长。 273

【Android Performance】Android CPU绑核实战:从taskset到cpuset的完整调优指南

本文系统讲解Android系统中的CPU绑核(CPU Affinity)技术,涵盖用户空间的taskset命令行绑核、内核cpuset分组机制的原理与操作、进程级与线程级绑核的代码实现、以及中断绑核的底层方法。文中深入拆解CPU亲和性掩码的计算规则、各cpuset分组的默认策略差异、绑核后的验证手段及常见踩坑场景,并配有多项实操命令与C代码示例。适合从事Android系统性能调优、进程调度优化的开发者阅读。

weixin_44081096的博客 1313

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C++数据结构——散列表(开散列)

#include <iostream> #include <cmath> using namespace std; const int Maxsize=11; struct Node { int data; Node* next; }; class HashTable2 { public: HashTable2(); ~HashTable2(); int Insert(int k); Node* Search(int k); v

qq_45875966的博客 904

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

开放性地址处理法与冲突法处理哈希表的查找和插入

/*用开放性地址处理冲突法定义的哈希表*/ #define M 997 typedef struct{ KeyType key; DataType data; }NodeType; typedef NodeType HashTable[M]; --------------------------------------------------------------

rzchong1988的专栏 1750

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

解决哈希冲突的三种方法(拉链法、开放地址法、再哈希法)

上篇博客我们说到了,什么是哈希冲突,其实就是再采用哈希函数对输入域进行映射到哈希表的时候,因为哈希表的位桶的数目远小于输入域的关键字的个数,所以,对于输入域的关键字来说,很可能会产生这样一种情况,也就是,一个关键字会映射到同一个位桶中的情况,这种情况就就叫做哈希冲突,解决哈希冲突的有三种方案,一种叫做拉链法(也叫作链接法、链地址法,一个意思),另外三种分别为开发地址法和再散列法。 一、拉链法 ...

小猛同学的博客 6万+

[C++]散列表 线性开型寻址&链表散列

线性开型寻址 由一维数组存放散列表,数组的每一项称为桶,不同的桶对应不同的关键字。 向散列表中插入元素时,先按照元素的关键字找到关键字对应的桶,若桶为空,则将元素存入其中;若桶已被其他元素占用,则从该桶开始向后寻找空桶(将数组视为环形,即数组末项的后一项是数组首项),将元素存入找到的第一个空桶中,若找不到空桶,说明数组已满,插入失败。 删除散列表元素时,先判断该元素是否存在,若不存在则无法删除。将该元素所在的桶清空,然后将其之后、下一个空桶之前所有受该桶影响被挤走的元素依次向前移动到空位。 代码 class

mzman的博客 947

开放地址散列表

开放地址散列表(open addressing hashing):是一种不使用链表解决冲突的方法。如果有冲突发生,那么就要尝试选择另外的单元,直到找出空的单元为止。1.线性探测法 2.平方探测法 3.双散列//开放地址散列表 //类型声明 enum KindOfEntry{ legitimate, empty, deleted };struct HashEntry{

yvhqbat的博客 714

[数据结构]Hash表初学(开放寻址法 )

/* Name:Hash表初学 (数组实现链表 开放寻址法 ) Actor:HT Time:2015年9月29日 Error Reporte: */ #include"stdio.h" #include"string.h" #include"stdlib.h" int hash[9000]; int value[9000]; int f1(int x) { return x

2361
下一篇: MFC下的日志系统开发
vector03
博客等级 码龄16年 62粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值