1 算法题 :使用斐波那契查找算法在有序数组中查找指定元素
1.1 题目含义
使用斐波那契查找算法在有序数组中查找指定的元素。斐波那契查找算法是一种基于斐波那契数列的查找算法,主要用于有序数组的查找。该算法将斐波那契数列与二分查找的思想相结合,从而提高查找效率。
斐波那契数列是这样的一个数列:0、1、1、2、3、5、8、13、21、34、…,其中每个数字是前两个数字的和。斐波那契查找算法利用斐波那契数列的性质,将数组分为两部分,如果目标值在前半部分,则在前半部分继续查找;如果目标值在后半部分,则在后半部分继续查找。当数组的长度不是斐波那契数时,算法会对数组长度进行适当的扩展(通常是增加元素,保证可以整除),并在查找完成后恢复数组原样。
注意:在实际编程实现时,斐波那契查找算法可能需要处理一些边界情况,如当数组为空或只包含一个元素时,以及当目标值超出数组范围时。此外,由于斐波那契查找算法可能需要对数组进行扩展,因此在实际应用中可能需要对原始数组进行备份,以便在查找完成后恢复其原样。
1.2 示例
示例 1:
输入:
- nums = [1, 3, 4, 7, 10, 12, 15, 18, 20, 25, 30]
- target = 7
输出:
- 3
说明:
- 因为元素 7 在数组中的索引是 3。
示例 2:
输入:
- nums = [1, 3, 4, 7, 10, 12, 15, 18, 20, 25, 30]
- target = 2
输出:
- -1
说明:
- 2 不存在于 nums 中,返回 -1。
示例 3:
输入:
- nums = []
- target = 0
输出:
- -1
说明:
- nums 为空,0 不存在于 nums 中,返回 -1。
2 解题思路
2.1 循环遍历实现
(1)初始化:
- 接收有序数组和目标值作为输入。
- 计算数组长度 n。
- 找到大于或等于n的最小斐波那契数F,这通常涉及计算斐波那契数列直到找到合适的数。
- 如果数组长度不是斐波那契数,则可能需要在数组末尾添加额外的元素(通常使用数组最后一个元素填充),以确保数组长度等于斐波那契数。
(2)定义斐波那契数列相关变量:
- 计算前两个斐波那契数 fibMMinus1 和 fibM(通常初始化为 0 和 1)。
- 后续斐波那契数可以通过迭代计算得到,直至达到或超过数组长度n。
(3)进行斐波那契查找:
- 初始化两个指针 low 和 high,分别指向数组的起始和结束位置。
- 当 low 小于等于 high 时执行循环:
-
- 计算 mid,即 low 和 high 之间的斐波那契分割点。
-
- 如果目标值等于数组中的 mid 位置的元素,返回 mid。
-
- 如果目标值小于 mid 位置的元素,则在数组的左半部分继续查找,更新 high 为 mid - 1。
-
- 如果目标值大于 mid 位置的元素,则在数组的右半部分继续查找,更新 low 为 mid + 1。
(4)处理边界情况:
- 如果循环结束仍未找到目标值,则目标值不在数组中,返回 -1。
(5)恢复数组:
- 如果在步骤 1 中扩展了数组,现在需要移除添加的额外元素,恢复数组原样。
(6)结束:
- 返回查找到的目标值的索引或 -1(如果未找到)。
2.2 递归实现
(1)初始化:
- 接收有序数组 arr、目标值 target、数组长度n作为输入。
- 初始化斐波那契数列中的两个关键数 fibMMinus2 和 fibMMinus1 为 0 和1。
- 根据数组长度 n,递归计算斐波那契数列中大于等于 n 的最小数 fibM。
(2)递归计算斐波那契数:
- 定义一个递归函数 fibonacci(int n),用于计算斐波那契数列中第n个数。
- 在函数内部,如果 n 等于 0 或 1,直接返回 n(斐波那契数列的前两个数)。
- 否则,递归调用f ibonacci(n-1) 和 fibonacci(n-2),并将它们的和作为当前斐波那契数返回。
(3)扩展数组:
- 如果数组长度 n 不是斐波那契数,通过复制最后一个元素来扩展数组,使其长度达到 fibM。
(4)递归查找:
- 定义一个递归查找函数 fibonacciSearch(int arr[], int low, int high, int target)。
- 在函数内部,计算中间位置 mid,该位置为 low + fibMMinus2。
- 如果 mid 越界(即 mid >= high),则调整 mid 为 high - 1。
- 如果目标值 target 等于 arr[mid],返回 mid。
- 如果 target 小于 arr[mid],递归调用 fibonacciSearch 在数组的左半部分查找,更新 high 为 mid - 1。
- 如果 target 大于 arr[mid],递归调用 fibonacciSearch 在数组的右半部分查找,更新 low 为 mid + 1,并且更新 fibMMinus2 和 fibMMinus1 为斐波那契数列中较小的两个数。
(5)处理未找到的情况:
- 如果递归查找函数返回 -1,说明目标值不在数组中,直接返回 -1。
(6)恢复数组:
- 如果在步骤 3 中扩展了数组,现在需要移除添加的额外元素,恢复数组原样。
(7)返回结果:
- 返回递归查找函数的结果,即目标值在数组中的索引,或者 -1(如果未找到)。
3 算法实现代码
3.1 循环遍历实现
如下为算法实现代码:
#include <iostream>
#include <vector>
// 计算斐波那契数列中大于等于n的最小数
int fibonacci(int n) {
if (n <= 1) return n;
int a = 0, b = 1, c;
while (a < n) {
c = a + b;
a = b;
b = c;
}
return b;
}
class Solution
{
public:
// 斐波那契查找算法
int fibonacciSearch(const std::vector<int>& arr, int target) {
int n = arr.size();
if (n == 0) return -1; // 空数组,直接返回-1
int fibM = fibonacci(n); // 计算斐波那契数列中大于等于n的最小数
// 扩展数组,确保长度为斐波那契数
std::vector<int> temp(fibM);
for (int i = 0; i < n; ++i) {
temp[i] = arr[i];
}
for (int i = n; i < fibM; ++i) {
temp[i] = arr[n - 1]; // 用最后一个元素填充
}
int low = 0, high = fibM - 1;
int mid, index = -1;
int fibMMinus1 = 0, fibMMinus2 = 1;
while (low <= high) {
mid = low + fibMMinus2; // 防止mid过大越界
if (mid >= high) {
mid = high;
}
if (temp[mid] > target) {
high = mid - 1;
fibMMinus2 = fibMMinus1;
fibMMinus1 = mid;
}
else if (temp[mid] < target) {
low = mid + 1;
fibMMinus1 = fibMMinus1 + fibMMinus2;
fibMMinus2 = fibMMinus1 - fibMMinus2;
}
else {
index = mid;
break;
}
}
// 如果找到了目标值,需要将其映射回原数组中的位置
if (index != -1 && index < n) {
return index;
}
return -1; // 未找到目标值
}
};
这段代码首先定义了一个辅助函数 fibonacci 来计算斐波那契数列中大于等于数组长度n的最小数。然后在 fibonacciSearch 函数中,扩展了数组以确保其长度是斐波那契数,并初始化了斐波那契查找所需的相关变量。接下来,使用循环进行查找,并根据比较结果调整 low 和 high 的边界以及斐波那契数列的相关值。如果找到目标值,则将其映射回原数组中的位置并返回;否则,返回 -1 表示未找到目标值。
调用上面的算法,并得到输出:
int main()
{
Solution s;
std::vector<int> arr = { 1, 3, 4, 7, 10, 12, 15, 18, 20, 25, 30 };
int target = 7;
int index = s.fibonacciSearch(arr, target);
if (index != -1) {
std::cout << "Element found at index: " << index << std::endl;
}
else {
std::cout << "Element not found in the array." << std::endl;
}
return 0;
}
上面代码的输出为:
Element found at index: 3
3.2 递归实现
如下为算法实现代码:
#include <iostream>
#include <vector>
// 递归计算斐波那契数
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
class Solution
{
public:
// 斐波那契查找算法
int fibonacciSearch(const std::vector<int>& arr, int target) {
int n = arr.size();
if (n == 0) return -1; // 空数组,直接返回-1
int fibM = fibonacci(n); // 计算斐波那契数列中大于等于n的最小数
// 初始化递归查找所需的斐波那契数
int fibMMinus1 = 0;
int fibMMinus2 = 1;
// 调用递归查找函数
return fibonacciSearchRecursive(arr, 0, n - 1, target, fibM, fibMMinus2, fibMMinus1);
}
private:
// 递归查找函数
int fibonacciSearchRecursive(const std::vector<int>& arr, int low, int high, int target, int fibM, int fibMMinus2, int fibMMinus1) {
if (low > high) {
return -1;
}
int mid = low + fibMMinus2;
// 防止mid越界
if (mid >= high) {
mid = high;
}
if (arr[mid] == target) {
return mid;
}
if (arr[mid] > target) {
return fibonacciSearchRecursive(arr, low, mid - 1, target, fibM, fibMMinus1, fibMMinus1 + fibMMinus2);
}
else {
return fibonacciSearchRecursive(arr, mid + 1, high, target, fibM, fibMMinus2, fibMMinus1);
}
}
};
这段代码中首先定义了一个递归函数 fibonacci 来计算斐波那契数。然后,定义了递归查找函数 fibonacciSearchRecursive,它接收数组、查找范围(low 和 high)、目标值以及斐波那契数列中的三个数(fibM、fibMMinus2 和 fibMMinus1)作为参数。递归查找函数根据中间位置 mid 与目标值 target 的比较结果来缩小查找范围,并递归调用自身。
4 测试用例
以下是针对上面算法的测试用例,基本覆盖了各种情况:
(1)基础测试用例
输入:数组 [-1, 0, 3, 5, 9, 12],目标值 9
输出:4
说明:目标值 9 存在于数组中,位于索引 4 的位置。
(2)目标值不存在于数组中
输入:数组 [-1, 0, 3, 5, 9, 12],目标值 2
输出:-1
说明:目标值 2 不存在于数组中。
(3)目标值位于数组开头
输入:数组 [-1, 0, 3, 5, 9, 12],目标值 -1
输出:0
说明:目标值 -1 位于数组的开头,即索引 0 的位置。
(4)目标值位于数组末尾
输入:数组 [-1, 0, 3, 5, 9, 12],目标值 12
输出:5
说明:目标值 12 位于数组的末尾,即索引 5 的位置。
(5)目标值位于数组中间
输入:数组 [-1, 0, 3, 5, 9, 12],目标值 3
输出:2
说明:目标值 3 位于数组的中间位置,即索引 2 的位置。
(6)空数组
输入:数组 [],目标值 9
输出:-1
说明:空数组不包含任何元素,因此无法找到目标值。
(7)数组只有一个元素
输入:数组 [9],目标值 9
输出:0
说明:数组只有一个元素,且该元素就是目标值,位于索引 0 的位置。
(8)数组中存在多个相同的目标值
输入:数组 [1, 2, 3, 3, 4, 5],目标值 3
输出:2 或 3
说明:数组中存在多个目标值 3,返回任意一个目标值的索引都是正确的。这里可以返回 2 或 3。