数据结构基础:P11.1-散列查找--->散列表

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-线性结构—>应用实例:多项式加法运算
数据结构基础:P2.5-线性结构—>应用实例:多项式乘法与加法运算-C实现
数据结构基础:P3.1-树(一)—>树与树的表示
数据结构基础:P3.2-树(一)—>二叉树及存储结构
数据结构基础:P3.3-树(一)—>二叉树的遍历
数据结构基础:P3.4-树(一)—>小白专场:树的同构-C语言实现
数据结构基础:P4.1-树(二)—>二叉搜索树
数据结构基础:P4.2-树(二)—>二叉平衡树
数据结构基础:P4.3-树(二)—>小白专场:是否同一棵二叉搜索树-C实现
数据结构基础:P4.4-树(二)—>线性结构之习题选讲:逆转链表
数据结构基础:P5.1-树(三)—>堆
数据结构基础:P5.2-树(三)—>哈夫曼树与哈夫曼编码
数据结构基础:P5.3-树(三)—>集合及运算
数据结构基础:P5.4-树(三)—>入门专场:堆中的路径
数据结构基础:P5.5-树(三)—>入门专场:File Transfer
数据结构基础:P6.1-图(一)—>什么是图
数据结构基础:P6.2-图(一)—>图的遍历
数据结构基础:P6.3-图(一)—>应用实例:拯救007
数据结构基础:P6.4-图(一)—>应用实例:六度空间
数据结构基础:P6.5-图(一)—>小白专场:如何建立图-C语言实现
数据结构基础:P7.1-图(二)—>树之习题选讲:Tree Traversals Again
数据结构基础:P7.2-图(二)—>树之习题选讲:Complete Binary Search Tree
数据结构基础:P7.3-图(二)—>树之习题选讲:Huffman Codes
数据结构基础:P7.4-图(二)—>最短路径问题
数据结构基础:P7.5-图(二)—>哈利·波特的考试
数据结构基础:P8.1-图(三)—>最小生成树问题
数据结构基础:P8.2-图(三)—>拓扑排序
数据结构基础:P8.3-图(三)—>图之习题选讲-旅游规划
数据结构基础:P9.1-排序(一)—>简单排序(冒泡、插入)
数据结构基础:P9.2-排序(一)—>希尔排序
数据结构基础:P9.3-排序(一)—>堆排序
数据结构基础:P9.4-排序(一)—>归并排序
数据结构基础:P10.1-排序(二)—>快速排序
数据结构基础:P10.2-排序(二)—>表排序
数据结构基础:P10.3-排序(二)—>基数排序
数据结构基础:P10.4-排序(二)—>排序算法的比较

文章目录

  • 一、散列的基本思路
  • 二、什么是散列表


一、散列的基本思路

我们来看一种查找需求的场景:

在我们编译的时候,提交给编译系统的是一个源代码,比如说是C语言的源代码。C语言有个规则,变量名必须要先定义后使用。当它碰到变量名的时候,有可能在两个位置。一个是在变量定义的位置,比如说定义int n。还有一种可能是在语句里面使用一个变量,比方说n=n+2
在这里插入图片描述
在使用或者引用这个变量的时候,它首先要判别这个变量有没定义过。没定义过它就给你一个编译错误,如果有定义过了它还要知道这个变量是什么类型的,这个类型在这样的语句环境里面能不能用。比方说我们以前定义的一个指针变量int *p,结果你让p=p*2 p。虽然p定义过了,但是指针变量不能这么用,所以它又要给你一个错误,所以我们涉及到变量的管理问题。
数据结构基础:P11.1-散列查找--->散列表_第1张图片
把它抽象一下,是对变量以及变量的属性的管理,涉及到插入查找。当我们碰到一个新的变量定义的时候,我们要把这个变量名以及它相应的定义插到我们要管理的这个集合里去。在编译的时候碰到一个变量,需要去找这个变量有没有定义过,也是说在这个集合里面存在不存在。如果是存在的,他把相应的属性拎出来,然后在语句环境里面去判别这样使用变量行不行。除了插入查找之外,我们还有可能会删除,所以这实际上是个动态查找问题。


对这样的一个动态查找问题,前面我们也有提到过一些方法,如查找树、AVL树。我们来分析看看能不能用 AVL树来解决。

在查找树的查找过程当中,经常要把一个关键词跟当前结点的关键词进行比较。如果对变量名来讲,意味着我们要把两个变量名进行比较,而变量名比较比整数的比较要复杂。整数比较一次比较大小相等就行了,而变量是个字符串,字符串的比较要一个一个字符的比下去,很显然这种比较的时间会比较长。所以用查找树、AVL树不是很好的方法。


假如说有个函数能够把变量名转化成某个具体的数字,接下来我们要比较这个变量名就变成是比较两个数字,这就快的多了。这些思想就属于散列查找里面的一些基本思想。接下来我们再来分析一下到目前已知的查找有哪些

顺序查找:复杂度为 O ( N ) \rm{O(N)} O(N),效果较差
二分查找(静态查找):复杂度为 O ( l o g 2 N ) {\rm{O(lo}}{{\rm{g}}_{\rm{2}}}{\rm{N)}} O(log2N),但是不适用于涉及插入删除操作的动态查找
二叉搜索树:复杂度为 O ( h ) \rm{O(h)} O(h),h为二叉查找树的高度,涉及大量比较操作,不合适
平衡二叉树:复杂度为 O ( l o g 2 N ) {\rm{O(lo}}{{\rm{g}}_{\rm{2}}}{\rm{N)}} O(log2N),涉及大量比较操作,不合适


