【第一章 数组part01】704. 二分查找 27. 移除元素 977.有序数组的平方

【第一章 数组part01】

704. 二分查找 27. 移除元素 977.有序数组的平方

💻[数组理论基础]

前置知识:数组理论基础

TIPS

  • 正是因为数组在内存空间的地址连续,因此增\删一个元素,就要移动后面所有元素的内存地址.
  • 对于数组,增删不方便,而改\查元素是方便的.
  • 在C++中二维数组是连续分布的.像Java是没有指针的 ,寻址操作完全交给虚拟机,因此java数组暴露出来的地址是经过处理的.

💻704. 二分查找

目标:熟练根据 左闭右开,左闭右闭 两种区间规则 写出二分法。
题目链接
文章讲解
视频讲解

一、题目和思考:

一个升序的数组nums中给定一个整数目标target,在nums中搜索目标,存在返回下标,不存在返回-1.

Tips:
使用二分的前提:有序数据and 无重复值.
如果计算的结果是小数,默认向下取整,(5 - 0) / 2 = 2.5,取2.

二、思路与题解:

思路

方法一:如果我定义区间是[left,right]

如果我定义搜索的边界是 [left, right],也就是说,包括 left 和 right 这两个边界,那么我应该选择 while(left <= right)。

定义左\右
当左<=右
    进入循环
    	定义中间
    	如果[mid]<target值
    		left=mid+1
		如果[mid]>target值
			right=mid-1
		否就是[mid]=target值
			返回mid
否则左>右
    也就是遍历后未找到目标,-1
方法二:如果我定义区间是[left,right)

如果我定义搜索的边界是 [left, right),也就是说,不包括 right 这个边界,那么我应该选择 while(left < right)。

定义左\右
当左<右
    进入循环
    	定义中间
    	如果[mid]<target值
        	left=mid+1
    	否则target<mid值
        	right=mid-1
否则
	如果[mid]=target值//[]mid]也就是当前的nums[left]
	 return mid
	否则
     遍历后未找到目标,-1

题解

方法一:如果我定义区间是[left,right]
class Solution {
    public int search(int[] nums, int target) {
        int left=0;
        int right=nums.length-1;//注意这里是-1
        while(left<=right){
            int mid=left+(right-left)/2;
            if(nums[mid]<target){
                left=mid+1;
            }else if(nums[mid]>target){
                right=mid-1;
            }else{
                return mid;
            }
        }
        return -1;
    }
}
方法二:如果我定义区间是[left,right)
class Solution {
    public int search(int[] nums, int target) {
        int left=0;
        int right=nums.length-1;//注意这里是-1
        while(left<right){
            int mid=left+(right-left)/2;
            if(nums[mid]<target){
                left=mid+1;
            }else if(nums[mid]>target){
                right=mid-1;
            }else{
                return mid;
            }
        }
        if(nums[left]==target){
            return left;
        }
        return -1;
    }
}

三、难点的解决:

while(left < right)while(left <= right) 的区别?

如果我定义搜索的边界是 [left, right]

while(left <= right)有等于号【推荐这样写】
left=right也被算进去

  • while(left <= right) 允许在 left == right 时进行最后一次检查,这对于大多数情况是直接返回结果的。
   while(left<right){ 

如果我定义搜索的边界是 [left, right)

while(left < right) 没有等于号 【不推荐!】
left=right会被忽略,要在while同级再进行判断

  • while(left < right) 则不允许在 left == right 时进行搜索,但可以通过在循环外做一次额外的检查来确保不会错过目标。
   while(left<right){
   }
   if (nums[left] == target) {
       return left; 
   } 

mid 的计算不正确?

解决:
1.避免直接计算 left + right,以免被赋值的mid超过整数最大值,导致溢出,从而mid计算错误
2.注意比较的是 nums[mid] 和 target
3.mid的初始化应当在while循环内进行,确保每次循环mid都会刷新,以此确保进行了有效缩小范围,解决超时报错

只使用 left = midright = mid,即不使用 +1-1 来更新边界是否可行?

标准二分法是left = mid+1right = mid-1,这样写的目的是 :
使得每一步都保持在 left 和right 边界的位置上,确保每一次while都缩小一半范围,直到搜索到目标值.


如果不使用 +1-1 ,只使用 left = midright = mid更新边界,
目标还是使得每一步都保持在left 和 right 边界的位置上.

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

           if (nums[mid] == target) {
               return mid; // 找到目标,返回下标
           } else if (nums[mid] < target) {
               left = mid; // 目标在右侧
           } else {
               right = mid; // 目标在左侧
           }
       }  

