day6刷题之二分搜索1

二分查找代码

class Solution {
    public int searchInsert(int[] nums, int target) {

        int left = 0, right = nums.length-1;
        //注意循环条件
        while (left <= right) {
            //求mid
            int mid = left + ((right - left ) >> 1);
            //查询成功
            if (target == nums[mid]) {
                return mid;
            //右区间    
            } else if (nums[mid] < target) {
                left = mid + 1;   
            //左区间               
            } else if (nums[mid] > target) {
                right = mid - 1;
            }
        }
        //返回插入位置
        return left;
    }
}

leetcode 34在排序数组中查找元素的第一个和最后一个位置

https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
题目描述
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

题目解析

们可以使用遍历,当查询到第一个 5 时,我们设立一个指针进行定位,然后到达最后一个 5 时返回,这样我们就能求的第一个和最后一个五了,当然也可以使用二分查找

使用二分查找:

target <= nums[mid] 时,让 right = mid - 1即可,这样我们就可以继续在 mid 的左区间继续找 5 。是不是听着有点绕,我们通过下面这组图进行描述。

左边界1

左边界2

其实原理很简单,就是我们将小于和等于合并在一起处理,当 target <= nums[mid] 时,我们都移动右指针,也就是 right = mid -1,还有一个需要注意的就是,我们计算下边界时最后的返回值为 left ,当上图结束循环时,left = 3,right = 2,返回 left 刚好时我们的下边界。我们来看一下求下边界的具体执行过程

 

变体:找出第一个大于目标元素的索引

所有可能如下:

模糊边界情况

1.数组包含目标元素,找出在他后面的第一个元素

2.目标元素不在数组中,数组内的部分元素大于它,此时我们需要返回第一个大于他的元素

3.目标元素不在数组中,且数组中的所有元素都大于它,那么我们此时返回数组的第一个元素即可

4.目标元素不在数组中,且数组中的所有元素都小于它,那么我们此时没有查询到,返回 -1 即可。

既然我们已经分析完所有情况,那么这个题目对咱们就没有难度了,下面我们描述一下案例的执行过程

nums = {1,3,5,5,6,6,8,9,11} target = 7

上面的例子中,我们需要找出第一个大于 7 的数,那么我们的程序是如何执行的呢?

二分查找模糊边界目标值

模糊边界目标0

public static int lowBoundnum(int[] nums,int target,int left, int right) {

        while (left <= right) {
            //求中间值
            int mid = left + ((right - left) >> 1);
            //大于目标值的情况
            if (nums[mid] > target) {
                 //返回 mid
                if (mid == 0 || nums[mid-1] <= target) {
                    return mid;
                }
                else{
                    right = mid -1;
                }

            } else if (nums[mid] <= target){
                left = mid + 1;
            }
        }
        //所有元素都小于目标元素
        return -1;
    }

找出最后一个小于目标元素的索引
通过上面的例子我们应该可以完全理解了那个变种,下面我们继续来看以下这种情况,那就是如何找到最后一个小于目标数的元素。还是上面那个例子

nums = {1,3,5,5,6,6,8,9,11} target = 7

查找最后一个小于目标数的元素,比如我们的目标数为 7 ,此时他前面的数为 6,最后一个 6 的索引为 5,此时我们返回 5 即可,如果目标数元素为 12,那么我们最后一个元素为 11,仍小于目标数,那么我们此时返回 8,即可。这个变种其实算是上面变种的相反情况,上面的会了,这个也完全可以搞定了,下面我们看一下代码吧。

public static int upperBoundnum(int[] nums,int target,int left, int right) {

        while (left <= right) {

            int mid = left + ((right - left) >> 1);
             //小于目标值
            if (nums[mid] < target) {
                //看看是不是当前区间的最后一位,如果当前小于,后面一位大于,返回当前值即可
                if (mid == right || nums[mid+1] >= target) {
                    return mid;
                }
                else{
                    left = mid + 1;
                }

            } else if (nums[mid] >= target){
                right = mid - 1;
            }
        }
        //没有查询到的情况
        return -1;
    }

 

寻找最小值

微信图片_20201226204620

