8. <tag-数组和二分查找_1>-lt.704-二分查找 + lt.33-搜索旋转排序数组 +lt.34-在排序数组中查找元素的第一个和最后一个位置 + lt.35-搜索插入位置 2.3

lt.704 二分查找

[案例需求]
在这里插入图片描述

[思路分析]

  1. 二分法典型例题, 有什么好说的, 我秒了(误)
  2. 口误啊口误, 可不敢说秒二分. 本题是二分法的标准入门题, 本意还是通过二分查找值, 是前面讲过的二分查找的第一个模板, 在这里就不讲太多了, 好好看下前面文章哈.

[代码实现]

	class Solution {
    public int search(int[] nums, int target) {
        // // 左开右闭

        // int left = 0;
        // int right = nums.length;

        // while(left < right){

        //     int mid = left + (right - left)/2;
            
        //     if(nums[mid] == target){
        //         return mid;
        //     }else if(nums[mid] < target){
        //         left = mid + 1;
        //     }else if(nums[mid] > target){
        //         right = mid;
        //     }
        // }

        //二分查找, 左闭右闭
        int left = 0;
        int right = nums.length - 1;
		
        while(left <= right){
            int mid = left + (right - left)/2;
            
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }
        }
        return -1;
    }
}

lt.33-搜索旋转排序数组

[案例需求]
在这里插入图片描述

[思路分析]

将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。
此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.
在这里插入图片描述

[代码实现]

class Solution {
    public int search(int[] nums, int target) {
        //数组局部有序 + 不重复, 考虑用二分查找
        
        int left = 0;
        int right = nums.length - 1;

        
        while(left <= right){
            int mid = left + (right - left)/2;

            //跳出遍历的出口
            if(nums[mid] == target) return mid;


            //遍历部分有序的序列, 对这部分有序的序列进行二分查找

            //在有序的左半段(nums[left] < nums[mid])
            if(nums[left] <= nums[mid]){
                
                //缩小左半段的边界(左半段的左边界), 去除组织这个序列单调的数
                if(nums[left] <= target && nums[mid] > target){
                    right = mid - 1; //左半段的单调序列
                }else{
                    left = mid + 1; //右半段的单调序列
                }
                
            }else{ //右半段有序的序列

                //缩小右边届的左半段
                if(nums[mid] < target && nums[right]  >= target){
                    left = mid + 1;	//右半段的单调序列
                }else{
                    right = mid - 1;//左半段的单调序列
                }
            }
        }
        return -1;
    }
}

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

[案例需求]
在这里插入图片描述

[思路分析]

  1. 一个二分方法向左查找第一个位置
  2. 另一个二分查找向右查找第二个位置
  3. 根据题意, 在找到nums[mid] == target位置时,应该继续向左或向右移动指针, 而不是直接返回mid了

如何判断和移动是比较需要记住的一点:

这里我们拿向左寻找第一个target, 并找到的条件下 作为示例:

if(nums[mid] == target){
    if(mid == 0 || nums[mid] != nums[mid - 1]){ //mid到达了序列的第一个数, 或者前面已经没有相同的数
        return mid; // mid一定存在, 因为上面的判断条件是 nums[mid] == target!!!
    }else{
        mid = mid - 1;
    }
}

[代码实现]

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftIndex = findLeft(nums, target);
        int rightIndex = findRight(nums, target);
        return new int[]{leftIndex, rightIndex};
    }

    public int findLeft(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int leftIndex = -1;

        while(left <= right){
            int mid = left + ((right - left) >> 1);

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



    public int findRight(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int rightIndex = -1;

        while(left <= right){
            int mid = left + ((right - left) >> 1);

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

原先的解法:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        // 要求 o(logn), 并且是有序序列, 二分法走起
        
        //特例
        if(nums.length == 0) return new int[]{-1,-1};        
        //左边界和右边界, 
        return new int[]{bsfirst(nums, target), bslast(nums, target)};
    }

    public int bsfirst(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;

        while(left <= right){
            int mid = left + ((right - left) >> 1);

            if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else if(nums[mid] == target){
                ///
                if(mid == 0 || nums[mid - 1] != target){
                    return mid;
                }else{
                    right = mid - 1;
                }
            }
        }

        return -1;
    }

    public int bslast(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;

        while(left <= right){
            
            int mid = left + ((right - left) /2);

            if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else if(nums[mid] == target){
                if(mid == nums.length - 1 || nums[mid + 1] != target){
                    return mid;
                }else{
                    left = mid + 1;
                }
            }
        }
           return -1;
    }
 
}

[思路分析二, 一次遍历法]

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int len = nums.length;
        int left = 0;
        int right = len - 1;
        

        while(left <= right){
            
            int mid = left + ((right - left) >> 1);
          
            if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                //1. 寻找左边第一个的前一个
                int L = mid - 1;
                while(L >= 0 && nums[L] == target){--L;}

                //2. 寻找右边第一个的后一个
                int R = mid + 1;
                while(R < len && nums[R] == target){++R;}

                return new int[]{L + 1, R - 1};
            }
            
        }

        return new int[]{-1, -1};
    }
}

lt.35-搜索插入位置

[案例需求]
在这里插入图片描述

[思路分析]

  1. 比较常规的二分查找, 找到目标值直接返回索引即可;
  2. 未找到目标值时, 此时 left > right ( left + 1 = right), 由于题目要求返回的是目标值应该插入的位置, 而我们在比较nums[mid] 和 target值时, 最后一次比较应该是 left == right的时候;
  3. 而最后一次while(left == right)内的比较都没有找到, 在这最后一次比较完成后, left > right, 所以要求的返回target值应该插入的位置 (即第一个比target值大的值所在的索引), 理应返回的是left 而不是right;
    [代码实现]
class Solution {
    public int searchInsert(int[] nums, int target) {
    //     //logn 二分
    //     int left = 0;
    //     int right = nums.length - 1;
    //     int mid = 0;

    //     while(left <= right){
    //         mid = left + (right - left)/2;

    //         if(nums[mid] == target){
    //             return mid;
    //         }else if(nums[mid] > target){
    //             right = mid - 1;
    //         }else if(nums[mid] < target){
    //             left = mid + 1;
    //         }
    //     }

    //     return left;
    // }
        int left = 0;
        int right = nums.length -1;

        while(left <= right){
            int mid = left + ((right - left) /2);

            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }
    }
        return left;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值