关键在于:每次更新边界时,不是简单地将 left 或 right 向中间靠拢,而是让 left 和 right 移动到 mid位置,这样可以确保每次搜索仍然有效。


还是使用"靠近"的方式更新边界吧,这种"代替" 的方式思考比较复杂.

为什么int mid=left+(right-left)/2就对了,用int mid=right-(right-left)/2就错了?

计算 mid 的两种方式:
int mid = left + (right - left) / 2; //方法一计算mid,运行通过
int mid=right-(right-left)/2;//方法二计算mid,运行报错

解决:

问:是因为Java除法向下取整的原因吗? 答:不是.

  • Java 的整数除法向下取整并不会直接导致越界。
  • 出现越界错误的根本原因是边界计算不准确,尤其是在循环的最后阶段。 看看什么是Java向下取整:
    Java 整数除法向下取整:当两个整数相除时,如果结果有小数部分,Java 会向下取整(即向负无穷方向取整),比如:
5 / 2 = 2   // 向下取整
-5 / 2 = -3 // 向下取整 

但是,回到我的代码中的 mid = right - (right - left) / 2;,这行代码本身并不会导致越界,因为: 我遇到的越界问题 通常是因为在循环过程中,leftright 的值已经非常接近,或者正好处于数组边界,而在进行下一次计算时没有正确判断边界情况。

错误的原因: 它和 mid = left + (right - left) / 2 结果是一样的。出错的根本原因是,在循环判断 left < right 时,可能会出现计算的 mid 值恰好位于数组的边界上,尤其是数组元素的数量非常小或者 leftright 差距非常小的时候。此时,mid 可能会访问到数组的非法索引,从而抛出ArrayIndexOutOfBoundsException

为避免这种问题,还是得使用 left + (right - left) / 2,因为它更加简洁且不会引发其他潜在的异常。

四、拓展练习

35.搜索插入位置
34.在排序数组中查找元素的第一个和最后一个位置
69.x 的平方根
367.有效的完全平方数


💻27. 移除元素

目标:暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。 双指针法 是本题的精髓,今日需要掌握,至于拓展题目可以先不看。
题目链接
文章讲解
视频讲解

一、题目和思考:

这里是题目的简述

TIPS

  • 思考

二、思路与题解:

思路

方法一:如果…
这里是方法一的思路
方法二:如果…
这里是方法二的思路

题解

三、难点的解决:

难点一XXX:

描述难点一

解决:

难点二XXX:

描述难点二

解决:

难点三XXX:

描述难点三

解决:

四、拓展练习

拓展练习
拓展练习
拓展练习
拓展练习


💻977.有序数组的平方

目标:本题关键在于理解双指针思想
题目链接
文章讲解
视频讲解

一、题目和思考:

这里是题目的简述

TIPS

  • 思考

二、思路与题解:

思路

方法一:如果…
这里是方法一的思路
方法二:如果…
这里是方法二的思路

题解

三、难点的解决:

难点一XXX:

描述难点一

解决:

难点二XXX:

描述难点二

解决:

难点三XXX:

描述难点三

解决:

四、拓展练习

拓展练习
拓展练习
拓展练习
拓展练习


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bingo冰冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值