参考:https://www.cnblogs.com/yongh/p/9232742.html#_label1_2
源码:https://github.com/sunrui849/selectAlgorithm
目录:顺序查找
斐波那契查找
斐波那契数列如下所示:
斐波那契查找原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1(F代表斐波那契数列),如下图所示:
对F(k-1)-1的理解:
由斐波那契数列 F[k]=F[k-1]+F[k-2] 的性质,可以得到 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 。该式说明:只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,即如上图所示。从而中间位置为mid=low+F(k-1)-1
但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于n即可。
时间复杂度:最坏情况下,时间复杂度为O(log2n),且其期望复杂度也为O(log2n)。
顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。
代码实现
/**
* 斐波那契数列递归法
* @param num
* @return
*/
public static int getFibonacciValue(int num){
if (num == 0){
return 0;
}else if (num == 1){
return 1;
}
return getFibonacciValue(num-1) + getFibonacciValue(num-2);
}
/**
* 斐波那契数列非递归法
* @param num
* @return
*/
public static int getFibonacciValue2(int num){
int v1 = 0;
int v2 = 1;
if (num == 0){
return v1;
}else if(num == 1){
return v2;
}
int value = 0;
for (int i = 2;i <= num; i++){
value = v1 + v2;
v1 = v2;
v2 = value;
}
return value;
}
private static int select(List<Integer> list, int selectValue) {
if (list == null || list.isEmpty()){
return -1;
}
int listSize = list.size();
//获取斐波那契数下标
int index = 0;
while (getFibonacciValue(index)-1 < listSize){
index++;
}
int endValue = list.get(listSize-1);
//将list扩容为 F(n)-1 长度 即存在某一个斐波那契数减一大于集合的长度,最小的那个斐波那契数。
for (int i = listSize;i < getFibonacciValue(index) - 1;i++){
list.add(endValue);
}
int low = 0;
int height = list.size() - 1;
while (height >= low){
//中间值左边为f(n-2)-1个值,右边为f(n-1)-1个值,一共f(n)-1个值
int middleIndex = low + getFibonacciValue(index-1) - 1;
if (list.get(middleIndex) > selectValue){
height = middleIndex-1;
index = index - 1;
}else if (list.get(middleIndex) < selectValue){
low = middleIndex+1;
index = index - 2;
}else {
if (middleIndex >= listSize){
return listSize - 1;
} else {
return middleIndex;
}
}
}
return -1;
}