第七章的内容是查找。
查找可以分为3种。分别是线性表查找、树表查找、散列表查找。
1、线性表查找
线性表查找主要介绍了顺序查找和折半查找这两种方法。
1、 顺序查找区别于上学期学的方法,设置了哨兵,采用从后往前开始查找的方法,将时间复杂度缩短了一倍。(ASL=(n+1)/2)
2、折半查找,其实也叫二分查找,原理是每次查找不成功之后将问题的规模缩短一段,所以这样算下来自然就很快乐。
但是折半查找有局限性,就是只有当数据以顺序存储结构存储而且按关键字有序时才能使用,不然效率反而不如顺序查找。
如果使用链式存储会怎么样呢?链式存储的话在排序其实差别不大,但是在得到mid后定位到某个位置的时候时间复杂度为O(n),而顺序存储结构为 O(1)。
二分查找的关键算法:(ASL=log2n次方)
while(low<=high){// =在这里异常重要,存在一种情况,匹配到只剩下最后一个数,此时如果没有=的话那么就不会进入循环内,最后一个即使匹配也不会有返回 mid=(low+high)/2; if(ST.R[mid]==key) return mid; else if(ST.R[mid]<key) low=mid+1; else high=mid-1; //这里如果改成 high=mid的话在找到的情况下或者key大于所有元素的情况下会正常工作,否则陷入死循环 }
return 0;//找不到的情况
2、树表查找
引入树表查找的目的是解决边查找边操作的应用需求。
1、二叉排序树。
二叉排序树是比较简单的例子,采用左小右大的存储规则进行存储,然后递归进行查找。
但是二叉排序树有一个很致命的缺点是ASL与树的形态密切相关。在最好情况下(左右结点都存在),此时的ASL与二分查找逼近,
在最坏的情况下,该二叉树是一棵单支树,那么此时就是 n的数量级了。所以为了解决这个问题,就引进了平衡二叉树。
2、平衡二叉树
构建的时候以平衡因子作为标准,平衡因子的取值只能在 1 0 -1之间取,如果为2那么就认为这棵树是不平衡的。具体的插入步骤在这里就不详细说了。
3、B-树
B-树的引入其实是为了解决 内外存反复交换的问题。在这里引入了多路查找树的概念,就是一个节点存储多个数据引出多个分支,从而在数据量大的时候减少树的深度。
3、散列查找
散列查找主要由两个步骤入手。
1、构造散列函数
散列函数的构造有4种方法。
1、数字分析法
2、平方取中发
3、折叠法
4、除留余数法,这个也是构造散列函数中用的比较多的方法。H(key)=key%p,p的选择比较重要,p一般是小于表长的最大质数。如果不是质数的话那么该因子在关键字列表中
可能存在多个被除数,导致一个散列地址对应有多个关键字,产生严重冲突,不方便我们获取信息。
2、处理冲突
上文提到了构造散列函数的时候会不可避免的产生冲突,书上针对这个问题就给出了一些解决方法。
分为2大类。
1、开放地址法 Hi=(H(key)+di)%m
(1)线性探测法。di=1,2,3,4.......n,发生冲突时往后找空缺,有空缺补上,如果到表尾的话,就继续叠加从而求余后在表头重新开始查找插入。
(2)二次探测法。di=1²,-1²,2²,-2²........k²,-k² 发生冲突时也是找空缺,不过是查找长度以及方向不一样,其实也看个人是如何设置的
(3)伪随机探测法。di为随机数,方法其实与线性探测大致相同。
2、链地址法
引入链地址法是为了避免开放地址法出现二次聚集的现象以及提升查找的效率,链地址法是把具有相同散列 地址的记录放在同一个单链表