LeetCode算法刷题(python) Day1|01数组|704. 二分查找、27.移除元素

目录

    • LeetCode 704. 二分查找
      • 解题思路
        • 左闭右闭[left, right]
        • 左闭右开[left, right)
      • 相关题目推荐
        • LeetCode 35. 搜索插入位置
    • LeetCode 27. 移除元素
      • 解题思路
        • 暴力解法
        • 双指针法

文档讲解:代码随想录 二分查找、代码随想录 移除元素
视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找、 数组中移除元素并不容易! | LeetCode:27. 移除元素

LeetCode 704. 二分查找

力扣题目链接
题目描述:给定一个n个元素有序的(生序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1

示例1

输入:nums = [-1,0,3,5,9,12], target = 9
输出:4
解释:9出现在nums中且下标为4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示

  1. 你可以假设nums中所有元素是不重复的。
  2. n将再[1, 10000]之间。
  3. nums的每个元素都将在[-9999, 9999]之间。

解题思路

  • 本题是有序数组,并且没有重复元素,因此可以用二分查找法。
  • 要注意区间的定义,有两种不同的写法,左闭右闭[left, right],或左闭右开[left, right)。
  • 时间复杂度: O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度: O ( 1 ) O(1) O(1)
左闭右闭[left, right]

这里定义target在一个左闭右闭的区间里,[left, right],需要注意:

  • while left <= right这里要用<=,因为left == right时区间[left, right]也是有意义的;
  • if nums[mid] > target: right = mid - 1,这里要给right赋值mid - 1,因为此时nums[mid]一定不是target,那么闭区间的右界从mid - 1就可以了

代码如下:

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

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

这里定义target在一个左闭右开的区间里,[left, right),需要注意:

  • 初始的区间右界right = len(nums),这里不是len(nums) - 1,因为这里是左闭右开区间,右界不包含在查找范围内;
  • while left < right这里要用<,因为left == right时,[left, right)是一个空的区间,没有意义;
  • if nums[mid] > target: right = mid,这里给right赋值mid,因为区间是右开的,所有右界赋值的mid是不包含在内的,若赋值为mid - 1则会漏掉nums[mid - 1]是否为target的判断。

代码如下:

class Solution:
    def search(self, nums: List[int], target: int) -> 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
            else:
                left = mid + 1
        return -1

相关题目推荐

LeetCode 35. 搜索插入位置

力扣题目链接
题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4

提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums 为 无重复元素 的 升序 排列数组
-10^4 <= target <= 10^4

其实二分法的思路还是很清晰的,唯一就是如果数组没有target,返回的下标是什么。其实while终止的时候一定是left > right了,此时left指向的是比target小的数的右边一位,right指向的是比target大的数的左边一位,说明此时nums[right]nums[left]中间刚好是需要插入的位置,并且此时left = right + 1,所以插入的位置为leftright + 1
LeetCode算法刷题(python) Day1|01数组|704. 二分查找、27.移除元素_第1张图片

代码如下:

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

LeetCode 27. 移除元素

力扣题目链接
题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O ( 1 ) O(1) O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例1

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入: nums = [3,2,2,3], val = 3
输出: 2, nums = [2,2]
解释: 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

提示

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

解题思路

暴力解法

本题最直接的方法就是暴力解法,两层for循环,一层for循环遍历数组,当遇到要删除的值时,另一层for循环更新数组。

  • 时间复杂度: 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:
        i, l = 0, len(nums)
        while i < l:
            if nums[i] == val: # 找到等于目标值的节点
                for j in range(i+1, l): # 移除该元素,并将后面元素向前平移
                    nums[j - 1] = nums[j]
                l -= 1
                i -= 1
            i += 1
        return l
双指针法

快慢指针:通过一个快指针和一个慢指针在一个for循环下完成两个for循环的工作。

  • 快指针:寻找新数组的元素,新数组就是不包含目标元素的数组
  • 慢指针:指向更新新数组元素的下标
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

删除过程如下(gif来源于代码随想录):

代码如下:

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

今日毕,后面补上相关题目推荐。

你可能感兴趣的:(LeetCode算法刷题,算法,leetcode,python)