查找balabala

基本概念

l  查找表:用于查找的数据集合称为查找表,由同一类型的数据元素构成,可以是数组或者链表等任何的数据类型。对查找表一般有四种操作:

1.    查询某个特定的数据元素是否在表中

2.    检索满足条件的特定元素的各种属性

3.    在查找表中插入一个数据元素

4.    在查找表中删除一个数据元素

l  静态查找表:仅涉及前两个操作的查找表,适合静态查找表的查找方法有:顺序查找、折半查找和散列查找

l  动态查找表:包括添加删除的操作,适合动态查找表的方法有:二叉排序树的查找、散列查找等。

l  平均查找长度:衡量查找算法的主要指标。一次查找长度指的是需要比较关键字的次数。平均查找长度就是所有查找过程中进行关键字的比较次数的平均值。ASL =  其中 为查找第i个元素的概览,一般认为是等概览的, 是找到第i个元素需要比较的次数。

顺序查找

l  基本是没有要求的,可以对线性表以及线性链表进行查找。并且也可以是无序的

l  思想:就是按照顺序一个一个依次查找,没有什么特殊的。书上设定了一个哨兵,所以查找失败的时候会比较n+1次,如果没有设置哨兵,那么应该就是比较n次即可。成功的时候,当查找第i个元素的是,且从后向前找的顺序,那么需要比较n-i+1次,假设每次找到的概率是不变的,那么查找成功的ASL=(n+1)/ 2;时间复杂度为O(n)

//顺序查找代码

intSequential_Search(int[] num, int key){

    for(int i = 0; i < num.length; i++){

        if(num[i] == key){

            return i;

        }

    }

    return 0;

}

折半查找

l  要求可以是随机存取的结构,所以只能在线性表中进行,不适合链式存储的形式。并且要求必须是有序的。

l  思想:每一次都和带查找所有元素的中间位置元素进行对比,如果key大于mid,则转去mid后面所有元素中进行重复工作,反之去mid前面的所有元素进行重复工作。反复执行直到不满足low<=high,跳出循环。这个过程可以对应为一个判定树上,一般而言,有序表中有n个元素,那么判定树上会有n个圆形节点,n+1个矩形结点(对应查找失败)。所以查找失败的查找长度为树高h= +1,查找成功时的平均查找长度则为每一层树高乘以该层的节点个数,最后除以元素个数n。ASL =  – 1.因为对于n个元素而言,树的高度为h =  + 1所以时间复杂度为O()

//折半查找代码

intBinary_Search(int[] num, int key){

    int low = 0;

    int high = num.length;

    while(low <= high){

        int mid = (low + high)/2;

        if(key > num[mid]){

            low = mid + 1;

        }else if(key < num[mid]){

            high = mid - 1;

        }else{

            return mid;

        }

    }

    return 0;

}

分块查找

思想:分为很多块,块内部可以没有顺序,但是块之间是有顺序的。并且会建立一个索引表,表中存储每一块的最大值和各块第一个元素的地址。所以索引表是一个有序的线性表。块内常常用顺序查找,块之间可以用折半或者顺序。

该方法可以比较方便的动态改变线性表中的内容。既有动态结构,又适用于快速查找。

B树

l  定义:B树又称为多路平衡查找树,B树中所有结点的孩子结点数的最大值称为B树的阶,通常用m表示,一棵m阶B树或为空树,或为满足一下特性的m叉树

1.    树中每个结点最多有m棵子树,所以最多含有m-1个关键字

2.    树中每个结点最少有 棵子树,所以最少含有 -1个关键字,除根节点以外。

3.    如果根结点不是终端结点,那么至少含有两个子树

4.    所有叶结点出现在同一层,并且不带信息。实际上这些结点都是不存在的,指向这些结点的指针为空。

B树是所有结点的平衡因子为0的多路查找树。

l  对于有n个关键字的B树,有n+1个叶结点,也就是查找不成功的结点。

