代码随想录算法训练营第1天(数组)| 704. 二分查找、27. 移除元素。

本文详细解析了LeetCode中的704. 二分查找问题,探讨了二分查找的逻辑重点,包括循环条件和边界处理。同时,介绍了27. 移除元素的解决方案,通过双指针法降低时间复杂度至线性。
摘要由CSDN通过智能技术生成

文章链接:

704. 二分查找

题目链接:力扣

文章讲解:代码随想录

视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

二分法:

        二分法查找适用于有序数组(有序是必要条件)。相比于线性搜索(Worst Case 的时间复杂度为O(n)),二分搜索通过不断将搜索区间一分为二,二分法查找可以将 Worst Case 的时间复杂度降低到 O(log_2n)。(两种算法的 Best Case 时间复杂度均为O(1))。

重难点:

        基础的二分法在逻辑上有两个让人捉摸不定的地方:

        1. 循环时是 while left < right 还是 while left <= right

        2. 更新区间时是 left = middle+1, right = middle-1 还是 left = middle, right = middle 或者 left = middle+1, right = middle 之类的

        关于这两个问题,代码随想录给出的解决方案是,搞清楚循环的“区间不变量”到底是左闭右闭还是左闭右开。这个解决方案很系统,但是我自己一开始理解起来还是稍微有点绕,甚至感觉有些牵强(可能是因为段位太低了),或许多理解几遍之后会发现这其中的精妙之处。我们先来讲解和总结一下这个循环区间不变量的方式,然后再记录一下我自己的理解。

        首先我们要决定我们循环的区间是左闭右闭的,还是左闭右开的。这个所谓“循环的区间”就是我们要在哪个区间里搜索我们想要的目标值。而这个区间的开闭一旦定下来,就不要改变,在整个搜索过程中要贯彻到底。

        如果区间是定成左闭右闭,那意味着我们之后将永远在一个左闭右闭的区间里寻找目标值,直到找到或确定目标值不存在。因此,当考虑是 while left < right 还是 left <= right 时,应该采用 while left <= right,因为当 left == right 时,区间定义是成立的。然后来解决 left/right = middle (+/- 1) 的问题。如果定义了区间是左闭右闭,那么如果有下一轮搜索,left/right 应该等于 middle-1/+1,而不是 middle 本身,因为下一轮会在新的 [left, right] 区间里进行搜索,而新的 left 和 right 作为边界值是会被搜索到的,因此我们不能把已经检查过的 原middle 设成 新left/right,因为 原middle 已经被检查过不是 target 了。

        而如果区间是左闭右开,那么 while left <= right 在区间定义上是不成立的(e.g., [3, 3) 这个区间不存在)。而考虑更新区间时,left 还是等于 middle+1,因为“左闭”意味着每次搜索时,左边界是要被搜索到的,所以如果 原middle 被检测为不是 target,那么 新left 可以直接等于 原middle+1。也就是说新一轮的搜索时,新left 会被检测到,而 原middle 已经被检测过了,所以不需要再检测一遍 原middle,直接从 原middle+1 开始就可以了。但是 right 要等于 middle 而不是 middle-1,因为“右开”意味着在新一轮的检测中,右边界不会被检测到,而 原middle 被检测过,所以应该检测的值是 原middle-1,那右边界就应该是 原middle(e.g., [3, 6) 这个区间实际被检测的值只有3,4,5,没有6)。

        后来发现一位录友总结得很精炼,稍微改动一下,当 right = len(nums) - 1 时,每次的搜索区间为 [left, right] (左闭右闭),此时对应的循环条件应为 while left <= right,终止条件为 left == right + 1,即 [right + 1, right],此时区间为空,故循环终止,程序返回 -1 即可;而当 left == right 时,[right, right] 这个区间还需要继续被搜索,直接返回-1是不对的。当 right = len(nums) 时,每次的搜索区间为 [left, right) (即左闭右开),此时对应的循环条件为 while (left < right),终止条件为 left == right,即 [right, right),此时区间不成立,跳出循环。(原文链接:https://blog.csdn.net/weixin_44605962/article/details/128837397)

        上面应该是代码随想录给出的思路。我感觉有点绕(可能因为这道题太基础,就感觉没必要这么绕,但是遇到困难的复杂的问题可能还是有必要有一个系统的解决方案的),希望多理解几遍之后会好一些。我自己原先的思路比较浅薄且暴躁,永远都是 left = middle+1, right = middle-1,然后你想边界值嘛,如果 left = middle+1, right = middle-1 了,搜索到最后就会出现 left == right 的情形(e.g., [3, 4], left = 3, right = 4, middle = 3, 如果 target 是4,那 middle < target,就需要 left = middle+1 = 4,这样就 left == right 了)。既然会出现 left == right 的情形,那就需要继续看这个 left == right 是不是 target,所以这时不能跳出循环。所以循环的时候要 while left <= right。

27. 移除元素

题目链接:力扣

文章讲解:代码随想录

视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili

数组:

        由于数组的存储地址是连续的,删除元素的实质是元素的不断替换。即若想删除数组 [1,2,3,4] 里的2,实质是将2换成3,将3换成4,新数组为 [1,3,4,4],而编程语言会对新数组进行处理,只返回前三项 [1,2,3] 而已

暴力双循环:

        首先遍历所有值,然后,当发现遍历到的值与想要删除的值相等时,再遍历该值和后面剩下的所有值,逐个进行替换。时间复杂度为O(n^2)

双指针:

        通过快慢指针,用一个循环完成两个循环的工作。需要搞清楚快慢指针的定义:

        慢指针:指向原数组中需要替换的元素/位置;

        快指针:寻找新数组中的元素,即用来替换原数组中删除的值的元素。新数组即不含有目标元素的数组。

        因此,只需要将快指针指向的元素替换掉慢指针指向的元素即可。时间复杂度降为O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值