数据结构之静态查找表

来源:互联网 发布:香港科技大学gpa 知乎 编辑:程序博客网 时间:2024/05/16 12:46


        小目录

         1.前言

         2.顺序表的查找 

         3.有序的查找(折半查找)

         4.静态树表的查找

         5.索引顺序表的查找



                  1.前言

     查找( Searching )就是根据给定的某个值 , 在查找表中确定一个其关键字等于给定值的数据的元素。根据操作的不同可以将查找分为静态查找和动态查找。对静态查找经常进行的操作有:(1)查找某个特定的数据元素中是否存在查找表中;(2)检索某个特定元素的各种属性。由于很多是程序的时间消耗在查找上,故从这篇文章开始给出相关算法的时间复杂度,也会每种类型进行比较总结。


       2.顺序表的查找

        顺序查找又称线性查找,是最基本的查找方法之一。其查找方法为:从表的一端开始,向另一端逐个按给定值key与关键码进行比较,若找到,查找成功,并给出数据元素在表中的位置;若整个表检测完,仍未找到与key 相同的关键码,则查找失败,给出失败信息。这里采用从顺序表的表尾进行往回查找的方式。

      其数据存储结构如下:

typedef int KeyType;//顺序表节点结构typedef struct {    KeyType key;    //关键词    int weight;    //权值(主要在静态树表查找中用到)}ElemType;typedef struct {    ElemType *elem;        int length;    //顺序表长度}SSTable;


       查找算法如下:

