首页 > 其他分享 >二分查找的三种形式&两道力扣

二分查找的三种形式&两道力扣

时间:2023-01-25 15:44:57浏览次数:59  
标签:二分 right return target nums mid 力扣 查找 left

前言

过年前刷Leetcode的时候遇到这样一道题目:
354. 俄罗斯套娃信封问题 - 力扣(Leetcode)
其中使用patience sorting这个算法的做法中,因为牌堆顶是有序数组,所以可以使用二分查找把时间复杂度降到对数级别,但是这个并不是需要查找某个数字所出现的位置,而是需要查找当前数字需要插入到何处,思考了大佬的代码后恍然大悟,AC后想系统性整理一下二分查找的基本用法和拓展,于是有了此文。

二分查找基本用法(寻找某数位置)

//用kotlin是为了练一下语法……几乎和java是一样的,应该没有阅读压力
class Solution {  
    fun binarySearch(nums: IntArray, target: Int): Int {  
        var left: Int = 0  
        //right可以是nums.size-1,也可以是nums.size,具体情况写到下文  
        var right: Int = nums.size - 1  
        //while中的条件是一个关键点,什么时候取等什么时候不取等  
        while (left <= right) {  
            //经典操作,以防直接相加导致的越界  
            val mid = left + (right - left) / 2  
            //每次调整搜索范围时,什么时候加1什么时候不加1也是一个关键  
            if (nums[mid] < target) left = mid + 1  
            else if (nums[mid] > target) right = mid - 1  
            else if (nums[mid] == target) return mid  
        }  
        return -1  
    }  
}

在阅读labuladong大佬的文章前,对二分查找也存在很多疑问点,也就是我在代码注释中写出来的部分。因为本身也不经常刷算法,而接触二分查找又是大一入学阶段,所以导致当时的疑问就这么堆着,如果现在的话,最好的做法就是带着疑问,用多组testcase进行debug,一步步分析代码。

1.while中何时取等何时不取等

这个问题的关键在于right变量的初始化

  • right变量初始化为nums.size-1时,就可以取等,反之则不能
    如果我们当前查找的区间形式是左闭右开,那么就可以取nums.size,反之则不能(很好想)。例如上述代码中查找区间形式为左闭右闭,while的终止条件为left>right(其实是right+1),这时闭区间内绝对不会存在数字,是一个非法区间,也就代表可以终止了,而终止的结果正是没有找到。
    如果我们使用左闭右开区间,while的终止条件就是left>=right(其实就是right),这时假如区间左右端点相等,那么区间内还存在一个数字,但while循环却终止了,明显是错误的。可以如下改正
//改成right也可以,因为此时left和right相同了
return nums[left] == target ? left : -1;

2.left和right什么时候+1 or -1,什么时候不需要

这个问题的关键就承接第一个问题。当我们取的是闭区间的时候,每次搜索,mid这个索引下的数字都是被搜索了的,所以下一次我们理所应当应该取mid的左或右,而当我们取开区间时,mid这个索引下的数字存在未被搜索的情况,对应其未被搜索时,就需要从mid这个索引开始下一次搜索

寻找左侧边界的二分查找

fun binarySearchForLeftBound(nums: IntArray, target: Int): Int {  
    var left = 0  
    var right = nums.size - 1  
    while (left <= right) {  
        val mid = left + (right - left) / 2  
        if (nums[mid] > target) right = mid - 1  
        else if (nums[mid] < target) left = mid + 1  
        else if (nums[mid] == target) right = mid - 1  
    }  
    if (left == nums.size) return -1  
    return if (nums[left] == target) left  
    else -1  
}

1.while中何时取等

这个问题的解答和上面那个是相同的,不再赘述了

2.为什么上述代码可以搜索到左侧边界

因为每当nums[mid]==target时,我们都不直接返回当前下标,而是将右边界缩小,相当于不断向左进行收缩,进而寻找左侧边界

寻找右侧边界的二分查找

fun binarySearchForRightBound(nums: IntArray, target: Int): Int {  
    var left = 0  
    var right = nums.size - 1  
    while (left <= right) {  
        val mid = left + (right - left) / 2  
        if (nums[mid] > target) right = mid - 1  
        else if (nums[mid] < target) left = mid + 1  
        else if (nums[mid] == target) left = mid + 1  
    }  
    if (right < 0) return -1  
    return if (nums[right] == target) nums[right]  
    else -1  
}

其实上述代码中最后nums[right]==target部分中有些人习惯将right写为left-1,当while循环结束后,left=right+1,所以二者可以替换。但是这里为什么不去判断left而是判断left-1,就是一个疑惑点,这是因为每次我们都需要将mid这个下标排除出搜索区间,当循环结束时,左侧边界不断靠右且每次移动都有mid=left-1这个关系,最后left下标处一定不是target,反而是left-1处可能是target。

