算法 - 二分查找

这篇博客探讨了二分查找算法在有序数组、旋转排序数组和存在峰值元素数组中的应用。文章详细介绍了如何处理溢出、找到旋转排序数组中的最小值以及在有重复数据的情况下进行查找。此外,还讲解了如何在数组中找到峰值元素。通过优化二分查找,可以更高效地解决这些问题。
摘要由CSDN通过智能技术生成

基础二分

   /**
     * 要求数组有序 ,二分法
     * @param arr
     * @param
     * @return
     */
    private static int binarySearch(int[] arr, int left, int right, int findVal) {
        //给一个出口
        if (left >= right) {
            return -1;
        }

        int mid = (left + right) / 2;
        int midV = arr[mid];

        if (midV < findVal) {
            return binarySearch(arr, mid + 1, right, findVal);
        } else if (midV > findVal) {
            return binarySearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }

    }

这里有一个讲究的地方:

如果用mid=(left+right)/2,在运行二分查找程序时可能溢出超时。
因为如果left和right相加超过int表示的最大范围时就会溢出变为负数。
所以如果想避免溢出,不能使用mid=(left+right)/2,应该使用mid=left+(right-left)/2。

还有的方式:

    public int search(int[] nums, int pivot) {
        int length = nums.length;
        int left = 0;
        int right = length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == pivot) return mid;
            if (nums[mid] > pivot) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }

同时要注意 left<= right 的退出条件,不然存在以下直接退出-1情况:

search(new int[]{1, 2, 3, 4, 5,6}, 4)

寻找旋转排序数组中的最小值

假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] 。
请找出其中最小的元素。

本题的关键为找间隙,找到那个进行的旋转的间隙位置,其左右就是最小值。

不断逼近间隙,

 public int findMin(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[right]) {          
                left = mid + 1;
            } else {                                
                right = mid;
            }
        }
        return nums[left];
    }

存在一种找到即退出的优化方案:

 public int findMin(int[] nums) {

    if (nums.length == 1) {
      return nums[0];
    }
    int left = 0, right = nums.length - 1;
    if (nums[right] > nums[0]) {
      return nums[0];
    }
    while (right >= left) {
      int mid = left + (right - left) / 2;
      if (nums[mid] > nums[mid + 1]) {
        return nums[mid + 1];
      }
      if (nums[mid - 1] > nums[mid]) {
        return nums[mid];
      }  
      if (nums[mid] > nums[0]) {
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
    return -1;
  }

存在重复数据

解析来自leetcode
在这里插入图片描述
第一种情况是nums[pivot] < nums[high]。如下图所示,这说明nums[pivot] 是最小值右侧的元素,因此我们可以忽略二分查找区间的右半部分。

在这里插入图片描述
第二种情况是 nums[pivot]>nums[high]。如下图所示,这说明 nums[pivot] 是最小值左侧的元素,因此我们可以忽略二分查找区间的左半部分。
在这里插入图片描述

第三种情况是nums[pivot]==nums[high]。如下图所示,由于重复元素的存在,我们并不能确定 nums[pivot] 究竟在最小值的左侧还是右侧,因此我们不能莽撞地忽略某一部分的元素。我们唯一可以知道的是,由于它们的值相同,所以无论 nums[high] 是不是最小值,都有一个它的「替代品」nums[pivot],因此我们可以忽略二分查找区间的右端点。
在这里插入图片描述

    public int findMin(int[] nums) {
        int low = 0;
        int high = nums.length - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            } else if (nums[pivot] > nums[high]) {
                low = pivot + 1;
            } else {
                high -= 1;
            }
        }
        return nums[low];
    }

寻找峰值

峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。

nums[-1] = nums[n] = -∞ 这个条件是关键,而且只需要找到一个。

   public int findPeakElement(int[] nums) {
        int l = 0, r = nums.length - 1;
        while (l < r) {
            int mid = (l + r) / 2;
            if (nums[mid] > nums[mid + 1])
                r = mid;
            else
                l = mid + 1;
        }
        return l;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值