代码随想录算法训练营DAY1 | 704. 二分查找、27. 移除元素

数组基础+常用方法

数组是存放在连续内存空间上的相同类型数据的集合。

  • 数组下标都是从0开始的。 [0,length-1]

正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。

数组的元素是不能删的,只能覆盖。

要注意:Java中二维数组地址 每一行之间都不是连续的


常用方法:

获取数组长度:数组名.length


704.二分查找

文章链接:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

视频链接:https://www.bilibili.com/video/BV1fA4y1o715/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=80cf8293f27c076730af6c32ceeb2689

第一想法:题目给了升序的数组nums,且元素不重复,以及目标值target。用二分,while循环,不断缩小区间范围。但区间的开闭和while条件不太明确。

思路讲解

二分法条件:有序数组+无重复元素

关键:

  • while(left ? right) 是<还是≤

  • 更新下标是 = middle,还是 = middle-1?

一般写二分法的时候,要么左闭右闭[left,right],要么左闭右开[left,right]对区间的定义会影响到边界处理!

区间的定义要作为循环不变量,贯穿整个代码。

左闭右闭写法

left = 0,right = nums.length

  • 接下来先看while条件有没有=

首先要明确:能进入到while循环里的,一定是个合法的区间(符合左闭右闭的定义)

left=right的情况合不合法呢?[left,left] 是合法的。所以应该是 ≤

mid = (left + right) / 2

计算出mid值后,一共有三种情况:

  • nums[mid] < target mid下标的值小于target,说明target在mid右侧,缩小到右半区间,更新left
  • nums[mid] > target mid下标的值小于target,说明target在mid右侧,缩小到右半区间,更新right
  • nums[mid] = target mid下标就是我们要找的值,直接return

第一种情况,nums[mid]已经不是要找的值了,新的区间里不能包含它。结合左闭右闭条件,新的right=mid-1

其他两种情况同理。

  • 如果退出while了都还没找到,则返回-1

在这里插入图片描述

  • 左闭右闭完整代码:
class Solution {
    public int search(int[] nums, int target) {
        int mid;
        int l = 0,r = nums.length-1;
        while(l <= r) {
            mid = (l + r) / 2;
            if(nums[mid] > target) r = mid - 1;
            else if(nums[mid] < target) l = mid + 1;
            else return mid;
        }
        return -1;
    }
}

左闭右开写法

[left, right)

left = 0,right = nums.length 这样才是[0,nums.length)

  • 首先我们先考虑while条件里有没有等号?

能进到while里的一定是合法的。考虑left=right的情况 [left,left) 显然是不合法的!

因此是while(left < right)

  • 接着考虑如何更新下标

    • 如果是nums[mid] < target的情况,要更新left。nums[mid]不是我们要的值,下一次循环里不能包含它。

      假设是left = mid + 1的话,新的区间就是[mid + 1,right)。mid不包含在新区间里。

      因此这种情况应该是left = mid + 1

    • 如果是nums[mid] < right的情况,要更新right。

      假设是right = mid + 1,新的区间就是[left,mid - 1),实际上多排除了一个mid-1,这是不对的。

      因此这种情况应该是right = mid

  • 左闭右开完整代码:

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

遇到的问题

左闭右开情况下,我第一次left和right还是写成0和nums.length-1,运行后报错

例如在数组只有一个元素的情况下,如[5],这样结果是-1.

因此==左闭右开要写成right=nums.length,[0,nums.length) 这样才能包含所有元素!==


27.移除元素

文章链接:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

视频链接:https://www.bilibili.com/video/BV12A4y1Z7LP/spm_id_from=333.788&vd_source=80cf8293f27c076730af6c32ceeb2689

第一想法:第一眼没仔细看题目,以为是升序数组,想着用双指针,但其实是无序的。再次考虑想法是,用标志数组,遍历到一个位置是val,就把标志数组当前位置设为空。依次往前填补,同时记录剩余元素个数。但感觉实现不了

数组理论基础

数组并不能删除一个位置上的元素,只能覆盖。新数组在内存里的空间还是原来的(虽然length变了)

其实数组的remove方法是O(n),不是O(1)!,是要将后面的元素整体往前移的

  • 对于库函数的使用:如果能直接用库函数解决问题,就不要用!

暴力法

两层for循环,第一层找到val,第二层把它后面的元素一个个往前覆盖掉

O(n^2)

双指针思路

用双指针可以O(n)解决,实际用一层for做了两层for的事情

  • 定义一个快指针fast,一个慢指针slow

快指针负责指向新数组里需要的元素(val以外的)

慢指针负责指向新数组的下标(让快指针找到的元素填进去)

  • slow = 0 fast指针要从0遍历到数组末尾,每找到一个≠ val的元素就把它赋给nums[slow],同时slow++

在这里插入图片描述

  • 最终直接return slow就是新数组的长度了。(这样想:fast每赋值一次给slow,slow++。一共有slow个值)

要真正理解快慢指针各自的含义!

  • Java代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        int size = 0;
        for(int fast = 0; fast < nums.length; fast++) {
            if(nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

个人体会

双指针法其实还是从暴力两层for出发,只不过把第二层for里,后面元素全部往前移动的操作,简化成O(1)的赋值操作。因为有了slow指针统计个数的同时指向新的数组,就不需要往前移了,fast直接赋值给当前位置。

今日总结

  • 学习时长:2小时
  • 题目难度:还好
    今天第一天主要是熟悉刷题的感受,学习了二分法左闭右闭和左闭右开两种写法。(以后自己写还是用左闭右闭,左闭右开要多考虑一点 比较麻烦)。双指针每次听完讲解都恍然大悟,以后自己写又不回了,要多积累双指针能运用的题型。
  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值