leetcode算法——二分查找

二分查找

单纯的二分查找系列
基本思路
  • 就是单纯的二分查找,但要注意可能存在的变形情况
74. 搜索二维矩阵
  • https://leetcode-cn.com/problems/search-a-2d-matrix/

  • class Solution {
    public:
        bool searchMatrix(vector<vector<int>>& matrix, int target) {
            int m = matrix.size();
            int n = matrix[0].size();
            int l = 0;
            int r = m * n - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                int i = mid / n;
                int j = mid % n;
                if (matrix[i][j] == target) return true;
                if (matrix[i][j] < target) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
            return false;
        }
    };
    
240. 搜索二维矩阵 II
  • https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

  • 可以对每行进行二分查找,但一般这道题,是从右上方开始找

  • class Solution {
    public:
        bool searchMatrix(vector<vector<int>>& matrix, int target) {
            int i = 0;
            int j = matrix[0].size() - 1;
            while (i < matrix.size() && j >= 0) {
                if (matrix[i][j] == target) return true;
                if (matrix[i][j] < target) {
                    i++;
                } else {
                    j--;
                }
            }
            return false;
        }
    };
    
旋转排序数组系列
基本思路
  • 二分,然后判断左边有序还是右边有序
  • 如果数组内元素不重复,那么当nums[mid] >= nums[left]时,就说明左侧有序,否则右侧有序。存在等于是由于midleft可能相同
  • 如果数组元素可以重复时
    • nums[mid] == nums[left]时,无法判断左侧是否有序,可能有序也可能无序,此时需要将left++,然后在判断
    • nums[mid] > nums[left]时,左侧一定有序
    • nums[mid] < nums[left]时,右侧一定有序
33. 搜索旋转排序数组
  • https://leetcode-cn.com/problems/search-in-rotated-sorted-array/

  • 首先判断当前位置是否是目标值。不是则由于排序数组数字不重复,那么就先判断哪边有序,然后在判断target是否在有序的那边,是就对有序的那边二分,不是就对另一边二分

  • class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int l = 0;
            int r = nums.size() - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (target == nums[mid]) {
                    return mid;
                }
                if (nums[mid] >= nums[l]) {
                    if (target >= nums[l] && target < nums[mid]) {
                        r = mid - 1;
                    } else {
                        l = mid + 1;
                    }
                } else {
                    if (target > nums[mid] && target <= nums[r]) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
            }
            return -1;
        }
    };
    
81. 搜索旋转排序数组 II
  • https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/

  • 首先判断当前位置是否是目标值。不是则由于排序数组数字可以重复,那么当nums[mid] == nums[left]时,无法判断出哪边有序,所以让left++然后在二分判断。判断哪边有序,target是否在有序集合里。在,二分有序集合,不在二分另一边

  • class Solution {
    public:
        bool search(vector<int>& nums, int target) {
            int l = 0;
            int r = nums.size() - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (target == nums[mid]) return true;
                
                if (nums[mid] == nums[l]) {
                    l++;
                } else if (nums[mid] > nums[l]) {
                    if (target >= nums[l] && target < nums[mid]) {
                        r = mid - 1;
                    } else {
                        l = mid + 1;
                    }
                } else {
                    if (target > nums[mid] && target <= nums[r]) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
            }
            return false;
        }
    };
    
153. 寻找旋转排序数组中的最小值
  • https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/

  • 数组元素不重复,所以当nums[mid] >= nums[left]时,左侧有序,那么就记录下此时最小值和nums[left]的更小值,然后去搜索另一个区间的最小值

  • class Solution {
    public:
        int findMin(vector<int>& nums) {
            int l = 0;
            int r = nums.size() - 1;
            int result = nums[0];
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] >= nums[l]) {
                    result = min(result, nums[l]);
                    l = mid + 1;
                } else {
                    result = min(result, nums[mid]);
                    r = mid - 1;
                }
            }
            return result;
        }
    };
    
154. 寻找旋转排序数组中的最小值 II
  • https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/

  • 数组元素重复,所以当nums[mid] == nums[left]时,无法判断哪边有序,此时将left++在二分判断。

  • 左侧有序,那么就记录下此时最小值和nums[left]的更小值,然后去搜索另一个区间的最小值。

  • class Solution {
    public:
        int findMin(vector<int>& nums) {
            int l = 0;
            int r = nums.size() - 1;
            int result = nums[0];
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] == nums[l]) {
                    result = min(result, nums[l]);
                    l++;
                } else if (nums[mid] > nums[l]) {
                    result = min(result, nums[l]);
                    l = mid + 1;
                } else {
                    result = min(result, nums[mid]);
                    r = mid - 1;
                }
            }
            return result;
        }
    };
    
大于等于target的最左侧位置或小于等于target的最右侧位置系列
基本思路
  • 在一个有序数组中找大于等于target的最左侧位置,就是当前mid位置值大于等于target的话,我仍然继续二分左侧区间,在最后退出条件后,left的位置就是要找的位置。如果说所有的数都小于target,则left值就是nums.size()
  • 在一个有序数组中找小于等于target的最右侧位置,就是当前mid位置值小于等于target的话,我仍然继续二分右侧区间,在最后退出条件后,right的位置就是要找的位置。如果说所有的数都大于target,则right值就是-1
  • 当然也可以变形,比如说大于target的最左位置,小于target的最右位置