Leetcode 34

image.png

class Solution {

    fun searchRange(nums: IntArray, target: Int): IntArray {

        return if (nums.isEmpty()) intArrayOf(-1, -1)

        else intArrayOf(searchLeftBound(nums, target), searchRightBound(nums, target))

    }

  

    private fun searchLeftBound(nums: IntArray, target: Int): Int {

        var left = 0

        var right = nums.size - 1

        while (left <= right) {

            val mid = left + (right - left) / 2

            if (nums[mid] < target) left = mid + 1

            else if (nums[mid] > target) right = mid - 1

            else if (nums[mid] == target) right = mid - 1

        }

        if (left == nums.size) return -1

        return if (nums[left] == target) left

        else -1

    }

  

    private fun searchRightBound(nums: IntArray, target: Int): Int {

        var left = 0

        var right = nums.size - 1

        while (left <= right) {

            val mid = left + (right - left) / 2

            if (nums[mid] < target) left = mid + 1

            else if (nums[mid] > target) right = mid - 1

            else if (nums[mid] == target) left = mid + 1

        }

        if (left - 1 < 0) return -1

        return if (nums[left - 1] == target) left - 1

        else -1

    }

}

Leetcode 354

image.png

import java.util.Arrays;

  

/**

 * @author Zzh

 * @date 2023/1/7

 * @time 16:03

 */

class Solution {

    public int maxEnvelopes(int[][] envelopes) {

        Arrays.sort(envelopes, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);

        int[] height = new int[envelopes.length];

        for (int i = 0; i < envelopes.length; i++) {

            height[i] = envelopes[i][1];

        }

        return lengthOfLIS(height);

    }

  

    private int lengthOfLIS(int[] nums) {

        if (nums.length == 0) return 0;

        int[] top = new int[nums.length];

        int piles = 0;

        for (int poker :

                nums) {

            int left = 0;

            int right = piles - 1;

            while (left <= right) {

                int mid = left + (right - left) / 2;

                if (top[mid] < poker) left = mid + 1;

                else if (top[mid] > poker) right = mid - 1;

                else if (top[mid] == poker) right = mid - 1;

            }

            if (left == piles) piles++;

            top[left] = poker;

        }

        return piles;

    }

}

标签:二分,right,return,target,nums,mid,力扣,查找,left
From: https://www.cnblogs.com/appletree24/p/17066997.html

相关文章

  • 力扣---1306. 跳跃游戏 III
    这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i+arr[i]或者i-arr[i]。请你判断自己是否能够跳到对......
  • 学习笔记——Linux中搜索查找类命令;压缩和解压类;Linux挂载和卸载;进程线程类命令;RPM;YUM
    2023-01-24一、搜索查找类命令1、find命令(1)find-name"*.txt" (功能描述:查找当前目录下包含“.txt”的文件)2、grep过滤查找及“|”管道符管道符,“|”,表示将前一个命......
  • 代码随想录 | Day 1 | LC 27 移除元素、704 二分查找
    704.二分查找题目解法1:纯遍历classSolution{publicintsearch(int[]nums,inttarget){for(inti=0;i<nums.length;i++){i......
  • sqlsugar 查找表是否存在
    一般的,就使用GetTableInfoList来获取所有表。然后对比一下就知道表是否存在了。使用sqlsugar时,都需要创建实体类。并且添加上特性 [SugarTable]  我这边想从一开始......
  • 704.二分查找
    题目:给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。*要求输入:nums=[-1,0,......
  • 力扣每日一题2023.1.24---1828. 统计一个圆中点的数目
    给你一个数组points,其中points[i]=[xi,yi],表示第i个点在二维平面上的坐标。多个点可能会有相同的坐标。同时给你一个数组queries,其中queries[j]=[xj,yj,......
  • 力扣---2. 两数相加
    给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。请你将两个数相加,并以相同形式返回一个表示和的链表......
  • 力扣---1221. 分割平衡字符串
    平衡字符串中,'L'和'R'字符的数量是相同的。给你一个平衡字符串s,请你将它分割成尽可能多的子字符串,并满足:   每个子字符串都是平衡字符串。返回可以通过分割得到的......
  • 力扣---2455. 可被三整除的偶数的平均值
    给你一个由正整数组成的整数数组nums,返回其中可被3整除的所有偶数的平均值。注意:n个元素的平均值等于n个元素求和再除以n,结果向下取整到最接近的整数。示例1......
  • 力扣每日一题2023.1.223---2303. 计算应缴税款总额
    给你一个下标从0开始的二维整数数组brackets,其中brackets[i]=[upperi,percenti],表示第i个税级的上限是upperi,征收的税率为percenti。税级按上限从低到高排......