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

704. 二分查找 (Binary search)

题目链接解题思路

二分查找最重要的就是坚持同一个思路,在循环中必须一直保持不变量成立。

Debug 的重要方法:在小数组(例如[],[1, 2][1, 2, 3])上进行测验,没能保持不变量的解法一般会陷入死循环或是错误的答案。

注意 int overflow:mid_idx = left_idx + (right_idx - left_idx >> 1)

左闭右闭

搜索范围:[left_idx, right_idx]
Invariant: left_idx <= target_idx <= right_idx

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left_idx = 0
        right_idx = len(nums) - 1
        while (left_idx <= right_idx):
            mid_idx = left_idx + (right_idx - left_idx) // 2        # prevent overflow
            if (nums[mid_idx] < target):
                left_idx = mid_idx + 1
            elif (nums[mid_idx] > target):
                right_idx = mid_idx - 1
            else:
                return mid_idx
        # if exit the while loop, means left_idx > right_idx and no target is found
        return -1

左闭右开

The search range is [left_idx, right_idx).

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left_idx = 0
        right_idx = len(nums) 
        while (left_idx < right_idx):
            mid_idx = left_idx + (right_idx - left_idx) // 2        # prevent overflow
            if (nums[mid_idx] < target):
                left_idx = mid_idx + 1
            elif (nums[mid_idx] > target):
                right_idx = mid_idx 
            else:
                return mid_idx
        # if exit the while loop, means left_idx > right_idx and no target is found
        return -1

35. 搜索插入位置

题目链接

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left_idx = 0
        right_idx = len(nums) - 1

        while (left_idx <= right_idx):
            mid_idx = left_idx + (right_idx - left_idx >> 1)
            if (nums[mid_idx] < target):
                left_idx = mid_idx + 1
            elif (nums[mid_idx] > target):
                right_idx = mid_idx - 1
            else:
                return mid_idx
        
        # exiting the loop, must have left_idx = right_idx + 1
        return left_idx

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

题目链接 | 解题思路

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        left_idx = 0
        right_idx = len(nums) - 1

        while (left_idx <= right_idx):
            mid_idx = left_idx + (right_idx - left_idx >> 1)
            if (nums[mid_idx] < target):
                left_idx = mid_idx + 1
            elif (nums[mid_idx] > target):
                right_idx = mid_idx - 1
            else:
                left_end_idx = right_start_idx = mid_idx
                while (left_idx < left_end_idx and nums[left_idx] != target):
                    left_mid_idx = left_idx + (left_end_idx - left_idx >> 1)
                    if nums[left_mid_idx] != target:
                        left_idx = left_mid_idx + 1
                    else:
                        left_end_idx = left_mid_idx
                while (right_idx >= right_start_idx and nums[right_idx] != target):
                    right_mid_idx = right_start_idx + (right_idx - right_start_idx >> 1)
                    if nums[right_mid_idx] != target:
                        right_idx = right_mid_idx - 1
                    else:
                        right_start_idx = right_mid_idx + 1
                return [left_idx, right_idx]

        return [-1, -1]

27. 移除元素 (Remove Element)

题目链接 | 解题思路

暴力算法

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        num_vals = 0
        i = 0
        while (i < len(nums) - num_vals):
            if nums[i] == val:
                num_vals += 1
                for j in range(len(nums) - i - num_vals):
                    nums[i+j] = nums[i+j+1]
            else:
                i += 1
        return len(nums) - num_vals

以下方法更简单,但是需要sort()

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        num_vals = 0
        for i in range(len(nums)):
            if (nums[i] == val):
                nums[i] = float("inf")
                num_vals += 1
        nums.sort()
        return len(nums) - num_vals

双指针 / 快慢指针 - 经典解法

  • fast_idx: 快指针负责获取数组信息(探路)
  • slow_idx: 慢指针根据快指针的结果决定是否复制快指针的结果(保留元素)
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

快慢指针保持了数组元素的相对位置,但缺点是很容易出现worst-case(只要第一个元素是目标值,就会出现 n n n FLOPs)

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow_idx = 0
        for fast_idx in range(len(nums)):
            if nums[fast_idx] != val:
                nums[slow_idx] = nums[fast_idx]
                slow_idx += 1
        return slow_idx

双向指针

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

双向指针中,每当从左向右遇到一个需要删除的目标值,就从数组从右向左地找到一个非目标值填入,从而最大程度地减少所需 FLOPs。但双向指针不能保持数组元素顺序。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        head_idx = 0
        tail_idx = len(nums) - 1
        while (head_idx <= tail_idx):
            if nums[head_idx] != val:
                head_idx += 1
            elif nums[tail_idx] != val:
                nums[head_idx] = nums[tail_idx]
                head_idx += 1
                tail_idx -= 1
            else:
                tail_idx -= 1
        return head_idx 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值