首页 > 其他分享 >给自己复盘用的随想录笔记day1-二分查找

给自己复盘用的随想录笔记day1-二分查找

时间:2024-08-22 19:23:24浏览次数:13  
标签:right target nums int 随想录 mid day1 复盘 left

二分法

使用情景

数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。

求某一个整数的平方根

边界条件

写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

区间的定义就是不变量,那么在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则。

相关题目

搜索插入位置

public int searchInsert(int[] nums, int target) {
    int left = 0;
    int right = nums.length;
    while (left < right) { //左闭右开 [left, right)
        int middle = left + ((right - left) >> 1);
        if (nums[middle] > target) {
            right = middle; // target 在左区间,在[left, middle)中
        } else if (nums[middle] < target) {
            left = middle + 1; // target 在右区间,在 [middle+1, right)中
        } else { // nums[middle] == target
            return middle; // 数组中找到目标值的情况,直接返回下标
        }
    }
    // 目标值在数组所有元素之前 [0,0)
    // 目标值插入数组中的位置 [left, right) ,return right 即可
    // 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
    return right;
}

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

这个左右边界的思想分别左右滑动寻找左边界右边界,我觉得这个思路最好理解

class Solution {
	public int[] searchRange(int[] nums, int target) {
		int index = binarySearch(nums, target); // 二分查找
		
		if (index == -1) { // nums 中不存在 target,直接返回 {-1, -1}
			return new int[] {-1, -1}; // 匿名数组 
		}
		// nums 中存在 target,则左右滑动指针,来找到符合题意的区间
		int left = index;
		int right = index;
        // 向左滑动,找左边界
		while (left - 1 >= 0 && nums[left - 1] == nums[index]) { // 防止数组越界。逻辑短路,两个条件顺序不能换
			left--;
		}
        // 向右滑动,找右边界
		while (right + 1 < nums.length && nums[right + 1] == nums[index]) { // 防止数组越界。
			right++;
		}
		return new int[] {left, right};
    }
	
	/**
	 * 二分查找
	 * @param nums
	 * @param target
	 */
	public int binarySearch(int[] nums, int target) {
		int left = 0;
		int right = nums.length - 1; // 不变量:左闭右闭区间
		
		while (left <= right) { // 不变量:左闭右闭区间
			int mid = left + (right - left) / 2;
			if (nums[mid] == target) {
				return mid;
			} else if (nums[mid] < target) {
				left = mid + 1;
			} else {
				right = mid - 1; // 不变量:左闭右闭区间
			}
		}
		return -1; // 不存在
	}
}

