力扣704-数组-二分查找

文章介绍了在有序整数数组中使用单边查询、双边查询和二分法(包括左闭右闭、左闭右开、左开右闭)寻找目标值的方法,比较了它们的时间和内存效率,并指出在二分法中选择正确的区间和middle值的重要性,特别提到了可能导致死循环的问题和解决方案。
摘要由CSDN通过智能技术生成

题目

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1


示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

方法一:单边查询

public class Solution {
    public int Search(int[] nums, int target) {
        int searchIndex=nums.Length;
        for(int i=0;i<searchIndex;i++)
        {
            if(nums[i]==target) return i;
        }
        return -1;
    }
}

非常简单的单边从左到右依次进行判断,时间击败48%,内存击败98%

方法二:双边查询

public class Solution {
    public int Search(int[] nums, int target) {
        int searchIndex=nums.Length;
        for(int i=0;i<searchIndex;i++)
        {
            if(nums[i]==target) return i;
            if(nums[searchIndex-i-1]==target) return searchIndex-i-1;
        }
        return -1;
    }
}

左右同时开始判断,时间击败89%,内存击败96%

方法三:二分法

双边查询,如果值在数列中间则很难查询到,利用二分法,先判断两端,再一直迭代判断中点

public class Solution {
    public int Search(int[] nums, int target) {
        int left=0;
        int right=nums.Length-1;
        int middle=0;
        if(nums[left]==target) return left;
        if(nums[right]==target) return right;
        if(left==right) return -1;
        while(right-left!=1){
            middle=(left+right)/2;
            if(nums[middle]==target) return middle;
            else if(nums[middle]<target) left=middle;
            else right=middle;
        }
        return -1;
    }
}

时间击败79%,内存击败84%,结果反而不如双边,总感觉代码很拖沓,且未通过长度为1的数列

方法四 真正的二分法

简单看了一下视频,发现我的二分法中出现的区别,middle进行多次的判断,应当赋值middle+1或者-1,重新进行书写

学习代码的视频:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

左闭右闭区间
public class Solution {
    public int Search(int[] nums, int target) {
        int left=0;
        int right=nums.Length-1;
        int middle=0;
        while(left<=right)
        {
            middle=(right+left)/2;
            if(nums[middle]==target) return middle;
            else if(nums[middle]<target) left=middle+1;
            else right=middle-1;
        }
        return -1;
    }

时间超过48%,内存超过60% 效率最高的居然还是双边,不知道是什么原因,等以后深入了解c#后再来回答

左闭右开
public class Solution {
    public int Search(int[] nums, int target) {
        int left=0;
        int right=nums.Length;
        int middle=0;
        while(left<right)
        {
            middle=(right+left)/2;
            if(nums[middle]==target) return middle;
            else if(nums[middle]<target) left=middle+1;
            else right=middle;
        }
        return -1;
    }
}
左开右闭
public class Solution {
    public int Search(int[] nums, int target) {
        int left=-1;
        int right=nums.Length-1;
        int middle=0;
        while(left<right)
        {
            middle=left+(right-left)/2+1;
            if(nums[middle]==target) return middle;
            else if(nums[middle]<target) left=middle;
            else right=middle-1;
        }
        return -1;
    }
}
总结

在二分法中,有几大要点会导致在某些序列中会导致一直循环的问题分别是:

1.选择搜索区间

2.在不等于target情况下,left 和 right 的取值问题

2.middle值的选择

搜索区间 

在二分法中搜索区间尤为关键,区间决定了 left 和 right 的取值,以及 while 中的 left与right比较问题。

三种不同区间的left,right取值如上述代码,总的概括就是

  1. left 和 right 的取值需要保证序列全部值在区间内
  2. left 与 right 比较需要保证在区间内的值均被遍历
在不等于target情况下,left 和 right 的取值问题

这类的区间问题需要保证的就是 需要遍历的值被遍历,且仅被遍历一次 在某些情况下,当被遍历时如果多次遍历同一值会陷入死循环

middle值的选取

这个问题出现自 左开右闭左闭右开 的代码区别上,很显然,两者是 对称 的,我们会下意识的简单的把不等于target情况下的 left 和 right 的取值 调换,但是这样做,左开右闭的情况下会造成死循环。

猜测原因:可能是因为在取middle的过程中,/2这一步骤是向下取整导致陷入死循环,当左开的时候需要实现向上取值,即+1,便不会陷入死循环中

题外tips 在写middle=(left+right)/2时尽量写成middle=left+(right-left)/2,可以有效防止(left+right)造成的溢出。

左开右闭的办法来自于:二分查找的细节(左闭右闭、左闭右开、左开右闭)及其两段性-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值