有序数组的查询三种方法,二分,插值,斐波那契,我觉得这是最优化的知识,不是编程算法的,可能大家不分家吧

问题背景

        给定一个有序数组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;
}
posted on 2021-06-12 09:48  雾恋过往  阅读(116)  评论(0编辑  收藏  举报

Live2D