上面这种思路是利用左右两个指针,如果简单的使用二分法的思路去解决这个问题,就是把这个问题分为两部分,先去求第一次出现目标值的位置,这个位置特点是前面的值都比目标值小;同理再去求最后一次出现目标值的位置,这个位置的特点是后面的都比目标值大

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = searchLeft(nums,target);
        int right = searchRight(nums,target);
        return new int[]{left,right};
    }
    public int searchLeft(int[] nums,int target){
        // 寻找元素第一次出现的地方
        int left = 0;
        int right = nums.length-1;
        while(left<=right){
            int mid = left+(right-left)/2;
            // >= 的都要缩小 因为要找第一个元素
            if(nums[mid]>=target){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        // right = left - 1
        // 如果存在答案 right是首选
        if(right>=0&&right<nums.length&&nums[right]==target){
            return right;
        }
        if(left>=0&&left<nums.length&&nums[left]==target){
            return left;
        }
        return -1;
    }

    public int searchRight(int[] nums,int target){
        // 找最后一次出现
        int left = 0;
        int right = nums.length-1;
        while(left<=right){
            int mid = left + (right-left)/2;
            // <= 的都要更新 因为我们要找最后一个元素
            if(nums[mid]<=target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        // left = right + 1
        // 要找最后一次出现 如果有答案 优先找left
        if(left>=0&&left<nums.length&&nums[left]==target){
            return left;
        }
        if(right>=0&&right<=nums.length&&nums[right]==target){
            return right;
        }
        return -1;
    }
}

x的平方根

这个地方用的是二分法的思想,一定不要把数组的二分法的代码直接套用。

我们可以使用「二分查找」来查找这个整数,不断缩小范围去猜。

猜的数平方以后大了就往小了猜;
猜的数平方以后恰恰好等于输入的数就找到了;
猜的数平方以后小了,可能猜的数就是,也可能不是

69. x 的平方根 - 力扣(LeetCode)

public class Solution {

    public int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }
        if (x == 1) {
            return 1;
        }

        int left = 1;
        int right = x / 2;
        while (left < right) {
            int mid = left + (right - left + 1) / 2;
            if (mid > x / mid) {
                right = mid - 1;
            } else if (mid == x / mid) {
                // 由于本题特殊性,如果 mid == x / mid 就找到了答案,其它问题不一定可以这样
                return mid;
            } else {
                left = mid;
            }
        }
        return left;
    }
}

一个整数的平方根肯定不会超过它自己的一半,但是 0 和 1 除外,因此我们可以在 1 到输入整数除以 2 这个范围里查找我们要找的平方根整数。0 单独判断一下就好。

问题:mid 为什么要加 1

上面那个理解有点麻烦,其实就单纯的从数学问题去理解,写成闭区间的形式去分析。

public class Solution {

    public int mySqrt(int x) {
    if(x==0){
        return 0;
    }
    if(x==1){
        return 1;
    }
    
    int l=1;
    int r=x/2;
    while(l<=r){
        int m=l+(r-l)/2;
        if(x/m<m){
        r=m-1;
        }else if(x/m==m){
            return m;
        }else{
            l=m+1;
        }
    }
    return l-1;
    }
}

有效数的完全平方根

这个题目可以利用数学知识解题

class Solution {
    public boolean isPerfectSquare(int num) {
        // 1 + 3 + 5 + ... + (2 * n - 1) = (2 * n - 1 + 1) * n = n * n
        for(int i=1;num>0;i+=2)
            num -= i;
        return num == 0;
    }
}

反正哪一种方法记的快方便理解就背那个方法,要刷算法题目

标签:right,target,nums,int,随想录,mid,day1,复盘,left
From: https://blog.csdn.net/weixin_46321761/article/details/141333098

相关文章

  • 代码随想录 -- 数组 -- 螺旋矩阵II
    59.螺旋矩阵II-力扣(LeetCode)每画一条边都要坚持一致的左闭右开注意处理n为奇数时的矩阵中心点classSolution(object):defgenerateMatrix(self,n):res=[[0]*nforainrange(n)]startX=0startY=0loop=mid=n/2c......
  • 代码随想录 -- 数组 -- 区间和
    58.区间和(第九期模拟笔试)(kamacoder.com)暴力解法大概率超时,应采用前缀和解法p[i] 表示vec[0]到vec[i]的累加和求vec[m] 到vec[n] 的和只需要 p[n]-p[m] 即可知识点input函数Python3 中raw_input()和input()进行了整合,去除了raw_input(),仅保留了i......
  • 代码随想录day37 || 518 零钱兑换,377 组合总和iv,70 爬楼梯
    0-1背包问题在0-1背包问题中,每种物品只能选择一次,因此一旦选择某个物品后,剩余的容量只能放入前面的物品。这就是为什么状态转移方程是:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w(i)]+v(i))这里的dp[i-1][j-w(i)]+v(i)表示选择第(i)个物品后,剩余的容量只能放入前(......
  • 题解:P9784 [ROIR 2020 Day1] 超速
    传送门思路我们设\(T\)为所花的总时间,\(d\)为超速多少。然后不难知道$T=\sum_{i=1}^{n}\frac{l_i}{v_i+d}$,所以我们实际上是要找到符合条件最小的\(d\)。再结合题目所说最高被罚款的金额最少,然后二分枚举答案即可。时间复杂度\(O(nq\log(m))\)。AC代码#include......
  • 移动互联 实训DAY10
    1.flex布局Flex是FlexibleBox的缩写,意为"弹性布局",⽤来为盒状模型提供最⼤的灵活性。任何⼀个容器都可以指定为Flex布局。容器属性:flex-flowflex-directionflex-wrapjustify-contentalign-itemsalign-content 元素属性:orderflex-growflex-shrinkflex-bas......
  • 代码随想录Day22
    77.组合给定两个整数n和k,返回范围[1,n]中所有可能的k个数的组合。你可以按任何顺序返回答案。示例1:输入:n=4,k=2输出:[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4],]示例2:输入:n=1,k=1输出:[[1]]提示:1<=n<=201<=k<=n正解......
  • 【题解】Solution Set - NOIP2024集训Day12 树上启发式合并
    【题解】SolutionSet-NOIP2024集训Day12树上启发式合并https://www.becoder.com.cn/contest/5472「CF600E」Lomsatgelral直接dsuontree。记录每一个颜色的出现次数。「IOI2011」Race之前是用点分治做的。考虑dsuontree。每个子树内维护到根节点的距离为\(x\)......
  • 代码随想录day36 || 1049 最后一筐石头重量||, 494 目标和,474 一和零
    1049最后一块石头重量||funclastStoneWeightII(stones[]int)int{ //本题思路在于要想得到最小差,就要尽可能将石头分割为两堆相近的重量,然后转换为背包问题 //dp[i]表示容量i背包能装的石头总价值,其中重量和价值相等 //递推公式dp[j]=max(dp[j],dp[j-w(i)]+v[i]......
  • 「代码随想录算法训练营」第四十三天 | 图论 part1
    797.所有可能的路径题目链接:https://leetcode.cn/problems/all-paths-from-source-to-target/description/文章讲解:https://programmercarl.com/kamacoder/0098.所有可达路径.html题目难度:中等题目状态:看题解思路一:DFSvoiddfs(vector<vector<int>>&graph,intx,intn......
  • 代码随想录Day21
    669.修剪二叉搜索树给你二叉搜索树的根节点root,同时给定最小边界low和最大边界high。通过修剪二叉搜索树,使得所有节点的值在[low,high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。可以证明,存在唯一的答案。所......