斐波那契查找
黃金比例又稱黃金分割,是指事物各部分間一定的數學比例關係,即將整體一分爲二,較大部分與較小部分之比等於整體與較大部分之比,其比值約爲1:0.618或1.618:1。
0.618被公認爲最具有審美意義的比例數字,這個數值的作用不僅僅體現在諸如繪畫、雕塑、音樂、建築等藝術領域,而且在管理、工程設計等方面也有着不可忽視的作用。因此被稱爲黃金分割。
大家記不記得斐波那契數列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(從第三個數開始,後邊每一個數都是前兩個數的和)。然後我們會發現,隨着斐波那契數列的遞增,前後兩個數的比值會越來越接近0.618,利用這個特性,我們就可以將黃金比例運用到查找技術中。
斐波那契查找的前提是待查找的查找表必須順序存儲並且有序。
相對於折半查找,一般將待比較的key值與第mid=(low+high)/2位置的元素比較,比較結果分三種情況
1)相等,mid位置的元素即爲所求
2)> ,low=mid+1;
3) < ,high=mid-1;
斐波那契查找與折半查找很相似,他是根據斐波那契序列的特點對有序表進行分割的。他要求開始表中記錄的個數爲某個斐波那契數小1,及n=Fk-1;
開始將k值與第F(k-1)位置的記錄進行比較(及mid=low+F(k-1)-1),比較結果也分爲三種
1)相等,mid位置的元素即爲所求
2)> ,low=mid+1,k-=2;
說明:low=mid+1說明待查找的元素在[mid+1,hign]範圍內,k-=2 說明範圍[mid+1,high]內的元素個數爲n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1個,所以可以遞歸的應用斐波那契查找
3)< ,high=mid-1,k-=1;
說明:low=mid+1說明待查找的元素在[low,mid-1]範圍內,k-=1 說明範圍[low,mid-1]內的元素個數爲F(k-1)-1
個,所以可以遞歸 的應用斐波那契查找
// 斐波那契查找.cpp
#include "stdafx.h"
#include <memory>
#include <iostream>
using namespace std;
const int max_size=20;//斐波那契數組的長度
/*構造一個斐波那契數組*/
void Fibonacci(int * F)
{
F[0]=0;
F[1]=1;
for(int i=2;i<max_size;++i)
F[i]=F[i-1]+F[i-2];
}
/*定義斐波那契查找法*/
int Fibonacci_Search(int *a, int n, int key) //a爲要查找的數組,n爲要查找的數組長度,key爲要查找的關鍵字
{
int low=0;
int high=n-1;
int F[max_size];
Fibonacci(F);//構造一個斐波那契數組F
int k=0;
while(n>F[k]-1)//計算n位於斐波那契數列的位置
++k;
int * temp;//將數組a擴展到F[k]-1的長度
temp=new int [F[k]-1];
memcpy(temp,a,n*sizeof(int));
for(int i=n;i<F[k]-1;++i)
temp[i]=a[n-1];
while(low<=high)
{
int mid=low+F[k-1]-1;
if(key<temp[mid])
{
high=mid-1;
k-=1;
}
else if(key>temp[mid])
{
low=mid+1;
k-=2;
}
else
{
if(mid<n)
return mid; //若相等則說明mid即爲查找到的位置
else
return n-1; //若mid>=n則說明是擴展的數值,返回n-1
}
}
delete [] temp;
return -1;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a[] = {0,16,24,35,47,59,62,73,88,99};
int key=100;
int index=Fibonacci_Search(a,sizeof(a)/sizeof(int),key);
cout<<key<<" is located at:"<<index;
system("PAUSE");
return 0;
}
解析:
首先要明確:如果一個有序表的元素個數爲n,並且n正好是(某個斐波那契數 - 1),即n=F[k]-1時,才能用斐波那契查找法。 如果有序表的元素個n不等於(某個斐波那契數 - 1),即n≠F[k]-1,這時必須要將有序表的元素擴展到大於n的那個斐波那契數 - 1才行,這段代碼:for (int i = n; i < F[k] - 1; i++) { a[i] = a[high]; }便是這個作用。
下面回答第一個問題:看完上面所述應該知道①是爲什麼了吧。 查找n在斐波那契數列中的位置,爲什麼是F[k] - 1,而不是F[k],是因爲能否用斐波那契查找法是由F[k]-1決定的,而不是F[k]。如果暫時不理解,繼續看下面。
第 二個問題:a的長度其實很好估算,比如你定義了有10個元素的有序數組a[20],n=10,那麼n就位於8和13,即F[6]和F[7]之間,所以 k=7,此時數組a的元素個數要被擴充,爲:F[7] - 1 = 12個; 再如你定義了一個b[20],且b有12個元素,即n=12,那麼很好辦了,n = F[7]-1 = 12, 用不着擴充了; 又或者n=8或9或11,則它一定會被擴充到12; 再如你舉的例子,n=13,最後得出n位於13和21,即F[7]和F[8]之間,此時k=8,那麼F[8]-1 = 20,數組a就要有20個元素了。 所以,n = x(13<=x<=20)時,最後都要被擴充到20;類推,如果n=25呢,則數組a的元素個數肯定要被擴充到 34 - 1 = 33個(25位於21和34,即F[8]和F[9]之間,此時k=9,F[9]-1 = 33),所以,n = x(21<=x<=33)時,最後都要被擴充到33。也就是說,最後數組的元素個數一定是(某個斐波那契數 - 1),這就是一開始說的n與F[k]-1的關係。
第三個問題:對於二分查找,分割是從mid= (low+high)/2開始;而對於斐波那契查找,分割是從mid = low + F[k-1] - 1開始的; 通過上面知道了,數組a現在的元素個數爲F[k]-1個,即數組長爲F[k]-1,mid把數組分成了左右兩部分, 左邊的長度爲:F[k-1] - 1, 那麼右邊的長度就爲(數組長-左邊的長度-1), 即:(F[k]-1) - (F[k-1] - 1) = F[k] - F[k-1] - 1 = F[k-2] - 1。
斐波那契查找的核心是:
1)當key=a[mid]時,查找成功;
2)當keya[mid]時,新的查找範圍是第mid+1個到第high個,此時範圍個數爲F[k-2] - 1個,即數組右邊的長度,所以要在[F[k - 2] - 1,high]範圍內查找。