算法刷题(Java与Python)1.二分查找

目录

二分查找

思路

总体

细节

问题一,为什么循环的条件是left<=right   ,为什么要有等号呢

问题二,为什么中间值是left +(right - left) / 2

问题三,为什么最后返回的是左边的值呢

情况 1:target 存在于数组中

情况 2:target 不存在于数组中

例题1

代码实现

Java

Python 

例题2

Java

Python

例题三

Java

Python

总结


二分查找

思路

总体

二分查找前提是已经是从小到大排序了,就是对半着查找

一开始先看最中间那个

如果等于,那就找到了,直接返回

如果目标小于中间值,那么把右边界赋值为中间值减一

如果目标大于中间值,那么把左边界赋值为中间值加一

细节

问题一,为什么循环的条件是left<=right   ,为什么要有等号呢
  • 当 left == right 时,搜索区间仍然包含 一个元素(即 nums[left] 或 nums[right]),需要检查这个元素是否等于 target

问题二,为什么中间值是left +(right - left) / 2

(right - left)只是偏移量,在0~5是可以用的, 但是如果最左边不是0,那就错了,要加上最左边

一开始写的是(left +right)/2    但是这样有可能会溢出  ,超出java  int类型的最大值,导致结果是负数,  java  int最大值是2,147,483,647(2³¹ - 1)     ,java的第一位是符号位,如果你超出了最大值,就会变成负数,负数除2还是负数。

int max = Integer.MAX_VALUE; // 2147483647 (0x7FFFFFFF)
int overflow = max + 1;      // 溢出后变为 -2147483648 (0x80000000)

注意:

>>是有符号右移 / 算术右移     ,移了之后原符号不变

>>>(无符号右移 / 逻辑右移), ,移了之后原符号可能改变

可以采用

方法一:left +(right - left) / 2

方法二:(left +right)>>>1,是向下取整的

虽然说超出了是负数,但其实二进制的数还是一样的,只是java把第一位看作是符号位,所以你向右移一位,其实就是除二向下取整

问题三,为什么最后返回的是左边的值呢
情况 1:target 存在于数组中
  • 在循环中直接返回 mid,不会走到最后的 return 语句。

情况 2:target 不存在于数组中
  • left 的物理意义

    • 它是 target 应该插入的位置,即第一个比 target 大的元素的位置。

    • 如果所有元素都比 target 小,left 会停在 len(nums)(数组末尾之后)。

    • 如果所有元素都比 target 大,left 会停在 0(数组开头)。

  • right 的问题

    • right 指向的是最后一个比 target 小的元素,插入后会破坏顺序。

    • 例如 nums = [1,3,5,6]target = 2

      • 终止时 left = 1right = 0

      • 插入到 left(1)是正确的 [1, **2**, 3, 5, 6],而 right(0)会错误地插入到开头。

例题1

代码实现

Java
package Test;

import java.util.HashMap;
import java.util.Map;

class Solution {
    public static int searchInsert(int[] nums, int target) {
        int length = nums.length;
        int left = 0;
        int right = length - 1;
        while (left <= right) {
            int mid = left +(right - left) / 2;
            //为什么是这样呢,(right - left)只是偏移量,在0~5是可以用的
            // 但是如果最左边不是0,那就错了,要加上最左边
            if (nums[mid] == target) {
                return mid;
            }
            else if(target<nums[mid]){
                right = mid - 1;

            }
            else if (target>nums[mid]) {
                left = mid + 1;

            }
        }

        return left;
    }

    public  static void main(String[] args) {
        int[] nums = {1,3,5,6};
        System.out.println(searchInsert(nums, 2));
        for (int num : nums) {
            System.out.print(num);
        }
    }
}

Python 

注意python中,/是有小数点的除   // 是整除,向下取整


class Solution:
    def searchInsert( self,nums, target):
        length = len(nums)
        left = 0
        right = length - 1
        while left<=right:
            mid=left+(right-left)//2
            if target==nums[mid]:
                return mid
            if target<nums[mid]:
                right=mid-1
            if target>nums[mid]:
                left=mid+1
        return left;