我们来看一个例子:QQ账号的管理

题目描述:每次登录QQ的时候,我们要输入我们的账号,而账号是一个很长的数字。那么用什么样的一种方法来管理这些账号,使得我们能很快的找到根据这个账号找到用户的信息。如果我们用二分查找:
①十亿 ( 1 0 9 ≈ 2 30 ) (10^9≈2^{30}) (109230)有效用户,用二分查找30次---->合理
②十亿 ( 1 0 9 ≈ 2 30 ) × 1 K ≈ 1024 G \rm{(10^9≈2^{30})×1K≈1024G} (109230)×1K1024G,1T连续空间---->合理
③按有效QQ号大小有序存储:在连续存储空间中,插入和删除一个新QQ号码将需要移动大量数据---->不合理
分析:所以二叉查找在这里不是好的一种方法,我们需要有一种好的方法来解决类似的问题,方法的关键就是:我们怎么能
快速找到关键词所在的位置,同时某些关键词可能是不太方便,不像整数实数一样进行直接的大小比较,要进行字符串的比较。而且,还是一个动态查找的过程,会经常有插入和删除操作。


根据上面的问题,我们再来看看查找的本质

查找的本质: 已知对象找位置,就是我有若干个对象,我事先放好在某些位置,然后我给你一个对象,让你去找这个对象在哪个位置出现。
有序安排对象
------全序:典型的是二分查找,从小到大将所有数据排好
------半序:某些关键词之间存在的一个秩序,典型的就是我找树。它的秩序是这样的:任何一个结点都比左子树的所有结点要大,比右子树的所有结点要小,但不像前面的二分查找一样的完全有序。
直接算出对象位置:散列


散列查找法的两项基本工作:

计算位置:构造散列函数确定关键词存储位置。即设计一个计算函数,这个函数为这个对象计算出一个整数值,这个整数值就代表这个对象将来要放在哪个位置;
解决冲突:应用某种策略解决多个关键词位置相同的问题。我们没法预计未来的对象是什么,很有可能来了一个对象他的散列函数算出的值对应的位置跟前面某个对象位置是一样的。这种情况不能绝对避免的,于是就要设计一个冲突解决策略。
时间复杂度几乎是常量: O ( 1 ) \rm{O(1)} O(1),即查找时间与问题规模无关


二、什么是散列表

前面我们介绍了散列的基本思路,具体来说它的抽象数据结构描述可以这样来表述:

类型名称:符号表(SymbolTable)
数据对象集:符号表是名字(Name)-属性(Attribute)对的集合。
操作集:Table∈SymbolTable,Name∈NameType,Attr∈AttributeType
1、SymbolTable InitializeTable( int TableSize ):
----创建一个长度为TableSize的符号表;
2、Boolean IsIn( SymbolTable Table, NameType Name):
----查找特定的名字Name是否在符号表Table中;
3、AttributeType Find( SymbolTable Table, NameType Name):
----获取Table中指定名字Name对应的属性;
4、SymbolTable Modefy(SymbolTable Table, NameType Name, AttributeType Attr):
----将Table中指定名字Name的属性修改为Attr;
5、SymbolTable Insert(SymbolTable Table, NameType Name, AttributeType Attr):
----向Table中插入一个新名字Name及其属性Attr;
6、SymbolTable Delete(SymbolTable Table, NameType Name):
----从Table中删除一个名字Name及其属性。

例子:

题目描述:有n=11个数据对象的集合{18,23,11,20,2,7,27,30,42,15,34}。符号表的大小用TableSize = 17,选取散列函数h如下:h(key) = key mod TableSize (求余)
存放
h(18)=1, h(23)=6, h(11)=11, h(20)=3, h(2)=2…….
在这里插入图片描述
如果新插入35, h(35)=1, 该位置已有对象!冲突!!
②查找::
key = 22, h(22)= 5,该地址空,不在表中
key = 30, h(30)= 13,该地址存放是30,找到
装填因子(Loading Factor):设散列表空间大小为m,填入表中元素个数是n,则称α =n/m为散列表的装填因子。本例子中α=11/17≈0.65

例子:

题目描述:将acos、define、float、exp、char、atan、ceil、floor、clock、ctime,顺次存入一张散列表中。 散列表设计为一个二维数组Table[26][2],2列分别代表2个槽,以便第一次发生冲突时可以将冲突的放在第二个槽。散列函数h(key) = key[0] – ‘a’
存放:根据散列函数依次为这些字符串设置存放位置。其中,最后的clock、ctime无法插入,因为冲突了,且我们这里没有设置冲突策略。
数据结构基础:P11.1-散列查找--->散列表_第2张图片
②从上面两个例子我们可以看出:如果没有溢出, T 查 询 = T 插 入 = T 删 除 = O ( 1 ) \rm{T_{查询}=T_{插入}=T_{删除}=O(1)} T=T=T=O(1)

你可能感兴趣的:(数据结构基础,数据结构,散列表,算法,c算法,c语言)