静态查找

来源:互联网 发布:软件总体技术方案 编辑:程序博客网 时间:2024/04/20 04:41

学完了3种数据结构(线性、树、图)之后,我们下面简要的介绍两大类算法:查找和排序。
为什么介绍这两种算法呢?主要是因为这两种算法的应用实在是太广了,所以这里就单独拿出来讨论一下。
查找算法可以分为两大类:静态查找和动态查找。静态查找,就是在查找的过程中不进行插入,删除等操作;反之就是动态查找。这也是与我们生活中的情况相对应的:如何一个学生去图书馆借书,他先得查图书馆里面有没有这本书,他做的事情就是静态查找;而对于图书管理员来说,别人借书时,需要在书库中找到这本书,并把它从书库中删去,当学生还书时,需要把这本书按一定的顺序添加到书库中,这就是动态查找。
这一小节介绍静态查找。静态查找中我们介绍3种基本的查找算法:顺序查找,折半查找和索引查找。
我们之前就接触过顺序查找。比如线性表的locateElem函数。它的基本思路就是把表中的每个元素都拿过来,跟我们的查找的关键字key相比较。

//顺序查找int seqSearch(int* arr,int size,int key){int cnt = 0;while(cnt != size){if(arr[cnt] == key)return cnt;++cnt;}return -1;}


我们可以分析一下这个算法的复杂度:每次循环需要做两次比较:cnt != size和arr[cnt] == key,当key为n时,需要进行2*n次比较。假设key的取值是等概的,均为1/n,所以这个算法的复杂度为:f(n) = (1/n)*2*(1+2+...n) = n+1。
有没有办法降低这个问题的复杂度呢?一种简单的办法就是把key的值放在整个集合的最后,当做“哨兵”,然后遍历整个新的数组,直到遇见哨兵为止。如果停止时的下标小于数组的大小,则说明在遇见哨兵前就找到了key值,否则就是之前一直没有遇见key,直到最后一个哨兵时才遇见。

int seqSerach_improve(int* arr,int size,int key){int* newarr = (int*)malloc(sizeof(int) * (size +1 ));for(int i = 0; i < size;++i)newarr[i] = arr[i];newarr[size] = key;int cnt = 0;while(true){if(newarr[cnt] == key)break;++cnt;}if(cnt < size)return cnt;elsereturn -1;}


这个改进看似平平,但是由于它减少了一次比较操作,所以整个复杂度也降低为原来的一半。
下来看折半查找。这种查找需要一个前提,就是待查找的集合是已经排好序的。所以每次查找时,我们可以将key与集合中间的元素相比,如果找到了,就返回该元素的下标;如果key比中间的元素小,那么在集合的前一部分继续进行折半查找;如果key比中间的元素大,那么在整个集合的后一部分进行折半查找。程序有两种写法,循环或者递归都可以。

//折半查找int BinarySearch(int* arr,int size,int key){int low = 0;int high = size-1;while(low <= high){int mid = (low + high)/2;if(arr[mid] == key)return mid;if(arr[mid] > key){high = mid-1;}if(arr[mid] < key){low = mid+1;}}return -1;}//递归实现的折半查找int index = -1;void BinarySearch_recursive(int* arr,int size,int key,int low,int high){if(low > high) return ;int mid = (low + high)/2;if(arr[mid] == key) index = mid;if (arr[mid] > key)BinarySearch_recursive(arr,size,key,low,mid-1);if(arr[mid] < key)BinarySearch_recursive(arr,size,key,mid+1,high);}

这个算法的复杂度为log(n)。至于怎么算的,推导比较长,这里就不列出来了。

最后我们看看索引查找。
在有些时候,由于待查找的集合是在是过于庞大,所以不能直接装入内存,只能放在硬盘中。可是读写硬盘的速度要远远低于内存的速度,于是人们想出了一个简单的办法,先建立一张索引表,把索引表放在内存中。这个表把整个的待查找集合分成的几个部分,记录了每个部分的开始位置,这一部分元素的个数,其中的最大元素。当我们需要查找时,将key值与索引表中每一部分的最大值相比,如果小于最大值,则说明key值在这一部分中,再把则一部分的数据装入内存,然后顺序查找。下面的程序模拟了这种基本思想:

typedef struct indexTable{int startIndex;int maxKey;int size;}indexTable;int findKeyInSubtable(indexTable subtable,int key,int* arr);int searchtable(indexTable* tableArr,int tableArrSize,int* arr,int key){for(int i = 0; i < tableArrSize;++i){if(key<= tableArr[i].maxKey){return findKeyInSubtable(tableArr[i],key,arr);}}return -1;}int findKeyInSubtable(indexTable subtable,int key,int* arr){for(int i = subtable.startIndex;;++i)if(key == arr[i])return i;}


在主函数中,需要先建立索引表,才能调用查找函数:

int main(){int arr[MAX] = {22,12,13,8,9,20,33,42,44,38,24,48,60,58,74,49,86,53};indexTable table[3];table[0].maxKey = 22;table[0].startIndex = 0;table[0].size = 6;table[1].maxKey = 48;table[1].startIndex = 6;table[1].size = 12;table[2].maxKey = 86;table[2].startIndex = 12;table[2].size = 6;int index1 = searchtable(table,3,arr,86);printf("%d",index1);return 0;}


 

值得注意的是,虽然3个索引表内部的元素是无序的,但是这3个中第一个表的元素是小于第二个的,第二个索引表中的元素是小于第三个的。
原创粉丝点击