【Leetcode刷题】数组2——二分查找

本篇文章为LeetCode数组模块关于二分查找内容的刷题笔记,仅供参考。

二分查找是一种低复杂度于解决有序数组的方法,主要的难点在于问题的终止条件,一旦处理不当极易陷入死循环。一般采用left、mid、right这3个指针,while(left<=right)作外循环,每次判断后进行赋值:left=mid+1right=mid-1,慎改变等号或者不作-1,否则容易陷入死循环。
设计二分算法的时候,先宏观设计总体算法,后考虑终止条件下的特殊情况。在考虑终止条件的时候,一般只取两个元素和一个元素的情况,判断如何正确跳出循环。

看到一篇关于二分边界的文章,需要的时候再回来看。

目录

  • Leetcode34.在排序数组中查找元素的第一个和最后一个位置
  • Leetcode35.搜索插入位置
  • Leetcode69.x的平方根
  • Leetcode33.搜索旋转排序数组
  • Leetcode81.搜索旋转排序数组 II
  • Leetcode153.寻找旋转排序数组中的最小值
  • Leetcode154.寻找旋转排序数组中的最小值 II
  • Leetcode162.寻找峰值
  • Leetcode74.搜索二维矩阵
  • Leetcode240.搜索二维矩阵 II

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

Leetcode34.在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

按照二分查找的思路,先根据left、mid、right三个指针不断二分确定某一个target的位置,然后再分别查找nums[left…mid]和nums[mid…right]两个部分的target开始位置和结束位置:

class Solution {
public:
    int searchleft(vector<int>& nums,int left,int right){ //寻找左区间
        int start=left;
        int end=right;
        int target=nums[right];
        int mid;
        while(start<=end){
            mid=(start+end)/2;
            if(nums[mid]==target){
                end=mid-1;
                if(end>=left&&nums[end]!=target)   return mid;
            }
            else{
                start=mid+1;
                if(start<=right&&nums[start]==target)   return start;
            }
        }
        if(start<=right&&nums[start]==target) return start; //一定要保证start范围
        else return right;
    }
    int searchright(vector<int>& nums,int left,int right){ //寻找右区间
        int start=left;
        int end=right;
        int target=nums[left];
        int mid;
        while(start<=end){
            mid=(start+end)/2;
            if(nums[mid]==target){
                start=mid+1;
                if(start<=right&&nums[start]!=target)   return mid;
            }
            else{
                end=mid-1;
                if(end>=left&&nums[end]==target)   return end;
            }
        }
        if(start<=right&&nums[start]==target) return start;
        else return right;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(nums[mid]==target){  //寻找左右区间
                int ans1=searchleft(nums,left,mid);
                int ans2=searchright(nums,mid,right);
                return {ans1,ans2};
            }
            else if(nums[mid]>target){
                right=mid-1;
            }
            else{
                left=mid+1;
            }
        }
        return {-1,-1};
    }
};
【Leetcode刷题】数组2——二分查找_第1张图片

需要注意的是,寻找左右区间时由于mid+1/mid-1有可能使得start、end超出left、right,需要判断,否则会溢出。

Leetcode35.搜索插入位置

Leetcode35.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为 无重复元素 的 升序 排列数组
-104 <= target <= 104

考虑一下4种情况:

  1. target在所有元素之前
  2. target在所有元素之后
  3. target为数组中某元素
  4. target插入数组中某一位置

情况3可以提前判断,情况1、2可归入4。下考虑终止情况:

(1)若最后left+1=right或left=right,且target=nums[left],则mid=left可直接返回
(2)若最后left+1=right,target=nums[right],则nums[mid] (3)若最后left+1=right,target (4)若最后left+1=right,target>nums[right],则left=mid+1=right,转入(5)或(6)
(5)若最后left=right,target (6)若最后left=right,target>nums[right],则right=mid+1>left循环终止,函数应返回left

综上所述,终止条件为left>right,函数返回值为left

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(nums[mid]==target)   return mid;
            else if(nums[mid]<target){
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        return left;
    }
};
【Leetcode刷题】数组2——二分查找_第2张图片

Leetcode69.x的平方根

Leetcode69.x的平方根
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
提示:
0 <= x <= 231 - 1

二分思路同Leetcode35.搜索插入位置,函数返回值为right

