有序表查找要求咱们的数据是有序的,是排序好的,咱们只须要进行查找便可
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。可是,折半查找要求线性表必须采用顺序存储结构,并且表中元素按关键字有序排列。
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,若是二者相等,则查找成功;
不然利用中间位置记录将表分红前、后两个子表,若是中间位置记录的关键字大于查找关键字,则进一步查找前一子表,不然进一步查找后一子表。
重复以上过程,直到找到知足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
int Binary_Search(int *a, int n, int key) { int low, high, mid; low = 0; high = n - 1; while (low<=high) { mid = (low + high) / 2; if (a[mid] < key) low = mid + 1; else if (a[mid]>key) high = mid - 1; else return mid; } return -1; } int main() { int a[10] = { 1, 6, 12, 21, 30, 31, 32, 42, 49, 52 }; int index; index=Binary_Search(a, 10, 49); if (index != -1) printf("find key in %d\n", index); else printf("not find key\n"); system("pause"); return 0; }
其时间复杂度是O(logn),不过因为折半查找的前提是须要有序表顺序存储,对于静态查找表,一次排序后再也不变化,这样的算法是比较好的。可是对于须要频繁插入或删除操做的数据集来讲,维护有序的排序会带来不小的工做量,不建议使用。
对于前面的折半查找,为啥必定要折通常,而不是1/4或者其余? 好比:咱们查字典Apple,咱们会先从中间查找,仍是有必定目的的向前找。
或者在0-1000个数之间有200个元素从小到大均匀分布排序,咱们如果须要查找到15,咱们会去中间查找500,仍是直接去前面开始查找。 以上都说明咱们上面的折半查找能够再进行改进
折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的),其前面的查找系数(比例参数)始终是1/2
也就是将上述的比例参数1/2改进为自适应的,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减小了比较次数。
基于二分查找算法,将查找点的选择改进为自适应选择,能够提升查找效率。固然,插值查找也属于有序查找。
int Insert_Search(int *a, int n, int key) { int low, high, mid; low = 0; high = n - 1; while (low <= high) { mid = low + (key - a[low]) / (a[high] - a[low])*(high - low); if (a[mid] < key) low = mid + 1; else if (a[mid]>key) high = mid - 1; else return mid; } return -1; }
实现方法和折半同样,只是修改了mid
而插值查找则比较灵活,并非简单的从中间进行的,它是根据咱们须要查询的值的渐进进行搜索的. 插值查找的不一样点在于每一次并非从中间切分,而是根据离所求值的距离进行搜索的.
对于表长较大,而关键字分布又比较均匀的查找表来讲,插值查找算法的平均性能比折半查找要好的多。反之,数组中若是分布很是不均匀,那么插值查找未必是很合适的选择。
时间复杂度:平均状况O(loglog(n)),最坏O(log(n))
除了插值查找以外,咱们再介绍一种有序查找,能够对折半进行优化,那就是斐波那契查找,利用了黄金分割原理来实现的
斐波那契查找与折半查找很类似,他是根据斐波那契序列的特色对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,即n=F(k)-1;
越向后,每两个数之间相除越接近黄金比例
例如上面的查找数组大小为n=10,F[6]<n<F[7],因此得出其位置为k=7
推文:斐波那契查找原理详解与实现html
是为了格式上的统一,以方便递归或者循环程序的编写。
表中的数据是F(k)-1个,使用mid值进行分割又用掉一个,那么剩下F(k)-2个。
正好分给两个子序列,每一个子序列的个数分别是F(k-1)-1与F(k-2)-1个,格式上与以前是统一的。
否则的话,每一个子序列的元素个数有多是F(k-1),F(k-1)-1,F(k-2),F(k-2)-1个,写程序会很是麻烦。
void Fibonacci(int **Fb, int n) { int f1, f2,ft; int count=3; f1 = 1; f2 = 1; while (f2<n) { ft = f1 + f2; f1 = f2; f2 = ft; count++; } (*Fb) = (int *)malloc(count*sizeof(int)); (*Fb)[0] = 0; (*Fb)[1] = 1; for (f1 = 2; f1 <= count - 1; f1++) (*Fb)[f1] = (*Fb)[f1 - 1] + (*Fb)[f1 - 2]; }
int Fibonacci_Search(int *a, int n, int key) { int low, high, mid, i, j,k; int *Fb,*temp; //根据实际状况来初始化 low = 1; high = n; Fibonacci(&Fb, n); k = 0; while (n > Fb[k]) //计算n位于斐波那契数列位置 k++; temp = (int *)malloc((Fb[k] - 1 + 1)*sizeof(int));//后面加1是由于还有个下标0空间 memcpy(temp, a, n*sizeof(n + 1)); for (i = n + 1; i <= Fb[k] - 1; i++) //咱们要查找的是数组1到F[k]-1,而实际数组长要包括一个下标0空间 temp[i] = a[n]; while (low<=high) { mid = low + Fb[k - 1] - 1; if (key == temp[mid]) { //分状况,是前半段,正常输出,后半段判断是否是在咱们补充的数组里面,这时咱们返回原数组最后一个下标 if (mid <= n) return mid; else return n; } else if (key>temp[mid]) { low = mid + 1; k = k - 2; } else { high = mid - 1; k = k - 1; } } return 0; //0是未找到,0下标使无心义的 }
int main() { int a[11] = { 0,1,16,24,35,47,59,62,73,88,99}; int index; index = Fibonacci_Search(a, 10, 73); if (index != -1) printf("find key in %d\n", index); else printf("not find key\n"); system("pause"); return 0; }
其时间复杂度也是O(logn),就平均性能来讲,斐波那契查找因为折半查找。可是如果最坏状况,好比key=1,始终处于左侧长半区查找,则效率低于折半查找。