数组理论基础:二分查找(6月5日-6月6日)

93 阅读4分钟

数据理论基础:二分查找

注意点:

1:数组下标都是从0开始的
2:数组内存空间地址是连续的
3:数组元祖不能删,只能覆盖
4:对于二维数组来说,地址是连续的吗?不同编程语言的内存管理是不一样的。 对于JAVA来说,地址空间不是连续的,对于C++来说,地址空间是连续的

题目

1:二分查找

leetcode.cn/problems/bi…
两种情况:左闭右闭,左闭右开 \

情况1:左闭右闭:

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

情况2:左闭右开

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        
        while(left < right) //左闭右开[left, right) 这里不一样
        {
            int middle = left + ((right -left) >> 1);
            if(target < nums[middle]){
                right = middle ;  //这里不一样
            }
            else if(target > nums[middle]){
                left = middle + 1;
            }
            else{
                return middle;
            }
        }
        return -1;
    }
}

情况3:左开右闭

class Solution {
    public int search(int[] nums, int target) {
        int left = -1;
        int right = nums.length -1;
        
        while(left + 1 < right) //左闭右开[left, right) 这里不一样
        {
            int middle = left + ((right -left) >> 1);
            if(target < nums[middle]){
                right = middle -1;  //这里不一样
            }
            else if(target > nums[middle]){
                left = middle ;
            }
            else{
                return middle;
            }
        }
        return -1;
    }

*这里还有问题,不太理解什么情况*

总结:
1:使用二分查找的前提条件有两个:有序数组 无重复元素

2:注意区间,一般情况下用左闭右闭或者左闭右开
3:时间复杂度:O(logn),空间复杂度O(1)

6月5日打卡完毕



相关题目:\

1:搜索插入位置

leetcode.cn/problems/se… \

首先查看所有情况:

  • 目标值在数组所有元素之前
  • 目标值等于数组中某一个元素
  • 目标值插入数组中的位置
  • 目标值在数组所有元素之后

即要解决这几个情况。

每次做算法题应该首先分析会有哪些情况,再根据情况去思考解题!!!

题解:

方法一:暴力法:

class Solution {
    public int searchInsert(int[] nums, int target) {
        //暴力法:从左往右找,若target小于于于等于则返回下标,若找到末尾还没找到则返回数组长度
        int l = nums.length;
        for(int i =0;i < l;i++){
            if(target <= nums[i]){
                return i;
            }
        }
        return l;
    }

}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

方法二:二分法(左闭右闭或左闭右开)

//左闭右闭
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length -1 ;
        int result = 0;
        while(left <= right){
            int middle = left + ((right -left) >> 1);
            if(target < nums[middle]){
                right = middle - 1;
            }
            else if(target > nums[middle]){
                left = middle + 1;
            }
            else if(target == nums[middle]){
                return middle;
            }
        }
        return right + 1;
    }
    // 1:l = 0  r = 3 mid = 1 r = 1-1 = 0
    //2: l = 0 r = 0 mid = 0 num[mid]=1
}
//左闭右开
class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length ; //这里有变化
        int result = 0;
        while(left < right){  //这里有变化
            int middle = left + ((right -left) >> 1);
            if(target < nums[middle]){
                right = middle;  //这里有变化
            }
            else if(target > nums[middle]){
                left = middle + 1;
            }
            else if(target == nums[middle]){
                return middle;
            }
        }
        return right;  //这里有变化
    }
    // 1:l = 0  r = 3 mid = 1 r = 1-1 = 0
    //2: l = 0 r = 0 mid = 0 num[mid]=1
}

`` 时间复杂度:O(logn),空间复杂度O(1)

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

leetcode.cn/problems/fi… \

class Solution {
    public int[] searchRange(int[] nums, int target) {
        /*分三种情况:
        1:target不在nums的范围中
        2:target在nums的范围中,但是并没有在nums中出现
        3:target在nums的范围中,并且在nums中出现 */
        int leftBorder = getleftBorder(nums,target);
        int rightBorder = getRightBorder(nums,target);
        if(leftBorder == -2 || rightBorder == -2){
            return new int[]{-1,-1};
        }
        if(rightBorder - leftBorder > 1){
            return new int[]{leftBorder + 1, rightBorder - 1};
        }
        else{
            return new int[]{-1,-1};
        }
         
    }

    //查找右边界,这里移动left向右查找
    int getRightBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int rightBorder = -2;
        while(left <= right){
            int middle = left + ((right -left) >> 1);
            if(target < nums[middle]){
                right = middle - 1;
            }
            else{  //>= 当等于的时候更新border,最后大于的时候再更新一次border,注意这时候border不包含target
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }

    //查找左边界,这里移动right向左查找
    int getleftBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int leftBorder = -2;
        while(left <= right){
            int middle = left + ((right -left) >> 1);
            if(target <= nums[middle]){//<= 当等于的时候更新border,最后小于于的时候再更新一次border,注意这时候border不包含target
                right = middle - 1;
                leftBorder = right;
            }
            else{  
                left = middle + 1;
            }
        }
        return leftBorder;
    }

}

本体还有优化空间,二刷的时候再看看

3:X的平方根

leetcode.cn/problems/sq…

class Solution {
    //思路和找边界是一样的,即用left去寻找右边界
    public int mySqrt(int x) {
        int left = 0;
        int right = x;
        int result = -1;
        while(left <= right){
            int middle = left + ((right - left) >> 1);
             long sq = (long)middle * middle;
            if(sq <= x){       //如果sq小于x,则要增大sq
                left = middle + 1;
                result = middle;
            }
            else{
                right = middle - 1;
            }


        }
        return result;
    }
}

注这里需要注意隐式转换问题,已经总结在问题中
juejin.cn/post/737722…

4:有效的完全平方数

leetcode.cn/problems/va…

class Solution {
    public boolean isPerfectSquare(int num) {
        int left = 0;
        int right = num;
        boolean result = false;
        while(left <= right){
            int middle = left + ((right - left) >> 1);
            long sq = (long)middle * middle;
            if(sq < num){
                left = middle + 1;
            }
            else if(sq > num){
                right = middle - 1;
            }
            else if(sq == num){
                result = true;
                break;
            }
        }
        return result;
    }
}