class Solution {
public:
    int mySqrt(int x) {
        int left=1;
        int right=x;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            long tmp=long(mid)*long(mid);
            if(tmp==x){
                return mid;
            }
            else if(tmp<x){
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        return right;
    }
};

然而该方法在最开始将mid*mid与x比较时相当于x2数量级,即264,即使使用long型也会溢出:

在这里插入图片描述
因此用mid和x/mid比较代替mid*mid和x比较

但计算mid=(left+right)/2又出现加法溢出的问题:

在这里插入图片描述
因此需要用long转变数据类型,即mid=((long)left+(long)right)/2;,AC代码如下:

class Solution {
public:
    int mySqrt(int x) {
        int left=1;
        int right=x;
        int mid;
        while(left<=right){
            mid=((long)left+(long)right)/2;
            if(mid==x/mid){     //除法代替乘法以防止溢出
                return mid;
            }
            else if(mid<x/mid){
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        return right;
    }
};
【Leetcode刷题】数组2——二分查找_第3张图片

Leetcode33.搜索旋转排序数组

Leetcode33.搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104

这题显然是二分查找,因为题干中旋转数组的旋转点k的取值是0<=k需要考虑nums是否是旋转数组:若函数传入的数组满足nums[left]>nums[right],则为旋转数组,否则是顺序数组。
对于旋转数组,有以下两种分布:

【Leetcode刷题】数组2——二分查找_第4张图片

对应target位置的4种情况:

if(nums[left]<nums[mid]){
	if(nums[left]<=target<=nums[mid])	search(nums[left...mid]);
	else	search(nums[mid...right]);
}
else{
	if(nums[mid]<=target<=nums[right])	search(nums[mid...right]);
	else	search(nums[left...mid]);
}

因为题目给定函数形式,不便于递归,因此此类二分查找的题目尽量采用while循环

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){         //left=right时也需要判断
            mid=(left+right)/2;
            //特殊处理防止遗漏
            if(nums[left]==target)   return left;
            else if(nums[mid]==target)   return mid;
            else if(nums[right]==target)   return right;
            else if(nums[left]<nums[mid]){
                if(nums[left]<=target && target<nums[mid]){
                    right=mid-1;    //不直接取mid以防陷入死循环
                }
                else{
                    left=mid+1;
                }
            }
            else{
                if(nums[mid]<target && target<=nums[right]){
                    left=mid+1;
                }
                else{
                    right=mid-1;
                }
            }
        }
        return -1;
    }
};
【Leetcode刷题】数组2——二分查找_第5张图片

更为规范的写法如下,不必每次循环都判断nums[mid]、nums[left]、nums[right],只用最后跳出循环时判断left和right处是否有target:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(nums[mid]==target)   return mid;
            else if(nums[left]<=nums[mid]){ //相等时应归入此类
                if(nums[left]<=target && target<nums[mid]){
                    right=mid-1;
                }
                else{
                    left=mid+1;
                }
            }
            else{
                if(nums[mid]<target && target<=nums[right]){
                    left=mid+1;
                }
                else{
                    right=mid-1;
                }
            }
        }
        if(left<nums.size()&&nums[left]==target)    return left;
        else if(right>=0&&nums[right]==target)      return right;
        else    return -1;
    }
};

上述代码还有一个改动就是在判断哪种旋转数组else if(nums[left]<=nums[mid])处加入了等号,以保证类似{3,1}的样例取到正确结果:

【Leetcode刷题】数组2——二分查找_第6张图片

Leetcode81.搜索旋转排序数组 II

Leetcode81.搜索旋转排序数组 II
已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
你必须尽可能减少整个操作步骤。
示例 1:
输入:nums = [2,5,6,0,0,1,2], target = 0
输出:true
示例 2:
输入:nums = [2,5,6,0,0,1,2], target = 3
输出:false
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104

思路同Leetcode33.搜索旋转排序数组,但本题数组内元素可以重复,因此需要解决类似{3,3,3,1,2,3,3,3,3,3,3}和{3,3,3,3,3,3,3,1,2,3,3}的问题,即nums[left]==nums[mid] && nums[mid]==nums[right],若出现此情况,别无他法,只能缩短搜索区间继续执行:

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(nums[mid]==target){
                return true;
            }
            else if(nums[left]==nums[mid] && nums[mid]==nums[right]){
            //类似{3,3,3,1,2,3,3,3,3,3,3}和{3,3,3,3,3,3,3,1,2,3,3}的情况无法判断旋转点的位置
                left++;
                right--;
            }
            else if(nums[left]<=nums[mid]){
                if(nums[left]<=target && target<nums[mid]){
                    right=mid-1;
                }
                else{
                    left=mid+1;
                }
            }
            else{
                if(nums[mid]<target && target<=nums[right]){
                    left=mid+1;
                }
                else{
                    right=mid-1;
                }
            }
        }
        if(left<nums.size()&&nums[left]==target)    return true;
        else if(right>=0&&nums[right]==target)      return true;
        else    return false;
    }
};
【Leetcode刷题】数组2——二分查找_第7张图片