35. 搜索插入位置
  • 其实就是找大于等于target的最左侧位置

  • class Solution {
    public:
        int searchInsert(vector<int>& nums, int target) {
            int l = 0;
            int r = nums.size() - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] >= target) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            return l;
        }
    };
    
34. 在排序数组中查找元素的第一个和最后一个位置
  • 就是两个都找

  • class Solution {
    public:
        vector<int> searchRange(vector<int>& nums, int target) {
            int l = 0;
            int r = nums.size() - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] >= target) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            if (l >= nums.size() || nums[l] != target) return {-1, -1};
            int left = l;
            l = 0;
            r = nums.size() - 1;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] <= target) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
            return {left, r};
        }
    };
    
69. x 的平方根
  • 其实就是找小于等于target的最右侧位置

  • class Solution {
    public:
        int mySqrt(int x) {
            int l = 0;
            int r = x;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if ((long)mid * (long)mid <= x) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
            return r;
        }
    };
    
寻找峰值系列
852. 山脉数组的峰顶索引
  • 寻找峰值,可以从left = 1, right = nums.size() - 2开始二分,防止出现数组越界

  • class Solution {
    public:
        int peakIndexInMountainArray(vector<int>& arr) {
            int l = 1;
            int r = arr.size() - 2;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) return mid;
                if (arr[mid] < arr[mid - 1]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            return -1;
        }
    };
    
162. 寻找峰值
  • https://leetcode-cn.com/problems/find-peak-element/

  • 首先判断边界条件,即0位置和最后一个位置是否是峰值

  • 如果不是,就可以对left = 1, right = nums.size() - 2进行二分。如果mid位置是峰值那么就找到了,如果不是,就看哪边是向上增加的,因为边界不是峰值,所以边界处一定是向下的,那么一个向上,一个向下,中间一定有一个峰值存在

  • class Solution {
    public:
        int findPeakElement(vector<int>& nums) {
            if (nums.size() == 1) return 0;
            if (nums[0] > nums[1]) return 0;
            int n = nums.size();
            if (nums[n - 1] > nums[n - 2]) return n - 1;
            int l = 1;
            int r = n - 2;
            while (l <= r) {
                int mid = l + (r - l) / 2;
                if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) return mid;
                if (nums[mid] < nums[mid + 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
            return -1;
        }
    };
    
寻找两个排序数组的第k个数系列
基本思路
  • 对于两个有序数组nums1nums2中,长度分别为mn,找两个有序数组中第k小的数。
  • 对k进行二分,则在nums10k/2,和nums20k/2中,目标数,可能在这两组数中,也可能不在,当在的时候,如果说nums2[k/2] 小于 nums1[k/2],则目标数一定不在nums20k/2中,后面在继续二分
4. 寻找两个正序数组的中位数
  • https://leetcode-cn.com/problems/median-of-two-sorted-arrays/

  • 看代码注释吧

  • class Solution {
    public:
        // 找两个有序数组的第k小的数
        // p1为当前nums1的起始位置
        // p2为当前nums2的起始位置
        int getK(vector<int>& nums1, vector<int>& nums2, int p1, int p2, int k) {
            // 如果nums1长度更小,则换一下位置,保证nums2长度更小
            if (nums1.size() - p1 < nums2.size() - p2) {
                return getK(nums2, nums1, p2, p1, k);
            }
            // 如果p2越界了,直接从nums1上面找
            if (p2 >= nums2.size()) {
                return nums1[p1 + k - 1];
            }
    		
            // 如果收k==1,就是找第一小的数,就直接从两者中找到最小的返回
            if (k == 1) {
                return min(nums1[p1], nums2[p2]);
            }
    		
            // t为分半后,p11为nums1要比较的位置,p22为nums2要比较的位置,由于nums2短,要防止越界
            int t = k / 2;
            int p11 = p1 + t - 1;
            int p22 = p2 + t - 1 >= nums2.size() ? nums2.size() - 1 : p2 + t - 1;
    		
            // nums1[p11] > nums2[p22],则砍掉nums2的那一段数据
            if (nums1[p11] > nums2[p22]) {
                return getK(nums1, nums2, p1, p22 + 1, k - (p22 - p2 + 1));
            }
            // nums1[p11] <= nums2[p22],则砍掉nums1的那一段数据
            return getK(nums1, nums2, p11 + 1, p2, k - (p11 - p1 + 1));
        }
    
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            int n = nums1.size() + nums2.size();
            if (n % 2) {
                return getK(nums1, nums2, 0, 0, n / 2 + 1);
            }
            int t1 = getK(nums2, nums1, 0, 0, n / 2);
            int t2 = getK(nums2, nums1, 0, 0, n / 2 + 1);
            return (double(t1) + double(t2)) / 2;
        }
    };
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值