B树的高度(磁盘存取次数):B树的高度不包括最后一行不带任何信息的结点。对于有n个关键字,m阶的B树来说,每一个结点最多有m个子树,结点上有m-1个关键字。所以n≤(m-1)*(1+m+ +…+ ) = - 1.所以h≥ 。每个结点最少有 棵子树,也就是有 -1个关键字,第一层有一个结点,第二层至少有两个结点,因为除了根节点之外有关键字的最少要求,所以第三层至少有2 个结点…第h+1层有2 个结点,但是这一层不算B树的高度,所以n+1≥2 ,于是h≤ +1,因此对于有n个关键字的B树来说,树的高度h满足: ≤h≤+1

l  B树的查找:过程与二叉查找树的过程很相似,先在B树中找结点,然后再结点中继续寻找是否存在。

B树的插入:二叉查找树的插入过程很简单,最终插入的结点一定是叶子节点。但是在B树中插入比较复杂,因为每个结点的关键字个数是有规定的。B树最终插入的地方一定是最低端的非叶节点。如果插入之后,该结点的关键字个数不超过m-1,那么不用进行其他操作,如果插入该关键字之后,超过m-1,那么就需要对该结点分裂。分裂过程如下,将插入之后的结点从中间位置将关键字分为两部分,左边部分继续留在原结点中,右边部分放到新结点中,中间的那个关键字,上升为父结点中,如果父结点的关键字个数也超了,那就重复此过程,到根节点也超的话,那么B树的高度增加1

l      B树的删除:删除和上面的插入一样,每次删除之后要判断关键字个数是否少于 -1,以决定是否需要合并。因为插入的地方一定是最底层的非叶节点,但是删除分两种情况。一种是不在终端结点;另一种是在终端结点。对于第一种情况,假如要删除第i个关键字,那就去找其i指针指向的终端结点中的关键字,至于找哪个子树的终端结点,是根据子树关键字的个数判断的。那边关键字个数大于 -1,那就去对应子树上找。然后用这个来替代需要删除的关键字,然后删除终端结点对应位置即可,其实还是归结为了在终端结点上删除的情况。如果俩子树的关键字个数都等于 -1,那就直接结合俩子树,当删除的关键字在终端结点的时候,如果删除之后的关键字个数大于 -1,那么就正常删除,但是如果删除之后发现小于 -1,就需要进行合并操作。合并过程如下:①如果其兄弟结点的关键字个数大于 -1,那么满足条件中兄弟结点中离自己最近的关键字上升到父结点中,然后用父结点对应的关键字替代删除之后的位置;②如果左右兄弟都不够借,那么就将关键字删除后,和左或者右兄弟结点及双亲节点中的关键字进行合并。因为在合并的过程中,双亲结点中的关键字个数也会减少,如果双亲结点是根节点,并且关键字个数减少为0,就直接将根节点删除,用新合并的结点当做根节点。如果不是根节点,那就继续讲双亲结点继续和它自己的兄弟结点合并,直到B树满足条件为止。

l  http://blog.163.com/zhoumhan_0351/blog/static/39954227200910231032917/

B+树

l  B+树是应数据库所需出现的B树的变形树

l  一棵m阶的B+树满足以下条件:

1.    每个分支结点最多具有m个子树

2.    非叶根节点至少有两棵子树,分支结点至少有 个子树

3.    结点的子树个数和关键个数相同

4.    所有叶节点包括所有的关键字以及指向相应记录的指针。并且叶节点中将关键字按大小顺序排列,并且相邻叶节点按大小顺序相互链接起来。

5.    每个分支结点(可以看成是索引的索引)仅包含它各个子结点(也就是下一级的索引块)的中关键字的最大值和指向其子结点的指针

l  B+树中分支节点的关键字是其子树中关键字的最大值的副本。在B+树中一般有两个头指针,一个是指向根节点的,另一个指向关键字最小的叶结点

l  B+树支持两种查找方式,从关键字开始的顺序查找和从根节点开始的多路查找

