/* 8.4.1 折半查找 折半查找又称二分查找,它的前提是线性表中的记录必须是关键码有序(通常从小到大有序). 线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象 ,若给定值与中间记录关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间 记录的左半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。 假设我们有这么一个有序数列{0,1,16,24,35,47,59,62,73,88,99},除0下标外共有10个数字。 对它进行查找是否存在62这个数。我们来看折半查找的算法是如何工作的。 */ //折半查找 int Binary_Search(int *a, int n, int key) { int low, high, mid; //定义最低下标为记录首位 low = 1; //定义最高下标为记录末位 high = n; while( low <= high) { //折半 mid = (low + high) / 2; //若查找值比中值小 if (key < a[mid]) //最高下标调整到中位下标小一位 high = mid - 1; //减1和下面的加1 是比较巧妙的 else if(key > a[mid]) low = mid + 1; else return a[mid]; } return 0; } /* 8.4.2 插值查找 折半查找的算法为: mid=(low+high)/2; 插值查找 将其换成: mid=low + (high-low) * (key-a[low])/(a[high]-a[low]); Latax 插入公式 公式1 公式2 应用场景:比如要在取值范围0~10000之间100个元素从小到大均匀分布的数组中查找5,我们自然会 想到从数组下标较小的开始查找。看来我们的折半查找还是有改进空间的。 折半查找,顾名思义,存在一个参数1/2(折半),也就是mid等于最低下标low加上最高下标low的差的一半。 算法科学家们考虑的就是将这个1/2进行改进,改进为下面的计算方案: 将1/2改成了(key-a[low])/(a[high]-a[low])有什么道理呢? 道理很简单 大大的提高了查找效率。 */ /* 8.4.3 斐波那契查找 前面的折半查找,是从中间分,也就是说,每一次查找总是一分为二,无论数据偏大还是偏小,很多时候 未必就是最合理的做法。除了差值查找,我们再介绍一种有序查找,斐波那契查找(Fibonacci Search), 它是利用了黄金分割原理来实现的。 */ //斐波那契查找 //a为查找的顺序数列;n为顺序数列a的长度;key为要查找的值 int Fibonacci_Search(int *a, int n, int key) { int low, high, mid, i, k; //定义最低下标为记录首位 low = 1; //定义最高下标为记录末位 high = n; k = 0; //计算n位于斐波那契数列的位置 while(n > F[k] - 1) //这里的F[k] 就是之前栈中的哪个递归函数 k++; //将不满的数值补全 for (i = n; i < F[k] - 1; i++) a[i] = a[n]; while(low <= high) { //计算当前分割的下标 mid = low + F[k-1] - 1; //若查找记录小于当前分隔记录 if (key < a[mid]) { //最高下标调整到分割下标mid-1处 high = mid - 1; //斐波那契数列下标减一位 k = k - 1; } //若查找记录大于当前分割记录 else if (key > a[mid]) { //最低下标调整到分割下标mid+1处 low = mid + 1; //斐波那契数列下标减两位 k = k - 2; } else { if (mid <= n) //若相等则说明mid即为查找到的位置 return mid; else //若mid>n说明是补全数值,返回n return n; } } return 0; } /*有点复杂,建议直接看书上代码的运行分析 相比于 插值查找的 mid=low + (high-low) * (key-a[low])/(a[high]-a[low]); 换为 mid = low + F[k-1] - 1; */