Java 二分查找 插值查找 斐波那契查找

常用的查询算法:

  1. 顺序查找
  2. 二分查找
  3. 插值查找
  4. 斐波那契查找
  5. 树表查找
  6. 分块查找
  7. 哈希查找
  8. 索引查询
  9. 深度搜索&&广度搜索

一、 二分查找

条件:数组必须是有序的
优点:比较次数少;查找速度快;平均性能好
缺点:待查表为有序数组(若为无序数组,分成两份查找无意义,排序本身也耗费时间);插入删除困难(增删需要移动大量的节点)
时间复杂度为O(lgN);

(一)基本方法

int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; // 注意

    while(left <= right) { // 注意
        int mid = left+(right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
        }
    return -1;
}

代码的注意点:
1.停止的条件为:left<=right。如果left<right的话,当left和right相等的值是检查不到的。
2.left=mid+1;right=mid-1。因为mid已经检查过了,所以可以剔除掉。
3.left+(right - left) / 2。不用(left+right)/2是为了防止溢出。
3.局限性:对于有序数组 nums = [1,2,2,2,3],target = 2,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。

(二)查找左边界的目标

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意

    while (left < right) { // 注意
        int mid = left+(right-left) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    if (left == nums.length) return -1;
	// 类似之前算法的处理方式
	return nums[left] == target ? left : -1;
}

修改的地方:
1.并不是nums[mid] == target时直接返回,而是将right值赋给它,因为我们要找最右侧的这个点。因此这个操作相当于是,将范围定位在这个值的左侧范围内。
2. int right = nums.length。相当于在这个值的右侧加了一个点 ,但是这个点是不能取到的。同样的,right=mid,但是mid点是不用取的,所以我们相当于是[left, right)范围内搜索的。因此,left < right的结束条件是left=right,此时搜索范围为空。

(三)查找右边界的目标

int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;

    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            left = mid + 1; // 注意
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    if (left == 0) return -1;
	return nums[left-1] == target ? (right-1) : -1;

修改的地方:
1.当 nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。

二、插值查找

二分查找选取中间位置,插值查找则通过查找值判定大概位于序列的哪个位置比例。

插入查找:选择下标 mid= left + (right - left) * (key - arr[left])/(arr[right] - arr[left])

(1)序列为有序序列

(2)根据公式选取比较值,相等返回下标,不相等则根据公式继续查找,未找到返回-1

用插值寻找时,需要这个有序序列中的数字大小排序整体均匀。例如[1,2,3,4,5],而非[1,5,6,15,39]。

三、斐波那契查找

斐波那契查找同样是不断修改mid值,令mid=low+F(k-1)-1,F(k-1)是斐波那契函数值。
计算过程依据的是F(k) = F(k-1) + F(k-2),初始的两个值是1,1,因此可以递归。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值