我们如果仅仅是把书整理在书架上,要找到一本书还是比较困难的,也就是用8.2节的顺序查找表,但如果我们在整理书架时,将图书按书名的拼音排序放置,那么要找到一本书就相对容易了。简单的说,就是对图书做了有序排列,一个线性表有序时,对于查找总是很有帮助的。
8.3.1折半查找
折半查找:又叫二分查找(binary search)。它的前提是线性表中的记录必须是关键码有序(通常从小到大),线性表必须采用顺序存储。折半查找的基本思想:在有序表中,选中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有的查找区域无记录,查找失败。
#include <stdio.h> #include <stdlib.h> int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int BinarySearch(int *a, int n, int key) { int low; int high; int mid; low = 0; high = n - 1; while(low<=high) { mid = (low + high)/2; if(key<a[mid]) high = mid - 1; else if(key>a[mid]) low = mid + 1; else return mid; } return -1; } int main() { int addr; int key = 2; addr = BinarySearch(arr, 10, key); if(addr == -1) printf("查找失败\n"); else { printf("查找成功\n"); printf("所在位置为:%d\n",addr); } return 0; }
8.3.2插值查找
我们现在的新问题是,为什么一定要折半,而不是四分之一或者折更多呢。
打个比方,在英文词典里查找“apple”,你会下意识的翻到靠前的书页,而不是从字典的中间开始查找。
插值查找:根据关键字key于查找表中最大最小记录的关键字比较后的查找方法。
在折半查找中 mid = (low + high)/2 = low + 1/2(high - low);
插值查找:mid= ((key - a[low])/(a[high] - a[low])) * (high - low);
8.3.3斐波那契查找
利用黄金分割原理来实现。
斐波那契数列:0 1 1 2 3 5 8 13 21 34 ......
#include <stdio.h> #include <stdlib.h> int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int F[10] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34}; int FibonacciSearch(int *a, int n, int key) { int low; int high; int mid; int i; int k; low = 0; k = 0; high = n - 1; while(n>F[k]) k++; for(i=n; i<F[k]-1;i++) a[i] = a[n-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-1) return mid; else return n-1; } } return -1; } int main() { int addr; int key = 2; addr = FibonacciSearch(arr, 10, key); if(addr == -1) printf("查找失败\n"); else { printf("查找成功\n"); printf("所在位置为:%d\n",addr); } return 0; }
3种算法的比较
折半查找:mid= (low + high)/2 = low + 1/2(high - low);//加法,乘法
插值查找:mid=low + ((key - a[low])/(a[high] -a[low])) * (high - low); // 乘法、除法。
斐波那契查找:mid = low + F[k] - 1;//加法
斐波那契算法效率高。