有序数组的查询三种方法,二分,插值,斐波那契,我觉得这是最优化的知识,不是编程算法的,可能大家不分家吧
问题背景
给定一个有序数组nums,和一个待查找的数target,找到target的下标
最简单的是挨个遍历,这个时间复杂度太拉跨了,不讨论
方法1----------二分法
每次都折半查找,找到就返回
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int binarySearch(vector<int>& nums, int target) {
if (nums[0] > target || nums[nums.size() - 1] < target) {
return -1;
}
int left = 0, right = nums.size(), mid = 0;
while (left <= right) {
mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
return -1; //当循环条件有=时
}
int main() {
vector<int> nums = {-150,-140, -110, -10, 0, 3, 5, 15, 19, 90, 110, 122, 172, 212};
//二分查找
int index = 0, target = 3;
index = binarySearch(nums, target);
if (index != -1) {
cout << target << "在数组中的下标是:" << index << endl;
}
return 0;
}
方法2----------插值
其实算是二分的扩展,你可以对半折,我也可以3/4折,或者1/4折吗,原来是 left + (right - left) / 2,那我乘 1/2 换成乘 ((target - nums[left]) / (nums[right] - nums[left]))
也是合理的吗,这个东西就相当于计算 target 和上边界和下边界的比重,看他更偏向哪边。
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int chazhiSearch(vector<int> nums, int target) { //因为函数里面要修改数组,所以不传引用,改传值,虽然会增加栈空间消耗,但是保证原始数据不受影响
if (nums[0] > target || nums[nums.size() - 1] < target) {
return -1;
}
int left = 0, right = nums.size(), mid = 0;
while (left <= right) {
mid = left + (right - left) * ((target - nums[left]) / (nums[right] - nums[left]));
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
return -1; //当循环条件有=时
}
int main() {
vector<int> nums = {-150,-140, -110, -10, 0, 3, 5, 15, 19, 90, 110, 122, 172, 212};
int index = 0, target = 3;
index = chazhiSearch(nums, target);
if (index != -1) {
cout << target << "在数组中的下标是:" << index << endl;
}
return 0;
}
方法3------------斐波那契法
大概思路就是保证每个查找的子空间都是 F[k]-1 个元素,满足下面的示意图,比mid小就查前面,大就查后面,还挺精髓的,还有黄金分割法,不过由于0.618,不太好变成整数下标,更适合做实数搜索吧
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<int> F = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34}; //先定义一个斐波那契数列的全局变量
int fibonaciiSearch(vector<int>& nums, int target) {
if (nums[0] > target || nums[nums.size() - 1] < target) {
return -1;
}
int left = 0, right = nums.size(), mid = 0, n = nums.size(), k = 0;
while (n > F[k]-1) { //计算n位于斐波那契数列的位置,第几个,即下标+1
++k;
}
for (int i = n; i < F[k]-1; ++i) { //添加数让数组始终保持在 F[k]-1个
nums.push_back(nums[n - 1]);
}
while (left <= right) {
mid = left + F[k-1] - 1; //分割点在 数列的前一个哪儿让数列变成 [left,mid-1](F[k-1]-1个数]) mid [mid+1,right](F[k-2]-1个数)
if (target < nums[mid]) {
right = mid - 1;
k = k - 1;
} else if (target > nums[mid]) {
left = mid + 1;
k = k - 2;
} else {
if (mid <= n)
return mid;
else
return n;
}
}
return mid;
}
int main() {
vector<int> nums = {-150,-140, -110, -10, 0, 3, 5, 15, 19, 90, 110, 122, 172, 212};
int index = 0, target = 3;
index=fibonaciiSearch(nums, target);
if (index != -1) {
cout << target << "在数组中的下标是:" << index << endl;
}
return 0;
}