solution = Solution()
nums=[1,3,5,6]
print(solution.searchInsert(nums, 2))  # 输出: 1


例题2

Java

class Solution {
    public int search(int[] nums, int target) {
        int length = nums.length;
        int left = 0;
        int right = length - 1;
        while (left <= right) {
            int mid = left +(right - left) / 2;
            //为什么是这样呢,(right - left)只是偏移量,在0~5是可以用的
            // 但是如果最左边不是0,那就错了,要加上最左边
            if (nums[mid] == target) {
                return mid;
            }
            else if(target<nums[mid]){
                right = mid - 1;

            }
            else if (target>nums[mid]) {
                left = mid + 1;

            }
        }

        return -1;
    }
}

Python

class Solution(object):
    def search(self, nums, target):
        mid=-1
        left=0
        right=len(nums)-1
        while left<=right:
            mid=left+(right-left)//2
            if(nums[mid]==target):
                return mid
            elif(target<nums[mid]):
                right=mid-1
            else:left=mid+1
        return -1

例题三

Java

在这次写法中,使用二分法之前,我先判断了数组是不是为空,和目标值在不在范围里面,可以避免无效的搜索,是一个优化的方法

package Test;

import java.util.HashMap;
import java.util.Map;

class Solution {
    public static int[] searchInsert(int[] nums, int target) {
        int length = nums.length;
        int left = 0;
        int right = length - 1;
        int leftcadidate=-1;
        int rightcadidate=-1;
        int[]result={leftcadidate,rightcadidate};
        //情况1,数组为空
        if(length==0)
            return result;
        //情况2,目标值超出范围
        // 我们知道了这个数组是递增的,那么我们先取边界值和目标值比较,如果不再范围内直接就返回了
        if(target<nums[0]||target>nums[length-1])
            return result;
        //情况三:目标值在范围里面
        while (left <= right) {
            int mid = left +(right - left) / 2;
            if (nums[mid] == target) {
               leftcadidate = mid;
               right = mid - 1;
            }
            else if(target<nums[mid]){
                right = mid - 1;

            }
            else if (target>nums[mid]) {
                left = mid + 1;

            }
        }
        left = 0;
        right = length - 1;
        while (left <= right) {
            int mid = left +(right - left) / 2;
            if (nums[mid] == target) {
                rightcadidate=mid;
                left = mid + 1;
            }
            else if(target<nums[mid]){
                right = mid - 1;

            }
            else if (target>nums[mid]) {

                left = mid + 1;
            }
        }
        result[0]=leftcadidate;
        result[1]=rightcadidate;
        return result;
    }

    public  static void main(String[] args) {
        int[] nums = {5,7,7,8,8,10};
        int target = 6;
       int [] result=searchInsert(nums, target);
       for (int i = 0; i < result.length; i++) {
           System.out.println(result[i]);
       }

    }
}

Python

class Solution(object):
    def searchRange(self, nums, target):
        mid=-1
        left=0
        right=len(nums)-1
        leftcandidate=-1
        while left<=right:
            mid=left+(right-left)//2
            if(target<nums[mid]):
                right=mid-1
            elif(nums[mid]<target):
                left=mid+1
            else:
                right=mid-1
                leftcandidate=mid
        mid = -1
        left = 0
        right = len(nums)-1
        rightcandidate = -1
        while left <= right:
            mid = left + (right - left) // 2
            if (target < nums[mid]):
                right = mid - 1
            elif (nums[mid] < target):
                left = mid + 1
            else:
                left=mid+1
                rightcandidate = mid
        return [leftcandidate,rightcandidate]

总结

1.数组的长度是length,但是数组是从零开始,使用数组的最后一个数字下标是length-1

2.可以先判断边界值和数组是否为空来优化算法

3.java创建数组是    int nums[]={1,2,3,4,5};

python是   nums=[1,2,3,4,5]

4.数组有可能为空,调用nums.length 会抛出异常,所以最开始要先判断是否为空(为空和数组长度为0是不一样的)(我的代码没有加入这个)。

在python,可以使用

if nums is None:
    return [-1,-1]

来判断是否为空

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值