Leetcode153.寻找旋转排序数组中的最小值

Leetcode153.寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
示例 3:
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 中的所有整数 互不相同
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

每次循环前先判断是否是顺序数组if(nums[left]<=nums[right]),若顺序则可以直接返回nums[left];若不是则二分缩短区间。值得注意的是,在nums[left]>nums[mid]的情况下,right不能取mid-1而只能取mid,否则容易略过最小值:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(nums[left]<=nums[right]) return nums[left];  //顺序数组
            else if(nums[left]<=nums[mid]){
                left=mid+1;
            }
            else{
                right=mid;      //不能取mid-1否则容易错过最小值
            }
        }
        if(left>=nums.size())    return nums[right];
        else if(right<0)      return nums[left];
        else if(nums[left]<=nums[right])    return nums[left];
        else    return nums[right];
    }
};
【Leetcode刷题】数组2——二分查找_第8张图片

Leetcode154.寻找旋转排序数组中的最小值 II

Leetcode154.寻找旋转排序数组中的最小值 II
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须尽可能减少整个过程的操作步骤。
示例 1:
输入:nums = [1,3,5]
输出:1
示例 2:
输入:nums = [2,2,2,0,1]
输出:0
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

思路同 Leetcode81.搜索旋转排序数组 II 和 Leetcode153.寻找旋转排序数组中的最小值:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<right){      //left=right时可以直接跳出
            mid=(left+right)/2;
            if(nums[left]<nums[right]){
                return nums[left];
            }
            else if(nums[left]==nums[mid] && nums[mid]==nums[right]){
            //类似{3,3,3,1,2,3,3,3,3,3,3}和{3,3,3,3,3,3,3,1,2,3,3}的情况无法判断旋转点的位置
                left++;
                right--;
            }
            else if(nums[left]<=nums[mid]){
                left=mid+1;
            }
            else{
                right=mid;
            }
        }
        if(left>=nums.size())    return nums[right];
        else if(right<0)      return nums[left];
        else if(nums[left]<=nums[right])    return nums[left];
        else    return nums[right];
    }
};

但需要将终止条件改为left>right,因为left=right时需要跳出,否则left++、right–可能出现段错误(如样例:{1} )。

【Leetcode刷题】数组2——二分查找_第9张图片

Leetcode162.寻找峰值

Leetcode162.寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。
提示:
1 <= nums.length <= 1000
-231 <= nums[i] <= 231 - 1
对于所有有效的 i 都有 nums[i] != nums[i +1]

因为题干中说nums[-1]=nums[n]=-∞,所以nums中必定存在峰值。循环过程中比较nums[mid]和nums[mid+1],取大的所在那一部分不断二分,一定可以找到某一个峰值。因为mid=(left+right)/2,所以mid+1一定小于等于right,即不会超出范围。

循环进行到left和right相差1时就可以停止了,返回数值较大的位置。但为了避免只有1个元素的数组样例,还需要判断left==right的情况:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;     //mid+1<=right显然成立
            if(left+1==right){
                if(nums[left]<nums[right])  return right;
                else    return left;
            }
            else if(left==right)    return left;    //防止n=1
            else if(nums[mid]<nums[mid+1]){
                if(mid+1==nums.size()-1||nums[mid+1]>nums[mid+2]){
                    return mid+1;
                }
                else{
                    left=mid+1;
                }
            }
            else{
                if(mid==0||nums[mid]>nums[mid-1]){
                    return mid;
                }
                else{
                    right=mid;
                }
            }
        }
        return -1;
    }
};
【Leetcode刷题】数组2——二分查找_第10张图片

Leetcode74.搜索二维矩阵

Leetcode74.搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
提示:
m = matrix.length
n = matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104

先搜索行首元素确定target所在行,再搜索该行确定是否在矩阵中。注意搜索行结束后所在行取high,但需要判断high<0的情况(如:{{1,2}},0):

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int low=0;
        int high=matrix.size()-1;
        int level;
        while(low<=high){
            level=(low+high)/2;
            if(matrix[level][0]==target)    return true;
            else if(matrix[level][0]<target){
                low=level+1;
            }
            else{
                high=level-1;
            }
        }
        //此时high即为下面搜索的行号
        if(high<0)  return false;   //防止{{1}}的情况
        vector<int> v=matrix[high];
        int left=0;
        int right=v.size()-1;
        int mid;
        while(left<=right){
            mid=(left+right)/2;
            if(v[mid]==target)  return true;
            else if(v[mid]<target)  left=mid+1;
            else                    right=mid-1;
        }
        return false;
    }
};
【Leetcode刷题】数组2——二分查找_第11张图片

