代码随想录 数组模块小结

从今天开始二刷代码随想录,想着做一些阶段性的总结,之前一刷的时候只是每一天会做一些记录,但没有对某一章节的整理,现在从数组篇开始,相当于是做笔记了,以后也方便自己回顾。

数组篇主要包含的重要知识点为二分法双指针,以及滑动窗口。

1、二分法

以LC_704为例

704. 二分查找 - 力扣(LeetCode)

注意,二分法的适用前提是数组一定是严格有序的。

二分法用于数组中快速搜索某一元素,需要定义左右边界以及中间下标middle

int left = 0;
int right = nums.length;
int middle;  // 左闭右开

 二分法的核心就是在循环中动态更新middle的值,使搜索范围不断减小

middle = left + ((right - left) >> 1);   // 每次更新middle 取right和left的中点

之后就是比较目标值与nums[middle]的大小关系,并动态更新middle

完整代码如下,要注意边界问题,如果是左闭右开的话,while循环里一定是left < right,因为left==right是没有意义的。

class Solution {
    public int search(int[] nums, int target) {
        if (target < nums[0] || target > nums[nums.length - 1]) return -1;
        int left = 0;
        int right = nums.length;
        int middle;  // 左闭右开
        while (left < right) {
            middle = left + ((right - left) >> 1);   // 每次更新middle
            if (target > nums[middle]) {
                // nums[middle]比target小 下一次左边界为middle + 1 因为是闭区间 不能取middle
                left = middle + 1;   
            }else if (target < nums[middle]) {
                // nums[middle]比target大 下一次右边界为middle,因为开区间取不到middle
                right = middle;
            }else return middle;  // 相等 直接返回该下标
        }
        return -1;   // 没找到
    }
}

2、双指针

双指针涉及的题目类型有很多,滑动窗口本质也是双指针

这里就以LC_27为例

27. 移除元素 - 力扣(LeetCode)

双指针的重点是搞清楚,两个指针什么时候该动,什么时候不动。

这道题使用的是快慢指针,也是双指针的一种

做法是fast指针自然向后移动,low指针移动的时机为,当nums[fast] != val时,移动low,并进行操作

完整代码如下,边界使用的是左闭右闭

class Solution {
    public int removeElement(int[] nums, int val) {
        int low = 0;   // 快慢指针
        int fast = 0;
        while (fast < nums.length) {
            if (val != nums[fast]) {   // 一样就fast向前 不一样就将fast的值赋给low的值 再让low向前
                nums[low++] = nums[fast];
            }
            fast++;
        }
        return low;
    }
}

该代码的逻辑用卡哥提供的动图解释非常清晰。

3、滑动窗口

滑动窗口本质也是双指针,只不过重点处理的是双指针之间的值,所以称为滑动窗口。

以LC_209为例

209. 长度最小的子数组 - 力扣(LeetCode)

sum为窗口内的数值之和,当sum大于等于target时,说明需要慢指针不断向右移以减小sum的值,同时缩短窗口的长度;当sum小于target时,说明需要快指针不断向右移动以增加sum的值,以得到sum=target的子数组的窗口。

sum的变化一定与窗口的变化是同步的。

因为要获得的是最小长度的子数组,所以每次缩短窗口时都需要更新窗口大小。

完整代码如下

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        
        int i = 0;   // 滑动窗口 或叫双指针
        int j = 0;
        int sum = 0;   // 累计和
        int subArrLen = 0;  // 每次保存j - i的值 即窗口长度
        int res = Integer.MAX_VALUE;   // 结果
        while (j < nums.length) {
            // 内部两个循环模拟窗口移动
            while (sum < target && j < nums.length) {  // 要用while
                sum += nums[j];
                j++;
            }
            while (sum >= target) {   // 要用while
                subArrLen = j - i;
                res = Math.min(res, subArrLen);  // 取最小的长度
                sum -= nums[i];  // 左窗口准备移动 要先减去i对应加的值
                i++;
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}

依然要注意边界,这里使用的是左闭右闭

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值