力扣hot100—二分查找6题

二分查找的通用板子,需要注意理解:

ep:1,2,2,2,3,5
//查大于等于target的第一个位置, target=2,目标位置是1
int l = 0, r = n - 1;
while (l < r) {
    int mid = l + r >> 1;
    // 找最早的那个值,nums[mid] >= x时,即使等于目标值,也要继续向左找,所以 r = mid
    // 这里用 r = mid 而不是 r = mid - 1,因为 mid可能是我们要找的左边界
    if(a[mid] >= x) r=mid;
    else l = mid + 1;
}

//查小于等于target的最后一个位置,target等于2,目标位置是3
int l = 0, r = n - 1;
while (l < r)
 {
        int mid = l + r + 1 >> 1;
        // 找最晚的那个值,nums[mid] <= x时,即使等于目标值,也要继续向右找,所以 l = mid
    	// 这里用 l = mid而不是 l = mid + 1,因为 mid可能是我们要找的右边界
        if (a[mid] <= x) l = mid;
        else r = mid - 1;
 }

搜索插入位置

转化思路,nums[pos−1]<target≤nums[pos],找pos,可以变成找第一个大于target的pos(acwing 查找不小于target的第一个位置)。代码同上面的模板。

搜索二维矩阵

找到最后一个不大于目标数的那一行,再对那一行进行二分查找。
也可以用思维方式,从左下角出发,比target大就向上走,反之向右走。更简单。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size(),n=matrix[0].size();
        int row=m-1,col=0;
        while(row>=0&&col<n){
            if(matrix[row][col]==target)return true;
            else if(matrix[row][col]>target)row--;
            else col++;
        }
        return false;
    }
};

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

同第一题。套两个模板。

搜索旋转数组

变种二分查找,虽然被打乱了一些顺序,但是还是可以分两部分看有序的,nums[0] <= nums[mid],代表左边是有序的,否则右边是有序的。

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        if(n == 0){
            return -1;
        }
        if(n == 1){
            return nums[0] == target? 0:-1;
        }
        int l = 0;
        int r = n - 1;
        while(l <= r){
            int mid = (l + r) / 2;
            if(nums[mid] == target){
                return mid;
            }
            // 左边有序
            if(nums[0] <= nums[mid]){
                if(nums[0] <= target && target < nums[mid]){
                    r = mid - 1;
                }else{
                    l = mid + 1;
                }
            }else{
                    if(nums[n-1] >= target && target > nums[mid]){
                        l = mid + 1;
                    }else{
                        r = mid - 1;
                    }
                }
            }
                    return -1;
        
    }
}

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

同4题,也是区分左右两边的有序性判断性质。如果左边有序,最小值肯定是最左的,如果右边有序,最小值肯定是nums[mid].

class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 1) return nums[0];
        int minV = nums[0];
        int l = 0;
        int r = nums.length - 1;
        while(l <= r){
            int mid = (l + r) / 2;
            if(nums[mid] < minV) minV = nums[mid];
            // 左边有序
            if(nums[mid] >= nums[0]){
                minV = nums[0] < minV? nums[0]:minV;
                l = mid + 1;
            }else{
                minV = nums[mid] < minV? nums[mid]:minV;
                r = mid - 1;
            }
        }
        return minV;
    }
}

寻找两个正序数组的中位数

难。

力扣题解:https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/3660794/deepseekti-jie-by-elk-r-xscq 将问题转化为在两个数组中寻找合适的分割线,使得分割线左边的所有元素都小于等于右边的所有元素,并且左右两边的元素数量相等或左边多一个。

确保第一个数组是较短的:这样我们可以将时间复杂度优化到O(log(min(m,n)))
定义分割线:分割线将数组分为左右两部分
对于数组A,分割线在i的位置,左边有i个元素,右边有m-i个元素
对于数组B,分割线在j的位置,左边有j个元素,右边有n-j个元素

理想分割条件:
A[i-1] ≤ B[j] 且 B[j-1] ≤ A[i](左边最大值 ≤ 右边最小值)
关于A[i-1] ≤ B[j] 且 B[j-1] ≤ A[i](左边最大值 ≤ 右边最小值),这个式子的意义在于:
找出来的i和j,一定是这样的序列(A[i-1],B[j-1]的顺序不重要,可以互换,重点在于i和j一定是紧挨着的):....A[i-1],B[j-1], A[i],B[j],所以一定会有:A[i-1] ≤ B[j] 且 B[j-1] ≤ A[i]
注意,此处的i,j表示的并不是下标,而是有多少个数,在左边,在右边

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 确保 nums1 是较短的数组
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }
        
        int m = nums1.length;
        int n = nums2.length;
        int imin = 0, imax = m, halfLen = (m + n + 1) / 2;
        
        while (imin <= imax) {
            int i = (imin + imax) / 2;
            int j = halfLen - i;
            
            if (i < m && nums2[j - 1] > nums1[i]) {
                // i 太小,需要增大
                imin = i + 1;
            } else if (i > 0 && nums1[i - 1] > nums2[j]) {
                // i 太大,需要减小
                imax = i - 1;
            } else {
                // 说明找到合适的分割线了,开始计算中位数
                
                // 计算左半部分的最大值
                int maxOfLeft;
                if (i == 0) {
                    // i==0 说明没有任何num1的元素在左边,此时的序列应该是:...j-1, i, j...左半部最小值只能是j-1 此处的i并不是下标 而是表示num1数组有多少个元素在左边
                    maxOfLeft = nums2[j - 1];
                } else if (j == 0) {
                    maxOfLeft = nums1[i - 1];
                } else {
                    maxOfLeft = Math.max(nums1[i - 1], nums2[j - 1]);
                }
                
                // 如果总长度是奇数,直接返回左半部分最大值
                if ((m + n) % 2 == 1) {
                    return maxOfLeft;
                }
                
                // 计算右半部分的最小值
                int minOfRight;
                if (i == m) {
                    // 同理 i==m,说明num1的所有元素全在左边,右边没有任何元素,右边的那个值只能取nums2[j]
                    minOfRight = nums2[j];
                } else if (j == n) {
                    minOfRight = nums1[i];
                } else {
                    minOfRight = Math.min(nums1[i], nums2[j]);
                }
                
                // 如果总长度是偶数,返回平均值
                return (maxOfLeft + minOfRight) / 2.0;
            }
        }
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