Leetcode240.搜索二维矩阵 II

Leetcode240.搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
示例 2:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false
提示:
m = matrix.length
n = matrix[i].length
1 <= n, m <= 300
-109 <= matrix[i][j] <= 109
每行的所有元素从左到右升序排列
每列的所有元素从上到下升序排列
-109 <= target <= 109

本题的矩阵具有排序上的特殊性,每行从左到右、每列从上到下、主对角线从左上到右下上的元素都是升序排列。一开始的思路是遍历主对角线,确定target范围[loc-1,loc],再在剩下的两块方形区域中(即图中阴影部分)遍历搜索。若loc没有被赋值,即target大于matrix[min{m-1,n-1}][min{m-1,n-1}],还有可能是矩阵非方阵,在多出来的长方形中搜索。搜索思路如图:

【Leetcode刷题】数组2——二分查找_第12张图片
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int loc=-1;
        int m=matrix.size();
        int n=matrix[0].size();
        if(matrix[0][0]>target || matrix[m-1][n-1]<target)   return false;
        else if(matrix[0][0]==target || matrix[m-1][n-1]==target)   return true;
        for(int i=1;i<m && i<n;i++){    //搜索位置
            if(matrix[i-1][i-1]==target || matrix[i][i]==target)    return true;
            else if(matrix[i-1][i-1]<target && matrix[i][i]>target){
                loc=i;
                break;
            }
        }
        //搜索完target在主对角线的位置
        if(loc>=0){
            for(int i=0;i<loc;i++){
                for(int j=loc;j<n;j++){
                    if(matrix[i][j]==target)    return true;
                }
            }
            for(int i=loc;i<m;i++){
                for(int j=0;j<loc;j++){
                    if(matrix[i][j]==target)    return true;
                }
            }    
        }
        //非方阵会出现遗漏
        if(loc==-1){    //matrix[min{m-1,n-1}][min{m-1,n-1}]
            if(m>n){    //下方部分未搜索
                for(int i=n;i<m;i++){
                    for(int j=0;j<n;j++){
                        if(matrix[i][j]==target)    return true;
                    }
                }
            }
            if(m<n){    //右方部分未搜索
                for(int i=0;i<m;i++){
                    for(int j=m;j<n;j++){
                        if(matrix[i][j]==target)    return true;
                    }
                }
            }
        }
        return false;
    }
};
【Leetcode刷题】数组2——二分查找_第13张图片

该思路也能很艰难的通过,,,下面根据官方解答重新编写。

Z字形查找
我们可以从矩阵matrix的右上角 matrix[0][n−1] 进行搜索。在每一步的搜索过程中,如果我们位于位置 matrix[x][y],那么我们希望在以 matrix[m-1][0] 为左下角、以 matrix[x][y] 为右上角的矩阵中进行搜索,即行的范围为 [x,m−1],列的范围为 [0,y]:

  1. 如果 matrix[x][y] = targe,说明搜索完成;
  2. 如果 matrix[x][y] > target,由于每一列的元素都是升序排列的,那么在当前的搜索矩阵中,所有位于第 y 列的元素都是严格大于 target 的,因此我们可以将它们全部忽略,即将 y 减少 1;
  3. 如果 matrix[x][y] < target,由于每一行的元素都是升序排列的,那么在当前的搜索矩阵中,所有位于第 x 行的元素都是严格小于 target 的,因此我们可以将它们全部忽略,即将 x 增加 1。

在搜索的过程中,如果我们超出了矩阵的边界,那么说明矩阵中不存在 target:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size();
        int n=matrix[0].size();
        int curx=0;
        int cury=n-1;
        while(curx<=m-1 && cury>=0){
            if(matrix[curx][cury]==target){
                return true;
            }
            else if(matrix[curx][cury]>target){
                cury--;
            }
            else{
                curx++;
            }
        }
        return false;
    }
};
【Leetcode刷题】数组2——二分查找_第14张图片

官方解答巧妙将大问题划小,并且小问题与原问题具有相同性质与形状。通过x- -和y++不断缩小搜索范围,最终得到结果。

你可能感兴趣的:(LeetCode刷题,leetcode,算法)