- 查找
- 二分查找(折半查找)
- 插值查找
- 斐波拉契查找
- 总结:
查找是在大量的信息里面寻找一个特定的信息元素
(1)静态查找和动态查找;
静态或者动态都是针对查找表而言的。动态表指查找表中有删除和插入操作的表
(2)无序查找和有序查找。
无序查找:被查找数列有序无序均可;
有序查找:被查找数列必须为有序数列。
二分查找(折半查找)说明:元素必须是有序的,如果是无序的则要先进行排序操作
基本思想:
也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。
时间复杂度
共有n个元素,需要搜索n/2,n/4,n/8…n/2^k…
n/2^k = 1
k = log2n(以2为底,n为对数)
O(n) = O(log2n)或者O(n) = O(logn)
#includeint BinarySearch(int *arr,int val,int len) { int low,high,mid; low=0; high=len-1; while(low<=high) { mid = low + (high - low)/2; if(arr[mid] == val) return mid; else if(val < arr[mid]) high = mid-1; else if(val > arr[mid]) low = mid+1; } return -1; } int BinarySearch1(int *a,int val,int low,int high) { if(low > high) return -1; int mid = low + (high-low)/2; if(a[mid] == val) return mid; if(val < a[mid]) return BinarySearch1(a,val,low,mid-1); if(val > a[mid]) return BinarySearch1(a,val,mid+1,high); } int main(int argc, char const *argv[]) { int arr[] = {12 ,22 ,28 ,29 ,35 ,36 ,37 ,39 ,69 ,453}; int len=sizeof(arr)/sizeof(*arr); int num,i; for(i=0;i 插值查找 (1)说明:
在介绍插值查找之前,首先考虑一个新问题,为什么上述算法一定要是折半,而不是折四分之一或者折更多呢?
打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。
同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。
经过以上分析,折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的)。二分查找中查找点计算如下
mid=(low+high)/2, 即mid=low+1/2*(high-low)
通过类比,我们可以将查找的点改进为如下:
mid=low+(key-a[low])/(a[high]-a[low])*(high-low)
#include//插值查找 int InsertSearch(int *a, int val, int low, int high) { int mid = low+(val-a[low])/(a[high]-a[low])*(high-low); if(a[mid]==val) return mid; if(a[mid]>val) return InsertSearch(a, val, low, mid-1); if(a[mid] int arr[] = {12 ,22 ,28 ,29 ,35 ,36 ,37 ,39 ,69 ,453}; int len=sizeof(arr)/sizeof(*arr); int num,i; for(i=0;i 斐波拉契查找 斐波那契查找(黄金分割法查找)
斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····。在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。
斐波那契查找是在二分查找的基础上根据斐波那契数列进行分割。在斐波那契数列找一个等于大于查找表中元素个数的数F(n),将原查找表扩展为长度为F(n)(如果要补充元素,则补充重复最后一个元素,直到满足F(n)个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F(n-1)个元素,后半部分F(n-2)个元素,找出要查找的元素在那一部分并递归,直到找到。斐波那契查找的时间复杂度是O(log2n)。
斐波那契查找算法的核心在于:
1)当key=a[mid]时,查找就成功;
2)当key F[k-1]-1个;
3)当key>a[mid]时,新范围是第m+1个到第high个,此时范围个数为
F[k-2]-1个。(1)我们需要先递归实现斐波拉契数列,然后根据原数组的大小计算斐波拉契数列的k值;
(2)数组扩容条件是:增大 k 值(索引从 0 开始),使得数组长度刚好大于或者等于斐波那契数列中的 F[k]-1 ,我们定义临时数组 temp ,temp 后面为 0 的元素都按照数组最大元素值填充;
(3)mid值的确定:mid = low + f[k - 1] - 1 ,即用黄金分割点确定 mid 的值;
(4)valuevalue> temp[mid] :目标值在黄金分割点的右边。因为全部元素 = 前面的元素 + 后边元素即 f[k] = f[k-1] + f[k-2],又前面有 f[k-2]个元素,所以可以继续拆分 f[k-1] = f[k-3] + f[k-4],即下次循环有mid=low+f[k-1-2]+1,即需要k-=2;
value== temp[mid] :找到目标值,因为数组经历过扩容,后面的值其实有些是多余的,mid 可能会越界(相对于原数组来说)则有:
1)mid <= high :证明 mid 索引在原数组中,返回 mid;
2)mid > high 时,证明 mid 索引已经越界(相对于原数组来说),返回 high;
(5)若没有找到则返回-1。#include总结:#include #include #include void print_arr(int *a,int len); // 通过循环来找到第几个斐波那契数大于number static int getFibonacci_size__(int size) { int i=2,a=1,b=1,c; for(;c c=a+b; a=b; b=c; i++; } return i; } //构建fibo数组 static void fibonacci_arr__(int *fibo,int size) { fibo[0]=1; fibo[1]=1; for(int i=2;i int fiboSize = getFibonacci_size__(size);//测算斐波那契数组的大小 //构建一个斐波拉契数组 int* fibo = calloc(fiboSize, sizeof(int));//开辟一个用于存储斐波那契数列的数组空间 fibonacci_arr__(fibo,fiboSize); int low=0; int high=size-1; int i,k=0; //表示斐波那契分割数值的下标 int mid; while(high>fibo[k]-1) //获取到斐波那契分割数值的下标 ++k; //扩展数组a到fibo[k]-1的长度 int* tmp = calloc(fibo[k] - 1, sizeof(int));//创建一个与当前斐波那契数(减一是从下标0开始)长度的数组空间 memcpy(tmp,arr,size*sizeof(int)); //将不满的数值补全 for(i=size;i tmp[i] = arr[high]; } // print_arr(tmp,fibo[k]-1); while(low <= high) { mid = low+fibo[k-1]-1; if(key > tmp[mid]) { low=mid+1; k-=2; //fibo[k-2]-1 //为什么是k -=2 //说明 //1. 全部元素 = 前面的元素 + 后边元素 //2. f[k] = f[k-1] + f[k-2] //3. 因为后面我们有f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4] //4. 即在f[k-2] 的前面进行查找 k -=2 //5. 即下次循环 mid = f[k - 1 - 2] - 1 } else if(key < tmp[mid]) { high = mid-1; k--; //fibo[k-1]-1 //为甚是 k-- //说明 //1. 全部元素 = 前面的元素 + 后边元素 //2. f[k] = f[k-1] + f[k-2] //因为 前面有 f[k-1]个元素,所以可以继续拆分 f[k-1] = f[k-2] + f[k-3] //即 在 f[k-1] 的前面继续查找 k-- //即下次循环 mid = f[k-1-1]-1 } else { free(tmp); tmp=NULL; return (mid>=size)?high:mid; // if(mid <= size) // { // return mid; //若相等则说明mid找到target // } // else // { // return high; //mid >= n,说明是扩展的数值返回n-1 // } } } free(tmp); return -1; } void ShellSort(int *a,int len) { int gap,i,j,target; for(gap=len/2;gap>0;gap/=2) { for(i=gap;i target=a[i]; j=i-gap; while(j>-1 && a[j]>target) { a[j+gap]=a[j]; j-=gap; } a[j+gap]=target; } } } void swap(int *a,int *b) { int tmp=*a; *a=*b; *b=tmp; } void quick_sort1(int *nums, int l, int r) { if (l + 1 >= r) return; int first = l, last = r - 1, key = nums[first]; while (first < last) { while(first < last && nums[last] >= key) last--; printf("(%d %d)",nums[first],nums[last]); nums[first] = nums[last]; while (first < last && nums[first] <= key) first++; printf("(%d %d)",nums[last],nums[first]); nums[last] = nums[first]; } nums[first] = key; quick_sort1(nums, l, first); quick_sort1(nums, first + 1, r); } void print_arr(int *a,int len) { for(int i=0;i if(low>=high) return; int first=low; int last=high-1; int key=a[low];//基准值 while(first while(a[last] >= key && first < last) last--; while(a[first] <= key && first < last) first++; if(first!=last) swap(&a[first],&a[last]); else swap(&a[low],&a[last]); //基准值交换 } quick_sort(a,low,first); quick_sort(a,first+1,high); } void RandomArr(int **arr,int *len) { srand((unsigned)time(NULL)); *len = random()%1000; *arr = malloc(sizeof(int)*(*len)); for(int i=0;i<(*len);i++) (*arr)[i]=random()%1000; } int BinarySearch(int *a,int val,int low,int high) { int mid; while(low<=high) { mid=low+(high-low)/2; if(a[mid]==val) return mid; if(val < a[mid]) high=mid-1; if(val > a[mid]) low=mid+1; } return -1; } int main(int argc, char const *argv[]) { int *arr; int len; int num; RandomArr(&arr,&len); print_arr(arr,len); printf("n"); quick_sort(arr,0,len); print_arr(arr,len); printf("请输入查找数字:"); scanf("%d",&num); printf("index %d n",BinarySearch(arr,num,0,len)); printf("index %d n",FibonacciSearch(arr,len,num)); return 0; }
折半查找是进行加法与除法运算(mid=(low+high)/2),插值查找进行复杂的四则运算(mid=low+(high-low)*(key-a[low])/(a[high]-a[low])),而斐波那契查找只是最简单加减法运算(mid=low+F[k-1]-1)。
斐波那契查找法在核心算法上,只有赋值与减法运算,而对半查找有加法和除法,插值查找有四则运算,因为除法比加减法消耗的时间更多,在海量数据的查找过程中,这种细微的差别可能会影响最终的查找效率。



