前言
编译语言:C++
编译器:VS2022
一、二分查找思想
由于二分查找所用的情况必须是有序的数列,所以我们通过找数组的中间的下标mid,通过mid和k值比较,比k值大则将right下标赋值为mid-1,比k值小则将left下标赋值为mid+1,一直循环直到k等于mid下标的数值,或则low>right退出循环
二、二分查找
#include <iostream>
using namespace std;
//二分查找
int BinarySearch(int arr[],int k,int n)
{
int left, right, mid;
left = 1;//将left赋值为排序第一个元素
right = n;//将right赋值为排序的最后一个元素
while (left <= right)//开始循环排序当left>right结束
{
mid = (left + right) / 2;//存储下标中间值
if (k < arr[mid])//如果k比下标为mid数组的数值小则说明k在mid左边
right = mid - 1;//将right赋值为mid的左边
else if (k > arr[mid])//如果k比下标mid数组的数值大则说明k在mid右边
left = mid + 1;//将left赋值为mid的右边
else
return mid;//相等则找到返回下标
}
return 0;
}
int main()
{
int p;
int arr[] = { 0,1,2,5,6,8,9,10,11,12,15 };//创建数组
int n = sizeof(arr) / sizeof(arr[0])-1;//计算数组的长度
int k = 9;
p = BinarySearch(arr, k, n);//二分查找
cout << p << " ";
return 0;
}
三、插值查找
插值查找其实就是二分查找的一种改进,二分查找是以数组一半作为mid值,那可不可以因另一种定义mid的方式来查找,这也就出来了插值查找。那它是怎么定义mid的呢,就是用k值减去最小的数除以最大的数减去最小的数乘以(right-left)+low,在数学上看来,其实也就是求一段线段在整个线段所占的比值,通过这样来定义mid值,这样也就有了一下代码
//插值查找
int InterpolationSearch(int arr[], int k, int n)
{
int left, right, mid;
left = 1;//存储数组第一个元素也就是最小值
right = n;//存储数组最后一个数,也就是最大值
while (left <= right)
{
mid = left + (right - left) * (k - arr[left]) / (arr[right] - arr[left]);
if (k < arr[mid])
right = mid - 1;
else if (k > arr[mid])
left = mid + 1;
else
return mid;
}
return 0;
}
int main()
{
int p;
int arr[] = { 0,1,2,5,6,8,9,10,11,12,15 };//创建数组
int n = sizeof(arr) / sizeof(arr[0])-1;//计算数组的长度
int k = 9;
p = InterpolationSearch(arr, k, n);//二分查找
cout << p << " ";
return 0;
}
四、斐波那契查找
前面所讲的二分查找,插值查找,都是通过二分,比例找mid的位置,这里再讲一种以黄金分割比例找mid的方法,也就是斐波那契查找,我们先写一段斐波那契数,{1,1,2,3,5,8,13,21,34,55…}其前一个比值和后一个比值接近黄金分割,所以用它来找mid位置。那该如何找到mid的位置呢?我们通过数组的总个数与斐波那契数进行比较得到斐波那契数的最小大于总个数的下标K,再通过K把数组也补充到F[k]-1个,这样可以防止越界访问,这样用F[K-1]来表示数组arr的左半部分,F[K-2]表示数组arr右半部分,也就是mid = low+F[k-1]-1,进行循环比较如果key<arr[mid],high = mid-1,这里看得出key再左半部分所以还要对左半部分分隔所以,k = k-1(也就是F[k-2])的大小对F[K-1]的部分分隔。也就是这样其他部分和这一样就不说明了
#define MAXSIZE 10
//生成斐波那契数
void Febonacci(int F[])
{
int i;
F[1] = 1;
F[2] = 1;
for (i = 3; i <= MAXSIZE; i++)
F[i] = F[i - 1] + F[i - 2];
}
//斐波那契查找
int FibonacciSearch(int arr[],int key,int n)
{
int i,low,high,mid;
low = 1;
high = n;
int k=0;//用来找数组总个数在斐波那契数最大值下标
int F[MAXSIZE + 1] = {0};//用于接受斐波那契数
Febonacci(F);//生成斐波那契数
while (n > F[k] - 1)//找数组总个数在斐波那契数最大值下标
{
k++;
}
for (i = n; i < F[k] - 1; i++)//将arr数组个数补充到F[k]-1个,防止之后越界访问
{
arr[i] = arr[n];
}
while (low <= high)//当low>high时退出循环
{
mid = low + F[k - 1] - 1;//mid将整个数组分隔成F[K-1]和F[k-2]
if (key < F[mid])
{
high = mid - 1;//将high赋值为mid左边一位数
k = k - 1;//得到F[k-1]上一位斐波那契数,准备分隔左半部分部分的数
}
else if (key > F[mid])
{
low = mid + 1;//将low赋值为mid右边一位数
k = k - 2;//得到F[K-1]上二位斐波那契数,准备分隔右半部分
}
else
{
if (mid <= n)//如果mid<=n就返回mid的值
return mid;
else
return n;
}
}
}
int main()
{
int p;//
int arr[20] = { 0,1,2,5,6,8,9,10,11,12,15 };//创建数组
int n =MAXSIZE;//计算数组的长度
int k = 9;
p = FibonacciSearch(arr, k, n);//二分查找
cout << p << " ";
return 0;
}
总结
二分查找的前提是需要有序顺序存储,对于只查找的话已经比较好了,对于经常插入和删除的数据来说,那就不建议使用。插入查找,就是一种比例查找。斐波那契查找也是通过斐波那契数相邻数进行分隔整个数组