LeetCode刷题---二分查找巩固

1385. 两个数组间的距离值

题目

给你两个整数数组 arr1 ,arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。

「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素arr2[j]满足 |arr1[i]-arr2[j]| <= d

示例 1:

输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
输出:2

解释:

对于 arr1[0]=4 我们有:
|4-10|=6 > d=2
|4-9|=5 > d=2
|4-1|=3 > d=2
|4-8|=4 > d=2
所以 arr1[0]=4 符合距离要求

对于 arr1[1]=5 我们有:
|5-10|=5 > d=2
|5-9|=4 > d=2
|5-1|=4 > d=2
|5-8|=3 > d=2
所以 arr1[1]=5 也符合距离要求

对于 arr1[2]=8 我们有:
|8-10|=2 <= d=2
|8-9|=1 <= d=2
|8-1|=7 > d=2
|8-8|=0 <= d=2
存在距离小于等于 2 的情况,不符合距离要求

故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2

示例 2:

输入:arr1 = [1,4,2,3], arr2 = [-4,-3,6,10,20,30], d = 3
输出:2

示例 3:

输入:arr1 = [2,1,100,3], arr2 = [-5,-2,10,-3,7], d = 6
输出:1

提示:

1 <= arr1.length, arr2.length <= 500
-10^3 <= arr1[i], arr2[j] <= 10^3
0 <= d <= 100

思路

1.暴力
逆向思维找出满足式子的值,再res–
2.二分查找
通过暴力可知找出abs(arr1[i] - arr2[j]) <= d的值,那么二分查找不就是从中间开始找那个值吗?
所以说如果一开始二分查找想不到可以先用简单的方法做出来再去想其他方法。

二分查找不是要有序吗?那就先排序arr2再二分。
排序sort是快排,时间复杂度nlogn,而for循环中一个二分法也是nlogn,相加渐进时间复杂度还是nlogn。

不过注意的是注意是否三个条件,用绝对值明显没有,需要把绝对值拆开。
得到-d+arr1[]<=arr2[]<=arr1[]+d

  • 如果满足res–
  • 如果-d + arr1[i] > arr2[middle],arr2中数值小了往右移
  • 如果arr2[middle] > arr1[i] + d,arr2中数值大了往左移

因为找的数需要满足的条件是-d+arr1[]<=arr2[]<=arr1[]+d,所以根据arr2中数值大小判断左移还是右边移

代码

1.暴力

class Solution {
    
    
public:
    int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {
    
    
        int res = arr1.size();
        for(int i = 0; i < arr1.size(); i++){
    
    
            for(int j = 0; j < arr2.size(); j++){
    
    
                if(abs(arr1[i] - arr2[j]) <= d){
    
    
                    res--;
                    break;
                }
            }
        }
        return res;
    }
};

2.二分查找

class Solution {
    
    
public:
    int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {
    
    
        sort(arr2.begin(),arr2.end());   
        int res = arr1.size();
        for(int i = 0; i < arr1.size(); i++){
    
    
            // 注意些在循环内
            int left = 0;
            int right = arr2.size() - 1;
            while(left <= right){
    
    
                int middle = left + (right - left) / 2;
                if(-d + arr1[i] <= arr2[middle] && arr2[middle] <= arr1[i] + d){
    
    
                    res--;
                    break;
                }
                else if(-d + arr1[i] > arr2[middle]){
    
    
                    left = middle + 1;  
                }
                else if(arr2[middle] > arr1[i] + d){
    
    
                    right = middle - 1;
                }
            }
        }
        return res;
    }
};

852. 山脉数组的峰顶索引

题目

符合下列属性的数组 arr 称为 山脉数组 :
arr.length >= 3
存在 i(0 < i < arr.length - 1)使得:
arr[0] < arr[1] < … arr[i-1] < arr[i]
arr[i] > arr[i+1] > … > arr[arr.length - 1]

给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i 。

示例 1:

输入:arr = [0,1,0]
输出:1

示例 2:

输入:arr = [0,2,1,0]
输出:1

示例 3:

输入:arr = [0,10,5,2]
输出:1

提示:

3 <= arr.length <= 104
0 <= arr[i] <= 106
题目数据保证 arr 是一个山脉数组

思路

题目的意思就是让你找出中间最大的值,注意左右边界left和right的值,不能生搬硬套使用模板

代码

class Solution {
    
    
public:
    int peakIndexInMountainArray(vector<int>& arr) {
    
    
        int left = 0;
        int right = arr.size() - 1;
        while(left <= right){
    
    
            int middle = left + (right - left) / 2;
            if(arr[middle - 1] < arr[middle] && arr[middle] > arr[middle + 1]){
    
    
                return middle;
            }
            // left和right要赋值为middle,因为middle还需要比较相邻的数
            // 而经典的left=middle+1是因为元素与数组外的数比较
            else if(arr[middle - 1] < arr[middle]){
    
    
                left = middle;
            }
            else if(arr[middle] > arr[middle + 1]){
    
    
                right = middle;
            }
        }
        // 官方更巧妙些
        return 0;
    }
};

744. 寻找比目标字母大的最小字母

题目

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

在比较时,字母是依序循环出现的。举个例子:

如果目标字母target = 'z'并且字符列表为 letters = ['a', 'b'],则答案返回 'a'

示例 1:

输入: letters = [“c”, “f”, “j”],target = “a”
输出: “c”

示例 2:

输入: letters = [“c”,“f”,“j”], target = “c”
输出: “f”

示例 3:

输入: letters = [“c”,“f”,“j”], target = “d”
输出: “f”

提示:

2 <= letters.length <= 104
letters[i] 是一个小写字母
letters 按非递减顺序排序 letters
最少包含两个不同的字母
target 是一个小写字母

思路

这道题与278. 第一个错误的版本几乎一模一样,方法都是判断middle与目标数比较的时候,如果middle小于目标数,往右移,如果右移第一个大于目标数,就是要找的数。而大于目标数往左移。

代码

class Solution {
    
    
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
    
    
        int left = 0;
        int right = letters.size() - 1;
        while(left <= right){
    
    
            int middle = left + (right - left) / 2;
            // 小于目标数,往右移,如果右移第一个大于目标数,就是要找的数
            if(letters[middle] <= target){
    
    
                left = middle + 1;
                if(letters[left] > target)
                return letters[left];
            }  
            //大于目标数往左移
            else if(letters[middle] > target){
    
    
                right = middle - 1;
            }

        }
        return letters[0];
    }
};

猜你喜欢

转载自blog.csdn.net/btufdycxyffd/article/details/127195821