算法刷题打卡001 | 二分查找,移除元素

LeetCode 704 二分查找

题目链接:704. 二分查找 - 力扣(Leetcode)

二分查找是数组的典型题目,一刷代码随想录以及后续力扣做题过程中经常遇到,时间复杂度为O(logn),空间复杂度O(1)。while循环的条件中,是否加等号取决于区间的特性,对于左闭右闭区间:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1

        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        return -1

 而Python数组区间默认为左闭右开,去掉等号的同时,right指针的写法也会略有不同:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1

        while left < right:
            mid = (left + right) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                right = mid
            else:
                left = mid + 1
        return -1

 二分的原理掌握之后,对于Python用户来说,调用内置的bisect模块会更方便(特别是当二分只是解题的一小部分的时候)。

bisect文档链接:bisect — Array bisection algorithm — Python 3.11.2 documentation

‘This module provides support for maintaining a list in sorted order without having to sort the list after each insertion.’ 也就是说该模块用二分的方式维护一个有序数组,插入元素时不改变原数组有序的特性。

主要方法:bisect.bisect_left,bisect.bisect_right,bisect.bisect,bisect.insort_left, bisect.insort_right,bisect.insort,前三个均为查找某元素x在数组中的插入位置(下标),区别在于,x已在数组中时,left返回的是已有x最左下标,而right和默认的bisect返回最右下标;后三个为查找操作对应的插入操作,调用时会先执行对应的bisect找到插入位置,然后执行insert方法插入元素(Keep in mind that the O(logn) search is dominated by the slow O(n) insertion step.)

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

 题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(Leetcode)

 注释部分是之前的做法,手写二分查找,找到其中一个target值之后,分别向前向后找到target的边界,这次回顾题目并练习用内置bisect模块完成。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if not nums:
            return [-1, -1]
        # left, right = 0, len(nums) - 1
        # while left <= right:
        #     mid = left + (right - left) // 2
        #     if target > nums[mid]:
        #         left = mid + 1
        #     elif target < nums[mid]:
        #         right = mid - 1
        #     else:
        #         # nums[mid] == target
        #         i, j = mid, mid
        #         while i >= 0 and nums[i] == target:
        #             i -= 1
        #         while j < len(nums)  and nums[j] == target:
        #             j += 1
        #         return [i + 1, j - 1]
        
        # return [-1, -1]

        # bisect模块
        left = bisect.bisect_left(nums, target)
        right = bisect.bisect_right(nums, target)
        return [left, right-1] if left != right else [-1, -1]
        # 当left==right时说明target不在nums中,所以找到的插入位置是一样的
        # 假设target在nums中只有一个且下标为i,left=i,right=i+1

LeetCode 27 移除元素

题目链接:27. 移除元素 - 力扣(Leetcode)

 题目中明确要求需原地修改数组,暴力解法对每找到一个val,将其后所有元素整体往前移一位,需要两层for循环遍历,时间复杂度是O(n^2):

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

有些要注意的点:用原数组长度减一的方式记录数组长度变化,需在移动元素之后;用while循环更好控制指针i的变化(val之后的数组整体往前移之后不能执行i+=1的操作,还要比较移动之后i位置上的值),当然也可以像注释中的在for循环nums[i] == val时让i -= 1,下一轮判断对象仍旧是nums[i]。

之前刷过题,所以知道双指针解法,它的奇妙之处在于,当前遍历的nums[i]不等于val时才进行覆盖和left指针后移操作,等于val时不做任何操作,保证left直接记录移除val之后的数组长度,时间复杂度优化到O(n)。

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

 今天任务量不大,复习基本算法,主要都花时间在博客记录上了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值