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

📝704(Easy)

题目链接:  - LeetCode

文章讲解:代码随想录

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


👩‍💻思路:binary search

用二分查找的方法可以巧妙地加快寻找到target的时间因为我们不再需要遍历整个数组去查找。方法分两种:左右闭合和左闭右开,但是大致思路是一致的。首先,我们要初始化的左端和右端的位置(index)计算出mid;随着范围的逐步缩小,确定target在哪,如果没找到则返回-1。

  •    左右闭合:"while left<=right" 这是因为区间闭合,left可以等于right。因此,right的初始值设置为数组中最后一位 "right = len(nums) - 1"。将mid与target进行比较,如果target大,那么左端更新为当前的mid向后顺延一位的value,反之,右端更新为mid向前顺延一位的value。
  •   左闭右开:"while left < right" 为了使其边界合法,右边打开不包含,所以left不可以等于right。right的初始值设置为数组中最后一位的后面 "right = len(nums)"。也就是说,左端不可能等于一个根本不存在与数组里的数。将mid与target进行比较,如果target比mid小,那么新的right变为当前的mid,反之,将左端更新为当前的mid向后顺延一位的value。

❌易错点/难点总结:

 1.避免整数溢出,mid = left + (right - left) // 2 很重要。不可以写成mid = right//2,他只适用于left=0的情况。因为我们要考虑到left的更新。此外,要记住//的优先级大于+。我找到一个例子,如下:

3.分不太清什么时候该用if,elif,else等等

  • `if` 用于第一次检查的条件。
  • `elif` 用于后续检查的条件,每个 `elif` 都是一个新的独立条件。
  • `else` 用于捕获所有前面条件都不满足的情况,作为一种默认处理机制。
  • tips:如果你有多个条件需要检查,你可以使用多个连续的 elif 语句来处理所有可能的情况。每个 elif 语句都会在前面的 ifelif 条件不满足时进行检查。如果最后还有未处理的情况,可以使用 else 来捕获所有剩余的可能性。请看以下的小例子:

4.更新left和right的值的时候是based on mid+1或-1而不是根据他们本身值的变动,在这一点上可以谨记抛弃原来有关左右端的value。

📊视频讲解要点总结:

  • 大家面临选择困难的两个方面:1.该用while(left <= right)作为判断条件还是while(left < right)呢?;2.当我们的mid>target时,右端应该怎么更新,是设置为middle还是middle - 1呢?
  • 如何定义区间:这个问题和上一个紧密相连。也就是说,只有确定了区间定义,才能解答我们针对上个问题发出的疑问。一般,我们有两种选择,分别是左右闭合和左合右开。我们要先选择用哪个区间解题,区间的选择会影响:1.right初始化的value;2.边界的处理;3.while后的判断条件
  • 左右闭合:在这种情况下,左右顶端的值都是包含在里面所以while(left <= right)。举个例子,当m大于target的时候,我们需要缩小右端,应该选择right=m-1。如果设置right = m的话,已经判断当前这个m所在的位置大于target,所以他肯定不是我们要找的位置,因此,要缩小一位形成新的区间,应该把这个旧的过滤掉了。
  • 左合右开:右端不包含所以左右相等没有任何意义,因此while(left < right)。假设m大于target,我们需要缩小右端的范围,选择right = m 。因为right本来就不包含原本的m(开区间过滤掉了),所以r=m就能实现一个新的区间范围。注意,如果错误用了m-1,我们也许会错漏一个element。

📸解题代码:

1.左合右开

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        left = 0
        right = len(nums)
       
        while left < right:
            mid = left + (right-left)//2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                right = mid
            elif nums[mid] < target:
                left = mid + 1

        return -1

2.左右闭合

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        left = 0
        right = len(nums)-1
        #这里用<=是因为这是一个闭合区间(由于right = len(nums)-1决定的
        while left <= right:
            mid = left + (right - left)//2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
        
        return -1

 📝27(Easy)

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

题目链接:. - 力扣(LeetCode)

文章讲解:代码随想录

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


👩‍💻思路:

1.暴力解法:
在 `while` 循环中,不断扫描列表元素,当发现目标值时执行移除操作。移除操作包括将目标值元素后面的元素向前平移一位,并更新列表长度 `l`。在移除操作后,为了处理连续目标值的情况,需要手动将 `i` 减去 1,以确保下一次循环在当前位置进行扫描。最后返回更新后的列表长度 `l`,即为移除目标值后的列表长度。

2.双指针:

左指针 slow 指向下一个非目标值元素的位置,右指针 fast 遍历整个列表,当发现非目标值时,将其放到 slow 指向的位置,并递增 slow。如果没有目标值,那么继续fast遍历后面的元素,slow保持在原位。最后返回 slow,即为移除目标值后列表的长度。

❌易错点/难点总结:

1. 在写暴力方法时,忘记手动给i减1。这一步不能省略因为在移除操作后旧的i对应新的元素,但是最后一步会给i加1,如果忘记提前剪去1的话,在遇到连续重复元素时会出错。

2. 双指针,nums[slow]=nums[fast]。感觉赋值的这一步一开始很难理解,不过可以想象成slow是坑,fast是萝卜,遇到合适的坑,就把fast放进去,没有合适的就继续寻找更合适的fast萝卜。

📊视频讲解要点总结:

暴力法是需要double for loops;双指针法将double for loops缩减为single,先遍历再覆盖。

📸解题代码:

1.暴力:

#暴力法
#时间复杂度:O(n^2)
#空间复杂度:O(1)
class Solution(object):
    def removeElement(self, nums, val):
        """
        :type nums: List[int]
        :type val: int
        :rtype: int
        """
        i = 0
        end = len(nums)-1
        while i < end:
            if nums[i] == val:
                for j in range(i+1,end):
                    nums[j-1]=nums[j]
                end -= 1 #向前移一位后数组少了一位数
                i -= 1   #i需要更新因为避免连续重复的情况
            i += 1
        return end #返回返回 nums 中与 val 不同的元素的数量

2.双指针:

#双指针法
#时间复杂度:O(n)
#空间复杂度:O(1)
class Solution(object):
    def removeElement(self, nums, val):
        """
        :type nums: List[int]
        :type val: int
        :rtype: int
        """
        slow = 0
        for fast in range(len(nums)):
            if nums[fast]!= val:
                nums[slow]=nums[fast]
                slow += 1
        return slow
  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了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题,题目要求在给定的数组中找到长度最小的子数组

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值