查找:
查找表:由同一类型的数据元素(或数据)构成的集合。
关键字是数据元素中某项数据项的值,又称为键值。
如果此关键字可以唯一的表示一个记录,则称此关键字为主关键字(类此数据库中primary key主键)
对于那些可以识别多个数据元素(或记录)的关键字,我们称为次关键字。
查找:根据给定的某个值,在查找表中确定一个其关键字等于给定的数据元素(或记录)。如果表中不存在关键字等于给定的记录,则称为查找不成功,此时查找的结果可以给出一个空记录或空指针。
1.查找表按照操作方式可以分为:静态查找表和动态查找表。
静态查找表:只做查找操作的查找表。它的主要操作时:
(1) 查询某个“特定的”数据元素是否在查找表中
(2) 检索某个特定的数据元素和各种属性。
动态查找表:在查找的过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。
(1) 查找时插入数据元素
(2) 查找时删除数据元素
面向查找的数据结构称为查找结构。
顺序查找:sequential search 又叫线性查找,是基本的查找技术,它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,如果某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果知道最后一个(或第一个),其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功。
顺序表查找算法:
Int sequential_search(int *a,int n,int key){
IntI;
For(i=1;i<=n;i++){
IF(a[i]==key)
ReturnI;
}
Return 0;
}
如何对于上面程序进行优化处理,这里有两个地方值得注意:(1)每次都要比较i和n的关系(2)每次都需要比较a[i]和key的关系;是否能够将这两个比较进行简化一下那,答案是可以的。
Int sequential_search(int *a,int n,int key){
IntI;
a[0]= key;
i=n;
while(a[i]!=key)i—
returni;
}
这里给设置一个哨兵,a[0],如果中间有a[i]=key的话,返回当前i值,如果没有找到与key相等的项,则返回0的位置。这里讲循环都两次比较缩减为一次。但是当数据比较多时,效率应该是提高了很多。
顺序查找的时间复杂度,如果在第一个位置就查找到了,o(1);如果是在最后一个位置查找到则是o(n);如果没有查找到也是需要n+1次比较的,如果关键字被查找到的在每一位的概率是相同的话,那么平均查找概率当时o((n+1)/2).所以最终的时间复杂度还是o(n)
顺序查找的缺点是如果n很大的话,查找的效率是极低的。优点是算法简单,对于静态查询表没有任何要求,在一些小型整数数据查找时,是适用的。同时根据概率问题,可以再构造静态表时将容易查找到的数据放到查找的前面。
2.有序表查找
对于已经有序的结构,查找的效率是很高的。如猜商品的价格,给出一个区间,我们给出一个价格,别人回答值高还是低,这样很快就能找到。
这里同样也是通过折半查找,二分查找。其前提条件就是线性表中的记录是有序的(关键码),线性表必须用顺序存储。其基本思想是:在有序表中,取中间记录作为比较对象,如果给定值与中间记录的关键字相等,则成功;如果给定的值小于中间记录的关键字,则在中间记录和左半区继续查找;如果给定的值大于中间记录的关键字,则在中间记录的右半区继续查找。重复上面过程,直到查找成功,或所查找的区域无记录,查找失败。
Int binary_search(int *a;int n,int key){
Intlow,high,mid;
Low= 1;
High= n;
While(low<=high){//low<=high
Mid= (low+high)/2;
If(a[mid]<key){
High = mid-1;
}else if (a[mid]>key)
{
Low = mid+1;
}else
Returnmid;
}
Return 0;
}
它的时间复杂度是o(logn),(利用具有n个结点的完全二叉树的深度是logn向下取整后+1)
如果是针对于静态查找表,这样的效果是足够好了,但是如果是数据频繁删除插入的情况的动态查找表的话,则将会是维护表的顺序有一定的工作量,不建议使用它。
3.插值查找
根据查找数据的大小,我们可以对于折半的这个地方进行优化,如数据很大,可以直接从后面;如果数据很小,可以从数据开始处理。这里是我们知道数据表的数值范围,排序顺序,同时知道查找的关键字的大概区域。有个动态调整自适应的变化,速度可以调节的快一些,或者调节的慢一些。
这里mid = (low+high)/2 = low +(high-low)/2,这一对于这里的1/2进行变化
如mid = low + ((key-a[low])/(a[high]-a[low]))*(high-low)
这里纯粹从公式上分析,如果key离a[low]很近的话,后面代替1/2的那部分的值就会很小;相反如果是key离a[high]很近,则这个值会很接近与1。也就是比原来变化的速度更快,能更快的接近于目标值。
这样就得到了另外一种有序表查找算法,插值查找法。插值查找法是根据要查找的关键字key与查找表中最大最小记录的关键字比较后查找方法,其核心在插值公式:
(Key-a[low])/(a[high]-a[low]).此外对于其他的插值公式,如果想要参考的话可以参见《数值分析》中讲解的各种插值形式。
这里从时间复杂度上仍然可以认为是o(logn)。但是如果表长比较长,且分布比较均匀,插值查找算法的平均性能要比二分法查找要好很多。
4.斐波那契查找
思想仍然是从二分法基础上而来,二分法是一分为2,插值查找是根据区间段的插值,而斐波那契查找则是利用黄金分割点,该数列的特性。
斐波那契数列:0 1 1 2 3 5 8 13 21 34 55 …
这里下标的改变使用该数列,其思想是:
(1) 当key=a[mid]时,查找成功;
(2) 当key<a[mid]时,新范围是第low个到第mid-1个,此时范围的个数为F[K-1]-1个;
(3) 当key>a[mid]时,新范围则是第mid+1个到第high个,此时范围个数为F[K-2]-1个;
注意这里开始补齐的a[n]到F[K]-1范围的数据,是防止比较过程中超出下标的比较,如果不补齐的话,将会出现数组越界比较的异常。F[K]为斐波那契数列
Int Fibonacci_search(int *a,int n,int key)
{
Intlow,high,mid,I,k;
Low= 1;
High= n;
K= 0;
While(n>F(k)-1)k++;//计算n在斐波那契数列中的位置
For(i=n;i<F[K]-1;i++)a[i]=a[n];//用a[n]补齐n到F[K]-1位置的数据
While(low<=high){
Mid= low +F[K-1]+1;//
If(key<a[mid])
{
High= mid -1;
K= k-1;
}else if(key>a[mid])
{
Low = mid+1;
K = k -2;
}else{
If(mid<n)return mid;//mid为查找的下标
Else return n;//说明查找的位置是补全的数值,返回n
}
}
Return 0;
}
这里不熟悉斐波那契的可以研究一下。
其时间复杂度为o(logn).但是平均性能要比二分法要好。如果key=1,在左侧的话,效果就要低于二分法。
同时这里可以注意到二分法的(low+high)/2,插值法则是mid = low+((key-a[low])/(a[high]-a[low]))*(high-low),而斐波那契数列方法则是,mid=low+F[K-1]-1,在海量数据处理过程中,加减法要比乘除的效率要高一些。
从这算法,从顺序表查找,到二分法查找,再到插值查找,斐波那契查找;算法的发展方向,永远是算法的缺点和对于更好效率的追求。从简单原始的起点开始,在算法的性能上有着明显的可以提高的可能性,利用已有的数据结构和已有的知识点,将算法中某一块可以提高或者改变的地方,通过变形,或者采用转换,达到提高算法效率的可能。
对于已有的数据如果在输入时就具有了一定的特征,这一点是可以利用的,也就是说特殊的算法对于输入的数据有特定的要求,而且该特定格式的数据对于特定的算法是有一定耦合的,数据形式不一致,不同算法效率不同。即使是较好的算法,如果其需要的数据没有被特殊处理,其效果也并不是如期望的那样的。
再者说想排序算法:冒泡排序、快速选择、简单插入、希尔排序、堆排序、归并排序、快速排序等,思想是逐渐向数据结构靠拢、逐渐将大的问题分割成小的问题,分割组装的代价要比大问题的要小的多。
由整个数据前后遍历、到分区间遍历、或者是二分归并,我们可以看到算法优化的一条轨迹。可以当做设计的参考,如果能够给予我们一些启迪,或者能够有所顿悟,也是一种学习的反馈。学习在于悟。
同时对于输入的数据,我们也可以根据数据本身特点,或者是问题的简单的数据,我们顺序目标进行一些简单的操作,有时也能够从其中获得一些对于算法帮助的想法。
写到这里,想加上一句,不知道有关还是没有关系啊。索性加载最后:《易经》有三易,一是简易,二是变易,三是不易。简易:说明易经是简单的,我们可以从简单的入门,如果把数据结构和基本的算法粗度一遍,体会设计的奥妙,相信会有一些体会的。变易:任何事务都是变化的,我们只要搞清楚其变化的规律,那就了解了事务的本质,同时我们也要变,改变墨守陈规的状态。不易:正所谓一切道理的终极道理都是不变的;(也可以说不变的是变啊),把握住了这个之后,触类旁通啊。
最后最后再说一句话,孔子说研究《易经》,我们要不断把玩,要洁净精微。希望自己也能不断向这方面靠拢啊。