我们见上图,我们需要考虑的情况是

数组完全有序 nums[left] < nums[right],此时返回 nums[left] 即可

left 和 mid 在一个都在前半部分,单调递增区间内,所以需要移动 left,继续查找,left = mid + 1;

left 在前半部分,mid在后半部分,则最小值必在 left 和 mid 之间(见下图)。则需要移动right ,right = mid,我们见上图,如果我们 right = mid - 1,则会漏掉我们的最小值,因为此时 mid 指向的可能就是我们的最小值。所以应该是 right = mid 。

 

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

难度中等352收藏分享切换为英文接收动态反馈

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

请找出其中最小的元素。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0

示例 3:

输入:nums = [1]
输出:1

 微信图片_20201226204725微信图片_20201226204725

python3实现如下 

 

    def findMin(self, nums: List[int]) -> int:
        left = 0
        right = len(nums) - 1
        # 注意循环条件
        while (left <= right):
            if (nums[left] <= nums[right]):
                return nums[left]
            mid = left + ((right - left) >> 1)  ## 求mid
            if (nums[left] <= nums[mid]):
                left = mid + 1
                # 右区间
            else:
                right = mid
        return nums[left]

变体二维数组

二维数组
下面我们来看一下另外一种变体,如何在二维矩阵里使用二分查找呢?

其实这个很简单,只要学会了二分查找,这个完全可以解决,我们先来看一个例子

我们需要从一个二维矩阵中,搜索是否含有元素 7,我们如何使用二分查找呢?其实我们可以完全将二维矩阵想象成一个有序的一维数组,然后用二分,,比如我们的二维矩阵中,共有 9 个元素,那定义我们的 left = 0,right = 9 - 1= 8,是不是和一维数组定义相同,然后我们求我们的 mid 值, mid = left +((right - left) >> 1)此时 mid = 4 ,但是我们的二维矩阵下标最大是,nums[2,2]呀,你这求了一个 4 ,让我们怎么整呀。如果我们理解了二分查找,那么这个题目考察我们的应该是如何将一维数组的下标,变为 二维坐标。其实也很简单,咱们看哈,此时咱们的 mid = 4,咱们的二维矩阵共有 3行, 3列,那我们 mid =4,肯定在第二行,那么这个应该怎么求得呢?

我们可以直接用 (mid/列数),即可,因为我们 mid = 4,4 /3 = 1,说明在 在第二行,那如果 mid = 7 ,7/3=2,在第三行,我们第几行知道了,那么我们如何知道第几列呢?我们可以直接根据 (mid % 列数 )来求得呀,比如我们此时 mid = 7,7%3 = 1,那么在我们一维数组索引为 7 的元素,其处于二维数组的第2列,大家看看下图是不是呀!

74https://leetcode-cn.com/problems/search-a-2d-matrix/s/

题目描述
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

示例1

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]], target = 3
输出:true

示例2

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]], target = 13
输出:false

示例3

输入:matrix = [], target = 0
输出:false

题目解析
在上面我们已经解释了如何在二维矩阵中进行搜索,这里我们再对其进行一个总结,就是我们凭空想象一个一维数组,这个数组是有二维数组一层一层拼接来的,也是完全有序,然后我们定义两个指针一个指向一维数组头部,一个指向尾部,我们求得 mid 值然后将 mid 变成二维坐标,然后和 target 进行比较,如果大于则移动 left ,如果小于则移动 right 。

def searchMatrix(matrix,target):
    row,col= len(matrix),len(matrix[0])
    left,right=0,row*col-1
    # 注意循环条件
    while (left <= right):
        mid = left + ((right - left) >> 1)  ## 求mid
        if (matrix[mid//col][mid%col] == target):
            return True
        elif(matrix[mid//col][mid%col]<target):
            left = mid + 1
        else:
            right = mid - 1
    return False
matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]]
print( len(matrix),len(matrix[0]))
target = 1
out=searchMatrix(matrix,target)
print('结果:',out)

 

参考链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/solution/yi-wen-dai-ni-gao-ding-er-fen-sou-suo-ji-ki52/

来源:力扣(LeetCode)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值