二分法已经非常快速了,但是还是可以改进二分法查找:
不要使用中间元素, 而是使用更为精确的区间,找到关键字在上面区间内,使其收敛更快。
使用Fibonacci序列数字来确定这个区间,就是Fibonacci Search。
二分法请参考:
http://blog.youkuaiyun.com/kenden23/article/details/16113241
下面是Wiki对Fibonacci Search的叙述:
To test whether an item is in the list of ordered numbers, follow these steps:
- Set k = m.
- If k = 0, stop. There is no match; the item is not in the array.
- Compare the item against element in Fk−1.
- If the item matches, stop.
- If the item is less than entry Fk−1, discard the elements from positions Fk−1+1 to n. Set k=k−1 and return to step 2.
- If the item is greater than entry Fk−1, discard the elements from positions 1 to Fk−1. Renumber the remaining elements from 1 to Fk−2, set k=k−2, and return to step2.
参考:Wiki
下面就根据这个算法一步一步实现这个程序:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void generateFibonacci(vector<int> &fibvec, int n)
{
if(n>47) n = 47; //整形数据只能存在47个fibonacci元素了。这里的fibonacci第一个数是0.
fibvec.push_back(0);
fibvec.push_back(1);
for (int i = 2; i < n; i++)
{
fibvec.push_back(fibvec[i-1] + fibvec[i-2]);
}
}
template<typename T>
int fibonacciSearch(vector<T> &vt, T key)
{
vector<int> fibvec;
generateFibonacci(fibvec, 47);
int n = vt.size();
//1.Set k = m.
int k = lower_bound(fibvec.begin(), fibvec.end(), n) - fibvec.begin();
//offset is needed for recording the begin point in vt.
int offset = 0;
int index = 0;
//2.If k = 0, stop. There is no match; the item is not in the array.
while (k>0)
{
index = fibvec[--k]+offset;
//3.Compare the item against element in Fk−1.
//5.If the item is less than entry Fk−1, discard the elements from positions Fk−1 + 1 to n. Set k = k − 1 and return to step 2.
//注意:这里的index有可能大于数组最大值n的。因为fibvec[k]有可能远远大于n,那么fibvec[k-1]+offset就有可能也大于n。注意这个特殊情况。比如数组元素有22个,那么就要选取fibvec[k] == 34。那么fibvec[k-1] == 21,第二趟循环的时候offset有可能变成21,那么f[k]==13,f[k-1]==8那么就很容易超标!
if(index>=n || key < vt[index]) continue;
//6.If the item is greater than entry Fk−1, discard the elements from positions 1 to Fk−1. Renumber the remaining elements from 1 to Fk−2, set k = k − 2, and return to step 2.
else if (key > vt[index])
{
offset = index;
k--;
}
//4.If the item matches, stop.
else return index;
}
return -1;
}
int main()
{
std::vector<int> vec;
// set some initial content:
for (int i=1;i<10;i++) vec.push_back(i<<2);
vec.resize(7);
vec.resize(12,80);
std::cout << "vec contains:";
for (int i=0;i<vec.size();i++)
std::cout << ' ' << vec[i];
std::cout << '\n';
cout<<endl<<"Fibonacci List:\n";
vector<int> fibvec;
generateFibonacci(fibvec, 120);
for (auto x:fibvec)
cout<<x<<" ";
cout<<endl<<endl;;
cout<<"Fibonacci Search: ";
int fibnd = fibonacciSearch(vec, 28);
cout<<fibnd;
if(fibnd != -1)
cout<<"\tValue: "<<vec[fibnd]<<endl;
else cout<<"\tNo Found!"<<endl;
system("pause");
return 0;
}
主要注意这个容易出错的地方:这里的index有可能大于数组最大值n的。因为fibvec[k]有可能远远大于n,那么fibvec[k-1]+offset就有可能也大于n。注意这个特殊情况。比如数组元素有22个,那么就要选取fibvec[k] == 34。那么fibvec[k-1] == 21,第二趟循环的时候offset有可能变成21,那么f[k]==13,f[k-1]==8那么就很容易超标!
运行结果: