LeetCode刷题(四)——二分搜索

Day6 2020.07.22&&2020.07.23

1.search-for-a-range

给出一个有序数组,请在数组中找出目标值的起始位置和结束位置,你的算法的时间复杂度应该在O(log n)之内,如果数组中不存在目标,返回[-1, -1].
例如:给出的数组是[5, 7, 7, 8, 8, 10],目标值是8,返回[3, 4].
解题思路:题目要求 O(log n),找到目标值然后前后扫描,当遇到全部一样的数字不符合时间复杂度要求了。可以进行两次二分查找,查找的过程中每一次找到一个边界。

class Solution {
public:
    vector<int> searchRange(int* A, int n, int target) {
        vector<int> result={-1,-1};
        int low1=0,high1=n-1;
        //查找右边界
        while(low1<=high1){
            int mid1=(high1+low1)/2;
            if(A[mid1]<=target)
                low1=mid1+1;
            else
                high1=mid1-1;
        }
        int low2=0,high2=n-1;
        while(low2<=high2){
            int mid2=(low2+high2)/2;
            if(A[mid2]>=target)
                high2=mid2-1;
            else
                low2=mid2+1;
        }
        if(low2<=high1){
            result[0]=low2;
            result[1]=high1;
        }
        return result;
    }
};
2.search-insert-position

给出一个有序的数组和一个目标值,如果数组中存在该目标值,则返回该目标值的下标。如果数组中不存在该目标值,则返回如果将该目标值插入这个数组应该插入的位置的下标,假设数组中没有重复项。
下面给出几个样例:
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0

class Solution {
public:
    int searchInsert(int* A, int n, int target) {
        if(n==0) return 0;
        for(int i=0;i<n;i++){
            if(A[i]>=target)
                return i;
        }
    }
};
3.search-a-2d-matrix

请写出一个高效的在m*n矩阵中判断目标值是否存在的算法,矩阵具有如下特征:
每一行的数字都从左到右排序
每一行的第一个数字都比上一行最后一个数字大
例如对于下面的矩阵:
[↵ [1, 3, 5, 7],↵ [10, 11, 16, 20],↵ [23, 30, 34, 50]↵]
要搜索的目标值为3,返回true;
解题思路:两次折半查找,第一次是在最后列中查找,第二次是在第一次查找到的列数对应的行中查找。

class Solution {
public:
    bool searchMatrix(vector<vector<int> >& matrix, int target) {
        if(matrix.empty()) return false;
        int size_row=matrix.size();
        int size_col=matrix[0].size();
        //折半查找
        int low=0,high=size_row-1,mid;
        while(low<=high){
            mid=(low+high)/2;
            if(matrix[mid][size_col-1]==target) return true;
            else if(matrix[mid][size_col-1]<target) low=mid+1;
            else high=mid-1;
        }
        //再在matrix[mid][]这一行中查找
        int tag;
        if(mid>=0&&mid<size_row) tag=mid;
        else return false;
        low=0; high=size_col-1;
        while(low<=high){
            mid=(low+high)/2;
            if(matrix[tag][mid]==target) return true;
            else if(matrix[tag][mid]<target) low=mid+1;
            else high=mid-1;
        }
        return false;
    }
};
4.first-bad-version

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
解题思路:因为这题中的二分查找是找到边界,所以循环跳出的条件是low==high,并且在查找过程中mid=low+(high-low)/2,high=mid。

class Solution {
public:
    int firstBadVersion(int n) {
        int low=1,high=n;
        //不能写 int mid = (lo + hi) / 2; 要写 int mid = lo + (hi - lo) / 2;
        while(low<high){
            int mid=low+(high-low)/2;
            if(isBadVersion(mid)==false) low=mid+1;
            else high=mid; //且此处是high=mid,因为此时的版本可能是第一个错误版本
        }
        //循环结束的条件是low==high
        return low;
    }
};
5.find-minimum-in-rotated-sorted-array

假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。你可以假设数组中不存在重复元素。
示例 1:输入: [3,4,5,1,2] 输出: 1
示例 2:输入: [4,5,6,7,0,1,2]输出: 0

//时间复杂度为O(n)
class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.empty()) return -1;
        for(int i=0;i<nums.size()-1;i++){
            if(nums[i]>nums[i+1]) return nums[i+1];
        }
        return nums[0];
    }
};

解题思路:leetcode上的,和first-bad-version一样,寻找边界问题。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low=0,high=nums.size()-1;
        while(low<high){
            int mid=low+(high-low)/2;
            if(nums[mid]<nums[high]){
                high=mid;
            }else{
                low=mid+1;
            }
        }
        return nums[low];
    }
};
6.find-minimum-in-rotated-sorted-array-ii

注意数组中可能存在重复的元素。
同find-minimum-in-sorted-array第一种解法一样,复杂度还是O(n)。
第二种解法:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low=0,high=nums.size()-1;
        while(low<high){
            int mid=low+(high-low)/2;
            if(nums[mid]>nums[high]){
                low=mid+1;
            }else if(nums[mid]<nums[high]){
                high=mid;
            }else high--;//要考虑重复数字即mid元素等于high元素的情况
        }
        return nums[low];
    }
};
7.search-in-rotated-sorted-array

给出一个转动过的有序数组,你事先不知道该数组转动了多少
(例如,0 1 2 4 5 6 7可能变为4 5 6 7 0 1 2).在数组中搜索给出的目标值,如果能在数组中找到,返回它的索引,否则返回-1。
假设数组中不存在重复项。
== 解题思路:还是用二分法,但是移动的时候在判定语句中加上条件,判断目标值所在的区间。==

class Solution {
public:
    int search(int* A, int n, int target) {
        if(n==0) return -1;
        int low=0,high=n-1;
        while(low<=high){
            int mid=low+(high-low)/2;
            if(target==A[mid]) return mid;
            //前半部分是从小到大  target在前半部分
            if(A[low]<=A[mid]&&(A[low]<=target&&target<A[mid])){
               high=mid-1;
            }else if(A[mid]<=A[high]&&!(A[mid]<target&&target<=A[high])){
                high=mid-1;
            }else
                low=mid+1;
        }
        return -1;
    }
};
8.search-in-rotated-sorted-array-ii

继续思考题目 “Search in Rotated Sorted Array”:
如果数组种允许有重复元素怎么办?
会影响时间复杂度吗?是怎样影响时间复杂度的,为什么?
编写一个函数判断给定目标值是否在数组中。

class Solution {
public:
    bool search(int* A, int n, int target) {
        if(n==0) return false;
        int low=0,high=n-1;
        while(low<=high){
            int mid=(low+high)/2;
            if(target==A[mid]) return true;
            else if(A[mid]>A[low]){
                if(A[low<=target]&&target<=A[mid]) high=mid-1;
                else low=mid+1;
            }else if(A[low]==A[mid]){
                low++;
            }else{
                if(A[mid]<=target&&target<=A[high]) low=mid+1;
                else high=mid-1;
            }
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值