虽是读书笔记,但是如转载请注明出处 http://segmentfault.com/blog/exploring/
.. 拒绝伸手复制党
想更一进步的支持我,请扫描下方的二维码,你懂的~
查找
根据给定的关键字值,在一组数据中确定一个其关键字值等于给定关键字值的数据元素。
若存在这样的数据元素,则称查找是成功的;否则称查找不成功。一组待查数据元素的集合又称为查找表。
关于查找,一般要明确下述两个问题。
(1)查找的方法
按照数据元素的组织方式决定采用的查找方法;为了提高查找方法的效率,又要求数据元素采用某些特殊的组织方式。因此,在研究各种查找方法时,必须弄清各种方法所适用的组织方式。
(2)查找算法的评价
标准有两个:时间复杂度和空间复杂度。在查找算法中,基本运算是给定值与关键字值的比较,所以,我们引出一个成为平均查找长度(ASL)的概念,作为评价查找算法好坏的依据。
对于含有 n 个数据元素的查找表,查找成功时的平均查找长度为:
n
ASL = ∑ Pi* Ci
i=1
其中:Pi 为查找表中第 i 个数据元素的概率,Ci 为查找第 i 个数据元素时需比较的次数。
顺序表查找 - 静态查找
顺序表的几种查找算法:
顺序查找、折半查找和分块查找
顺序查找
基本思想:
从第一个元素开始,逐个把元素的关键字值和给定值比较,若某个元素的关键字值和给定值相等,则查找成功;否则,若直至第 n 国记录都不相等,说明不存在满足条件的数据元素,查找失败。
查找表的存储结构:
既适用于顺序存储结构,也适用于链式存储结构。
算法讨论:
平均查找长度 ASL 在等概率的情况下
n n
ASL = ∑ Pi*Ci =(∑(n-i+1) )/n =n+1/2
i=1 i=1
平均查找长度 ASL 在等概率的情况下
ASL ≤n/2
优点:
-对结点的逻辑次序 (不必有序) 和存储结构 ( 顺序、链表均可)无要求;
-当序列中的记录 “基本有序” 或 N 值较小时,是较好的算法;
缺点:
-ASL 较长
顺序表查找算法的特点是简单、容易实现.但其缺点也是不容忽视的,那就是要预先分配最大存储空间、频繁地插入、删除操作效率很差。
折半查找
基本思想:
先将欲查找的数值和该组数据的中间位置上的数值比较,当小于中间值时,再向前查询,大于中间值时向后查询,继续取前面(或后面)一半数据的中间值进行比较,如果小于再向前查询,大于就向后查询,一直到找到或查询完毕为止。
折半查找树
折半查找的过程看,以有序表的中间记录作为比较对象,并以中间记录将表分割为两个子表,对子表继续上述操作。所以,对表中每个记录的查找过程,可用二叉树来描述,二叉树中的每个结点对应有序表中的一个记录,结点中的值为该记录在表中的位置。通常称这个描述折半查找过程的二叉树为折半查找判定树。
(1)折半查找判定树是一颗BST,即每个节点的值均大于其左子树上所有节点的值,小鱼右子树上所有节点的值;
(2)折半查找判定树中的结点都是查找成功的情况,将每个结点的空指针指向一个实际上并不存在的结点——称为外结点,所有外结点即是查找不成功的情况,如图(e)所示。如果有序表的长度为n,则外结点一定有n+1个。
(3)在折半查找判定树中,某结点所在的层数即是查找该结点的比较次数,整个判定树代表的有序表的平均查找长度即为查找每个结点的比较次数之和除以有序表的长度。
例如,长度为10的有序表的平均查找长度为: ASL = (1×1+2×2+3×4+4×3)/10=29/10
在折半查找判定树中,查找不成功的过程就是走了一条从根节点到外部节点的路径,和给定值进行比较的关键字个数等于该路径上内部结点个数
。整个判定树代表的有序表在查找失败时的平均查找长度即为查找每个外结点的比较次数之和除以外结点的个数。例如,长度为10的有序表在查找失败时的平均查找长度为: ASL=(3×5+4×6)/11=39/11
总结说来:
折半查找成功的ASL与序列中的具体元素无关
,只取决于序列中元素的数目
。所以,折半查找判定树只与查找表中元素的数目有关
。
采用二分查找方法查找长度为n的线性表时,每个元素的平均查找长度为O(logn)
1.顺序查找法适合于存储结构为_______的线性表。
A. 散列存储 B. 顺序存储或链接存储
C. 压缩存储 D. 索引存储
2.使用二分查找法时,要求查找表中各元素的键值必须是_______排列的。
A.递增或递减 B.递增 C. 递减 D.无序
3.采用二分查找方法查找长度为n的线性表时,每个元素的平均查找长度为_______。
A. O(n2) B. O(nlog2n) C.O(n) D. O(log2n)
4. 有一个长度为12的有序表,按二分查找法对该表进行查找,在表内各元素等概率情况下,查找成功所需的平均比较次数为________。
A. 35/12 B. 37/12 C. 39/12 D, 43/12
5.用n个键值构造一棵二排序树,该二叉排序树的最低高度为______。
A. n/2 B. n C. [log2n] D.[log2+1]
1.B 2. A 3. D 4. B 5. D
树表查找 - 动态查找
BST(最基本),红黑树(更平衡查找效率更高的BST)
基本思想
⑴ 当二叉排序树不空时,首先将给定值 k 与根结点的关键字进行比较,若相等则查找成功;
⑵ 若给定值 k 小于根结点的关键字,则下一次与左子树的根结点的关键字进行比较,若给定值 k 大于根结点的关键字,则与右子树的根接到的关键字进行比较。
如此递归的进行下去直到某一次比较相等,查找成功。如果一直比较到树叶都不等,则查找失败。
ASL
总结说来:
二叉排序树查找成功的平均查找长度取决于二叉排序树的形状,而二叉排序树的形状既与结点数目有关,更取决于建立二叉排序树时结点的插入顺序。
哈希查找
基本思想
也叫散列查找,different from above methods,它的查找并不建立在比较基础上。哈希查找则是通过计算存储地址的方法进行查找的。
哈希查找是建立在哈希表的基础上,它是线性表的一种重要存储方式和检索方法。在哈希表中可以实现对数据元素的快速检索
若已知哈希函数及冲突处理方法,哈希表的建立步骤如下:
Step1.取出一个数据元素的关键字 key,计算其则哈希表中的存储地址 D=H(key)。若存储地址为 D 的存储空间还没有被占用,则将该数据元素存入;否则发生冲突,执行 Step2。
Step2. 根据规定的冲突处理方法,计算关键字为 key 的数据元素之下一个存储地址。若该存储地址的存储空间没有被占用,则存入;否则继续执行 Step2,直到找出一个存储空间没有被占用的存储地址为止。
解决冲突的方法
解决碰撞的方法:
1. 拉链法
:
2. 开放寻址法
$$hash_i =( hash(key) + di ) mod m$$
其中hash(key)为散列函数,m为散列表长,d_i为增量序列,i为已发生碰撞的次数。
增量序列可有下列取法:线性探测
平方探测
伪随机数探测
3. 双重散列 h(k,i) = (h1(k) + i * h2(k)) mod n
4 再散列
⑴线性探测再散列
{ D = H(key);
ND = (D+di)%m; di 取 1,2,3,……,m-1
线性探测再散列处理冲突的基本思想: 若数据元素在存储地址 D 发生冲突,则放到存储地址(D+1)%m;若又发生冲突则放到存储地址(D+2)%m;若再发生冲突则放到存储地址(D+3)%m;……;直到碰到第一个为空的存储地址(D+i)%m,则将数据元素存放在该存储空间。
⑵二次探测再散列
{ D = H(key);
ND = (D+di)%m; di 取 1*1,-1*1,2*2,-2*2,……,K*K,-K*K (K≤m/2)
值得提醒的是,对利用开放地址法查了冲突所产生的哈希表中删除一个元素,不能简单地直接删除,因为这样将截断其它具有相同哈希地址的元素的查找地址,所以应设定一个特殊的标志以表明该元素已被删除。
查找过程
散列表的查找过程和造表过程一致。
查找过程为:对于待查值,计算散列地址,若散列地址内为空标记,则查找失败;若待查值与该地址内记录关键字相等,则查找成功。 否则,求下一地址,直至散列地址为空标记,或已搜索了表中的所有单元(查找不成功),或待查值与该地址内记录关键字相等(查找成功)为止。 决定散列表平均查找长度的因素主要有散列函数、处理冲突的方法和查找表记录数
ASL
摘自
做到一道求 哈希表查找成功与查找不成功 情况下平均查找长度的计算问题,迷惑了好一会,在这里总结下来:
首先,你要明白的是平均查找长度求的是期望,那么你就按照求期望的方法来求平均查找长度吧,千万记着期望怎么求平均查找长度就怎么求啊。
题目:
在地址空间为 0~16 的散列区中,对以下关键字序列构造两个哈希表:
{Jan, Feb, Mar, Apr, May, June, July, Aug, Sep, Oct, Nov, Dec}
(1) 用线性探测开放地址法处理冲突;
(2) 用链地址法(开散列存储)处理冲突
并分别求这两个哈希表在等概率情况下查找成功和查找不成功时的平均查找长度。设哈希函数为
H(key) = i/2, 其中 i 为关键字中第一个字母在字母表中的序号, 如下:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
解决如下:
(1) 线性探测进入散列区的次序如下,X 代表冲突,要找下一个空格
Jan -> 5
Feb -> 3
Mar -> 6
Apr -> 0
May -> 6X -> 7
June -> 5X -> 6X -> 7X -> 8
July -> 5X -> 6X -> 7X -> 8X -> 9
Aug -> 0X -> 1
Sep -> 9X -> 10
Oct -> 7X -> 8X -> 9X -> 10X -> 11
Nov -> 7X -> 8X -> 9X -> 10X -> 11X -> 12
Dec -> 2
很明显,查找成功时,查找 Jan、Feb、Mar 等仅需要一次,其余的也可以由上面看出来
所以查找成功时平均查找长度 (ASL) = (1 + 1 + 1 + 1 + 2 + 4 + 5 + 2 + 2 + 5 + 6 + 1) / 12 = 31/12 = 2.58 为什么是除以 12 呢?因为查找成功的情况总共有 12 种啊
查找不成功时呢?什么是查找不成功呢?查找不成功就是从查找位置开始直到一个位置为空需要比较的次数。
首先,26/2=13,也就是说查找不成功的情况也只能出现在 0~13 之间,只有这 14 种情况。
举个例子来说,查找 Aay 吧,根据 hash 表,与 Apr 比较不匹配,接着与 Aug 比较又是不匹配,接着与 Dec 比较又是不匹配,又与 Feb 比较又是不匹配,到了 4 位置的时候为空了,即 4 上内容与 nullkey 比较,结果为空,所以查找 Aay 失败,查找长度为 5。同理也能计算其他的。
最终平均查找失败时平均查找长度为(5+4+3+2+1+9+8+7+6+5+4+3+2+1)/14=60/14。注意啊,这里是除以 14 啊。(这是求期望的方法啊)
(2) 链地址法
0 之下有 Apr, Aug
2 之下有 Dec
3 之下有 Feb
5 之下有 Jan, June, July
6 之下有 Mar, May
7 之下有 Oct, Nov
9 之下有 Sep
查找成功时候,查 Apr, Dec, Feb, Jan, Mar, Oct, Sep 各需 1 次,查 Aug, June, May, Nov 各需 2 次,查 July 需 3 次。
所以查找成功时平均查找长度 (ASL) = (1 * 7 + 2 * 4 + 3 * 1) / 12 = 18/12 = 1.5
查找失败时平均查找长度:举个例子吧,查找 Boy,2/2=1,而 1 的地方的指针为空,就不用比较就可以知道不存在,查找产度为 0。查找 Aay,与 Apr 比较不匹配,与 Aug 比较不匹配,同时,Aug 指向下一个节点的指针为空,就可以知道查找失败,查找长度为 2。
所以查找失败的平均查找长度:(2+1+1+3+2+2+1)/14=12/14。