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

420 阅读3分钟

数组理论基础

  • 数组是存放在连续内存空间上的相同类型数据的集合

    • 数组的下标从0开始
    • 数组的内存空间是连续的
  • 因为数组的内存空间地址是连续的,所以删除/添加元素的时间复杂度是O(n)

  • 数组的元素是不能删的,只能覆盖。

  • 不同语言对于数组的实现是不一样的


704. 二分查找

代码随想录视频讲解

代码随想录文章讲解

二分查找的区间问题

左闭右闭

  • 赋初值:left, right = 0, len(nums) - 1

    因为是闭区间,所以左右指针都应该指向能取到的区间。(数组的index范围[0, len(num-1)]

  • while循环更新:left = mid + 1; right = mid - 1

    因为判断条件是targetnums[mid]比较,当结果是不等时才进行更新,所以mid是不应该存在于下次循环的查找区间范围的,所以left和right都不取mid的值

  • while条件判断:left <= right

    因为左右闭区间,右指针指向的index是左指针可以取到的,所以是小于等于

左闭右开

  • 赋初值:left, right = 0, len(nums)

    因为是右开,所以右指针应该指向能取到的区间+1的地方。

  • while循环更新:left = mid + 1; right = mid

    因为判断条件是targetnums[mid]比较,当结果是不等时才进行更新,所以mid是不应该存在于下次循环的查找区间范围的。因为右是开区间,所以右指针应该指向取不到的mid。

  • while条件判断:left < right

    因为右是开区间,右指针指向的index是左指针取不到,所以不是小于等于

区间左闭右闭

# Time complexity: O(logN)
# Space complexity: O(1)
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
​
        while left <= right:
          # floor division
            mid = (left + right) // 2
            if target < nums[mid]:
                right = mid - 1
            elif target > nums[mid]:
                left = mid + 1
            else:
                return mid
        return -1

区间左闭右开

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

27. 移除元素

代码随想录视频讲解

代码随想录文章讲解

暴力解法:双循环

  • 第一层循环找到等于val的元素的下标
  • 第二层循环将等于val的元素后面的全部元素前移一格,覆盖掉等于val的元素,实现删除
# Time complexity: O(N^2)
# Space complexity: O(1)
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        count = len(nums)
        i = 0
        while i < count:
            if nums[i] == val:
                count -= 1
                for j in range(i, count):
                    nums[j] = nums[j+1]
            else:
                i += 1
        return count

快慢指针

  • 将原数组的前k个位置看作是新数组,用慢指针去数新数组的长度
  • 快指针遍历原数组找到属于新数组的元素,并按照顺序插入新数组(慢指针指向的下标是新数组的尾部)
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        # fast to find the elements that we need in the new array
        # slow count the number of elements in the new array (len(new) = len(old) - len(delete))
        slow = 0
        for fast in range(len(nums)):
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
        return slow

相向指针

  • 左指针找需要删除的元素,右指针找下标最大的新数组元素
  • 直到遍历完整个数组
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        left, right = 0, len(nums)
        while left < right:
            if nums[left] == val:
                right -= 1
                nums[left] = nums[right]
            else:
                left += 1
        return left