l  B+树不论查找是否成功,路径都是从根结点到叶结点的路径。

B树和B+树的差异

l  B+树中,具有n个关键字的结点含有n个子树,即每个关键字对应一个子树;但是在B树中,n个关键字对应n+1个子树.

l      B+树中,每个结点关键字个数为 ≤n≤m;B树中 -1≤n≤m-1,其实两种树的最大子树个数和最小子树个数都是定的,不同的只是B树中关键字比子树少一个,B+树中关键字个数等于子树个数而已。

l  B+树中,叶节点是有信息的,包含了所有的关键字所有非叶节点只是起到了索引的作用,有点像分块查找中的索引表。

散列表(Hash)

常用的散列函数:

1.    直接定址法:H(key) = a*key + b这种方法计算简单,并且不会发生冲突。适合关键字分布基本连续的情况,如果分布不连续,空位会较多,造成空间的浪费

2.    除留余数法:最常用,最简单。假定散列表表长为m,选取一个不大于m但最接近或等于m的质数p。H(key) = key%p,所以散列函数的关键是质数p的选择

3.    数字分析法:关键字为r进制,选取数码分布较为均匀的若干位作为散列地址。这种方法适合于已知的关键字集合,如果更换了关键字,就要重新构造新的散列函数

4.    平方取中法:取关键字的平方值得中间几位作为散列地址。适合关键字的每一位取值都不够均匀或都小于散列地址所需的位数

5.    折叠法:将关键字分割为位数相同的几部分(最后一部分可以短一点),然后取这几部分的叠加和作为散列地址。适合关键字位数很多,并且关键字中的每一位上数字分布大致均匀。

处理冲突的方法

1.      开放定址法:可存放新表项的空闲地址既向它的同义词项开放,也向它的非同义词项开放。 表示发生冲突后的第i次探测的散列地址,  = (H(key)+ ) % m。其中m为散列表的表长, 为增量序列。注,开放地址法不能随便物理删除表中已有的元素,应该做个删除标记,然后定期维护散列表的时候,把具有标记的元素物理删除。增量序列的选取方法如下:

        线性探测法: =1,2,…,m-1,冲突发生的时候,就顺序查找表中下一个单元,直到找到一个空闲的表项。这种可能产生聚集的现象,大大降低查找效率

        平方探测法: = ,- , ,- ,…, ,- ,其中k≤m/2,m必须是一个可表示为4k+3的质数,可以避免堆积,但是不能探测到表中所有位置,只能有一半单元

  再散列法:双散列法,就是有两个散列函数。最多经过m-1次就会遍历所有位置

        伪随机序列法: =伪随机数序列

2.    拉链法:让同义词挂到一个链表上。适合经常进行插入和删除的操作

散列表的查找效率取决于:散列函数、处理冲突方法和装填因子

平均查找长度取决于装填因子α,不依赖于n和m。α定义为散列表中以后的元素除以表长。

字符串模式匹配(KMP)

l  定义:求第一个字符串(模式串)在第二个字符串(主串)中的位置

l  KMP算法:在线性的时间复杂度上实现字符串的匹配,O(m+n),其中m和n分别是模式串和主串的长度。中心思想是,当发现不匹配的时候,主串的下标不动,但是让模式串向前移动一定的位置,移动的个数取决于next数组中对应的数值,所以重点是next数组的建立。

l  next数组建立:首先初始化next[1] = 0,next[2] = 1,然后对应主串中的j,令k =next[j-1];然后去比较S[k]和S[j-1],如果两者相等,那么令next[j] = k + 1,如果不相等,就让k = next[k],如果k≠0,则继续去比较S[k]和S[j-1];如果k=0,那么令next[j]=1;

l  KMP算法匹配过程:当遇到两者不相等的时候,主串的下标i不变,令j=next[j],然后继续比较。如果j = 0,那么就让i和j同时加一进行比较。这里注意的是,i和j都是从下标为1开始进行比较的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值