/** @description:顺序查找,找到则返回元素位置,否则返回0* @more:这里监视哨的作用是,为了避免每次循环都判断i是否小于0而且即使最后查找不成功也会返回0,程序变得简介*/int Search_Seq(SSTable S,KeyType key) {int i;S.elem[0].key = key;//监视哨for(i = S.length ;!EQ(S.elem[i].key,key); i-- );return i;}

 分析

                        

          上图是平均查找长度的计算,可以看出来算法中的基本工作就是关键码的比较,因此,查找长度的量级就是查找算法的时间复杂度,其为O(n)。顺序查找的缺点是当n 很大时,平均查找长度较大,效率低;优点是对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。




   3.有序表的查找(折半查找)

      折半查找的思想为:在有序表中,取中间元素作为比较对象,若给定值与中间元素的关键码相等,则查找成功;若给定值小于中间元素的关键码,则在中间元素的左半区继续查找;若给定值大于中间元素的关键码,则在中间元素的右半区继续查找。不断重复上述查找过程,直到查找成功,或所查找的区域无数据元素,查找失败。

  比如:

      有序序表按关键码排列如下:

7,14,18,21,23,29,31,35,38,42,46,49,52
      在表中查找关键码为14 和22 的数据元素,其查找过程如下:

                                  

折半查找算法(必须保证为有序表)如下:

/** @description:折半查找(折半查找建立在有序表上的)*/int Search_Bin(SSTable S,KeyType key) {int low,high,mid;low = 1;high = S.length;while(low <= high ) {mid = (low + high) / 2;//向前找if(LT(key,S.elem[mid].key))high = mid - 1;//向后找else if(LT(S.elem[mid].key,key))low = mid + 1;elsereturn mid;}return 0;}


分析

                      

   通过分析平均查找长度可知折半查找的时间复杂度为O(log2n)。注意log2(n)和log(n)其实是同样的复杂度,因为它们之间仅仅差了一个常量系数而已。


   4.静态树表的查找:

      上面介绍了折半查找,从折半查找过程看,以表的中点为比较对象,并以中点将表分割为两个子表,对定位到的子表继续这种操作。所以,对表中每个数据元素的查找过程,可用二叉树来描述,称这个描述查找过程的二叉树为判定树(如图)。

                                 

      上面对折半查找的分析是基于等概率的前提下进行的,那如果查找概率不相等呢,按照上面的折半查找其性能坑能不是最优的,那么应该如何进行查找呢?换句话说,描述查找过程的判定树为怎样的二叉树其性能最优。如果只是考虑查找成功的情况,则查找性能最佳的判定树是其带权内路径长度之和为最小的二叉树,即静态最优查找树。由于该树复杂难懂(我也没看过,呵呵),这里提供构造次最优查找树的方法(至于这里的公式怎么的出来的我也不懂,在论坛上问了也没人理会链接,有懂的告诉我啊,大家看着公式理解算法就好)。

                   

      实现算法如下:

/** @description:构造次优查找树,递归的思想无处不在*/void SecondOptimal(BiTree *T,ElemType R[],int sw[],int low,int high) {int i,j,dw,min;i = low;min = abs(sw[high] - sw[low]);dw = sw[high] - sw[low - 1];//选择最小的for(j = low + 1; j <= high ; j++) {if( abs(dw - sw[j] - sw[j - 1]) < min) {i = j;min = abs(dw - sw[j] - sw[j - 1]);}}//生成节点*T = (BiTree) malloc(sizeof(struct BiTNode));if(!(*T))exit(STACKOVER);(*T)->data = R[i];(*T)->order = i;//这是我附加的用来记录每个元素在顺序表中的序号和书上的略有不同if(i == low)(*T)->lchild = NULL;elseSecondOptimal(&(*T)->lchild,R,sw,low,i - 1);//构造左子树if(i == high) (*T)->rchild = NULL;elseSecondOptimal(&(*T)->rchild,R,sw,i + 1,high);//构造右子树}


实现其有序构造的算法(相关的调用函数后面附上):

/** @description:创建有序的次优查找树*/Status CreateSOSTree(BiTree *T,SSTable S) {int *sw;//存放累计权值if(S.length == 0)return ERROR;else {sw = (int *) malloc( (S.length + 1) * sizeof(int));if(!sw)exit(STACKOVER);FindSW(sw,S);SecondOptimal(T,S.elem,sw,1,S.length);}return OK;}


其查找算法如下:

/** @description:静态树表的查找* @more:注意这里T传进来之后是会改变的,即下次就不能在使用T来作为查找树了且这里无法返回在原来中的位置的,当然可以考虑加入一个记录序号的域http://write.blog.csdn.net/postedit/40217067*/int  Search_SOSTree(BiTree T,KeyType key) {while(T) {if(T->data.key == key)return T->order;else if(T->data.key > key)T = T->lchild;elseT = T->rchild;}return 0;}

分析

       次优查找树的查找方法与折半查找类似(这里不再分析),其平均查找长度与 log n 成正比。构造它的时间代价远远低于构造最优查找树,但查找性能只比最优查找树差1%~2%,很少差3%以上。其时间复杂度为O(nlogn).



        5.索引顺序表的查找

索引顺序表包括存储数据的顺序表(称为主表)和索引表两部分。顺序表被分为若干子表(又称块),整个顺序表有序或分块有序。将每个子表中的最大关键字取出,再加上指向该关键字记录所在子表第一个元素的指针,就构成一个索引项,将这些索引项按关键字增序排列,就构成了索引表。

                         

     对于索引顺序表可按分块进行查找,过程分为两个阶段:

(1) 确定待查记录所在的子表(块),由于索引表是按关键字有序排列的,对于索引表可按折半查找,若待查记录关键字的key值,<=第i个子表最大关键字,且大于第i-1个子表的最大关键字,即K(i-1) < key < K(i),则待查关键字位于第i个子表中。

(2) 在已确定的子表中确定待查记录的位置,由于子表中的记录不一定按关键字有序排列,只能按顺序查找法查找。


   分析:一般情况下,为进行分块查找,可以将长度为n的表均匀分成b块,每块还有s个元素,即b = [n/s],如果索引表采用顺序查找的方式,则查找成功时的平均查找长度为:(s+b+2)/2如果索引表采用折半查找,则成功时的平均查找长度为:log(2)(n/s+1)+s/2

以上。

最后附上完整的代码地址:GitHub


     
0 0
原创粉丝点击