二分查找算法的四种变形题型

之前,我们了解了二分查找的基本算法情况,如果有小伙伴不是特别清楚的话,可以查看之前的文章二分查找算法
现在讨论的也就是二分查找的变种题型

在此,我先给出测试的案例,然后简单分析不同的变种题型需要注意的点,我们测试的数组,是基于有序的数组进行测试的。

原数组是nums[] = [1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15]
测试的数据有多种情况 分别是 target 为 0,1,2,7,15,17 中的一种情况

在这里插入图片描述

现在已知数组的最小的下标值时 low =0,最大值的索引下标是high = nums.length,数组中的中间值的下标值时mid = (low + high)/ 2

查找第一个值等于给定值的元素

先根据二分查找,当满足 nums[mid] = target 的时候,需要再向前判断一位 nums[mid -1] 是否等于 target?
如果相等的话,我们需要继续使用二分查找,此时high = mid -1;
如果不相等的话,表示此时这个目标查找的第一个值,所以返回mid的下标值

此时我们分析 当target = 2 的情况,其他情况比较简单。
在这里插入图片描述

查找最后一个值等于给定值的元素

这个题型,和第一个的题型的不同之处,在于当满足了 nums[mid] = target 的时候,我们需要判断mid的下一位值是否也等于给定值,也就是满足 nums[mid+1]= target。如果不满足,直接返回就好了
在这里插入图片描述

查找第一个大于等于给定值的元素

这个题型的思路,很类似于第一种情况,查找第一个值等于给定值的元素

假设数组 nums[] = [1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15],目标值target 7
查找 第一个大于等于给定值的元素,我们可以看到元素是9对应的下标索引是 12

通过二分查找,其中有两种情况,

  • 第一种情况就是给定的值在数组中,那么和 查找第一个值等于给定值的元素 的解法相同
  • 第二种情况就是给定的值不再数组中,那么需要找到第一个大于该元素的索引下标,我们可以假定target=7,满足条件的也就是9对应的下标索引12

这个我就省略不写了哈 o(╥﹏╥)o

查找最后一个小于等于给定值的元素

同志们,举一反三吧 这个我就省略不写了哈 o(╥﹏╥)o
以下我给出了具体的算法实现

算法实现

package com.sh.study.search;

import java.util.Arrays;
import java.util.List;

/**
 * 二分查找的几个变形问题
 * <p>
 * 1:查找第一个值等于给定值的元素
 * 2:查找最后一个值等于给定值的元素
 * 3:查找第一个大于等于给定值的元素
 * 4:查找最后一个小于等于给定值的元素
 *
 * @Author zhouwenchen
 * @Data 2020/8/13/16
 **/
public class BinarySearchDemo {

    /**
     * 1:查找第一个值等于给定值的元素
     * 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向前判断一位  nums[mid -1] 是否等于  target
     * 如果不等于,就直接返回mid
     * 如果等于,需要继续使用二分查找
     *
     * @return
     */
    public static int binarySearch1(int[] nums, int target) {
        if (nums == null) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;

        // 判断第一个是否等于 target,如果等于,直接返回
        if (nums[start] == target) {
            return start;
        }
        while (start <= end) {
            int mid = start + (((end - start)) >> 1);
            if (nums[mid] == target) {
                // 需要判断前一位是否也等于 target,不等于直接返回
                if (nums[mid - 1] != target) {
                    return mid;
                }
                // nums[mid -1] == target
                end = mid - 1;
            } else if (nums[mid] < target) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        return -1;
    }

    /**
     * 2:查找最后一个值等于给定值的元素
     * 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向后判断一位  nums[mid + 1] 是否等于  target
     * 如果不等于,就直接返回mid
     * 如果等于,需要继续使用二分查找
     *
     * @param nums
     * @param target
     * @return
     */
    public static int binarySearch2(int[] nums, int target) {
        if (nums == null) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;

        // 判断最后一个是否等于 target,如果等于,直接返回
        if (nums[end] == target) {
            return end;
        }
        while (start <= end) {
            int mid = start + (((end - start)) >> 1);
            if (nums[mid] == target) {
                // 需要判断后一位是否也等于 target,不等于直接返回
                if (nums[mid + 1] != target) {
                    return mid;
                }
                // nums[mid + 1] == target
                start = mid + 1;
            } else if (nums[mid] < target) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        return -1;
    }

    /**
     * 3:查找第一个大于等于给定值的元素
     * 这题思路同第一个  `查找第一个值等于给定值的元素`
     * <p>
     * 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向前判断一位  nums[mid -1] 是否等于  target
     * 如果不等于,就直接返回mid
     * 如果等于,需要继续使用二分查找
     *
     * @param nums
     * @param target
     * @return
     */
    public static int binarySearch3(int[] nums, int target) {
        if (nums == null) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;

        // 判断第一个是否大于等于 target,如果满足,直接返回
        if (nums[start] >= target) {
            return start;
        }
        if(nums[end] < target){
            return -1;
        }
        while (start <= end) {
            int mid = start + (((end - start)) >> 1);
            if (nums[mid] == target) {
                // 需要判断前一位是否也大于等于 target,不满足 直接返回
                if (nums[mid - 1] != target) {
                    return mid;
                }
                // nums[mid + 1] == target
                end = mid - 1;
            } else if (nums[mid] < target) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        if (end < start && nums[end] < target && nums[start] > target) {
            return start;
        }
        return -1;
    }

    /**
     * 4:查找最后一个小于等于给定值的元素
     * 思路同 `查找最后一个值等于给定值的元素`
     * <p>
     * 思路:先根据二分查找,当 mid 满足 nums[mid] == target,需要再向后判断一位  nums[mid + 1] 小于 等于  target
     * 如果不等于,就直接返回mid
     * 如果等于,需要继续使用二分查找
     *
     * @param nums
     * @param target
     * @return
     */
    public static int binarySearch4(int[] nums, int target) {
        if (nums == null) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;

        // 判断最后一个是否大于等于 target,如果满足,直接返回
        if (nums[end] <= target) {
            return end;
        }
        if(nums[start] > target){
            return -1;
        }
        while (start <= end) {
            int mid = start + (((end - start)) >> 1);
            if (nums[mid] == target) {
                // 需要判断后一位是否也大于等于 target,不满足 直接返回
                if (nums[mid + 1] != target) {
                    return mid;
                }
                // nums[mid + 1] == target
                start = mid + 1;
            } else if (nums[mid] < target) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        if(end < start && nums[end] < target && nums[start] > target){
            return end;
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 9, 11, 15,15};
        // target 的测试取值,要包含多种情况 0,1,2,7,15,17 这几种情况
        int target = 17;
        System.out.println("查找第一个值等于给定值的元素:" + binarySearch1(nums, target));
        System.out.println("查找最后一个值等于给定值的元素:" + binarySearch2(nums, target));
        System.out.println("查找第一个大于等于给定值的元素:" + binarySearch3(nums, target));
        System.out.println("查找最后一个小于等于给定值的元素:" + binarySearch4(nums, target));
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值