代码随想录LeetCode | 数组刷题总结01

前沿:撰写博客的目的是为了再刷时回顾和进一步完善,其次才是以教为学,所以如果有些博客写的较简陋,是为了保持进度不得已而为之,还请大家多多见谅。

预:看到题目后的思路和实现的代码。

见:参考答案展示。

感思:对比答案后的思考,与之前做过的题目是否有关联。

行:

(1)对于没做出来的题目,阅读答案后重新做一遍;

(2)下次做题可以尝试改善的方向;

(3)有助于理解的相关的题目

优先级:做题进度>学习&总结>默写回顾>做题数量

数组相关题目链接

数组基础知识

为什么使用数组?

对于同一类型数据能够一次性创建足够大的空间存储在一起,像一个班的成绩能够通过一个数组全部存储。

数组的定义:存储一堆相同数据类型在连续的地址中

  • 数组参考定义:存放在连续地址相同类型数据的集合,并且其下标从0开始。
  • 连续地址存储→下标获取效率高→任意插入和删除/覆盖时效率低→链表插入删除效率高

如何使用数组?数据类型[] 名称 = new 数据类型[];数组下标从0开始

704. 二分查找

题目链接:704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums中并且下标为 4
  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

1.阅读题目看到有序+元素不重复→二分查找

2.撰写大体思路+特殊情况

大体思路:双指针left,right,middle,while判断在哪边就往哪边偏

特殊情况:nums.length = 0,return -1;1,if判断;

class Solution {
    public int search(int[] nums, int target) {
        // 双指针left,right,middle,while判断在哪边就往哪边偏
        // 特殊情况:nums.length = 0,return -1;1,if;
        if(nums.length == 0){
            return -1;
        }else if(nums.length == 1){
            if(nums[0] == target){
                return 0;
            }else{
                return -1;
            }
        }
        int left = 0;
        int right = nums.length-1;
        int middle;
        // 左闭右闭
        while(right >= left){ //是否要添加等号?
            middle = (right+left)/2;
            // System.out.println(middle);
            if(nums[middle] < target){
                left = middle+1;
            }else if(nums[middle] > target){
                right = middle-1;
            }else{
                return middle;
            }
            // cout << left << " " << right << endl;
        } 
        return -1;
    }
}

阅读答案,实现另一种思路:左闭右开

class Solution {
    public int search(int[] nums, int target) {
        if(nums.length == 0){
            return -1;
        }else if(nums.length == 1){
            if(nums[0] == target){
                return 0;
            }else{
                return -1;
            }
        }
        int left = 0;
        // 左闭右开
        int right = nums.length;
        int middle;
        while(right > left){
            middle = left+((right-left)>>1);
            // middle = (right+left)/2+1;
            if(nums[middle] == target){
                return middle;
            }else if(nums[middle] < target){
                left = middle+1;          
            }else{
                right = middle;      
            }
        }
        return -1;
    }
}

感思

发现自己考虑的特殊情况不能真正起到作用,但我认为有意识地思考特殊情况是必要的。

相比参考答案,将target两端值比大小,能更快速地排除极端的情况。

// 参考答案
if(target < nums[0] || target > nums[nums.length - 1]){
    return -1;
}

左闭右开总结:

小结:

1.右开表示右边是取不到的值

right = nums.length→while (left < right)→right = middle(instead of middle+1

2.画图>>脑子想

出错:在实现左闭右开时,脑子已经有点疲倦了,此时撰写判断条件后的right和left赋值写反了。

习惯性用脑子想判断关系,但实际上对于我来说是很容易出错的;每次遇到需要思考一下的情况,就尽量在草稿纸上写下来判断,遇到区间赋值时,则采用画区间显示化的方式。

有空再刷:

35.搜索插入位置

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

27. 移除元素

题目链接:27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50

发现二刷反而出现逻辑问题没有写出来。

1.阅读题目,不要忽视提示部分,因为其会给相关范围。

2.先撰写大体思路+特殊情况

两边双指针法:假设left和right,left值等于val时,先判断right值是否等于val,等于val,right-1,直到不等于val时再往内移动一格,并交换值。

特殊情况:nums.length = 0,return 0;1,if判断;

class Solution {
    public int removeElement(int[] nums, int val) {
        // 假设left和right,left值等于val时,先判断right值是否等于val,等于val直接-1,直到不等于val时再往内移动一格,并交换值。
        // 特殊情况:nums.length = 0,return 0;1,if判断;
        if(nums.length == 0){
            return 0;
        }
        if(nums.length == 1){
            if(nums[0] == val){
                return 0;
            }else{
                return 1;
            }
        }
        // 两边双指针法
        int left = 0;
        int right = nums.length-1;
        int temp;
        while(right >= left){
            if(nums[left] == val){
                while(right > 0 && nums[right] == val){
                    right--;
                }
                if(right > left){
                    temp = nums[left];
                    nums[left] = nums[right];
                    nums[right] = temp;
                }else{
                    return left;
                }
                left++;
                right--;
            }else{
            left++;
            }
        }
        if(right == left && nums[right] == nums[left]){
            return left;
        }
        return left;
    }
}

见:阅读答案后,实现另一种版本:快慢指针

class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0){
            return 0;
        }
        if(nums.length == 1){
            if(nums[0] == val){
                return 0;
            }else{
                return 1;
            }
        }
        // 快慢双指针法
        int slow = 0;
        for(int fast = 0;fast < nums.length;fast++){
            if(nums[fast] != val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

感思

移除元素:数组内存地址连续不能单独移除某元素,只能覆盖

双指针法(快慢指针法):

通过双指针法一个for循环下完成两个for循环的工作

左右双针法实现过程中的两次报错:

1. 未考虑两边迭代时,可能出现空指针的情况

while(nums[right] == val) ,加上right > 0

2.有时会少输出一个

while(right >left):需要考虑相等的情况,否则可能出现相等时又不等于的情况时,新数组则会漏掉一个值。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/binary-search
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二刷总结

  1. 懒的习惯是顽固的
    1. 二刷时认为题目简单,实现后出现细节问题,总想着脑子想一下修改就能出答案,结果改bug改到头晕都没写出来。
    2. 回顾一刷时这篇文章就有提到“画图 > 脑子想”,但二刷时又忘记了,所以还是要对所有题目都保持谨慎,出现逻辑问题,则一定要写/画出来寻找问题在哪,而不是偷懒只用脑子想。
    3. “大体思路+特殊情况”的思考逻辑也是被忘得一干二净,想着简单就只思考了大体思路,发现a出来也就没有去思考是否有特殊情况需要判断了。
  2. 要抓住主要问题,而不要困在某个细节实现中。
    1. 当思路可行,但实现总是有问题时,要思考我这题的关键是什么?
    2. 像移动元素,若以左指针作为更新指标,则要以为主要判断依据,而不是先考虑右指针的情况。
    3. 治标不治本快,实际上却是事倍功半→不能总想着以解决没有通过示例来完善,不然多个示例有问题,每次都得再考虑。

三刷总结

  1. 题目都暗示只需保证前面的数组正确和返回新长度即可→快慢指针直接覆盖
  2. 思维是顽固的三刷移除元素每次解题思路都选择了更困难的方式,不专注直接没不出来。

 做题记录

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值