刷题Day(1) | 代码随想录

168 阅读2分钟

本系列文章是我刷代码随想录过程中的笔记。代码地址:leetcode

今天是我刷“代码随想录”的第一天

今日内容

  • 了解数组的基本理论
  • 二分查找
  • 移除元素

数组的基本理论

数组是存放在连续内存空间上的相同类型数据的集合,因而在获取数组中的元素时可以通过下标索引的方式获取到对应的数据。下面是一个数组的示例。

算法通关数组.png

值得注意的是。

  1. 数组的下标从零开始
  2. 数组的内存空间是连续的

因此,对于数组中的元素来说,其增删改查的操作的时间复杂度分别为o(n)o(n)o(1)o(1)。原则上来说,数组中的元素是不能删除的。但是,我们可以通过覆盖的方式变相的使该位置上的数据消失,也就实现了“删除”。

二分查找

二分查找通常用于在有序数组中查找指定元素,同时数组中不能有重复元素。在求解二分查找的问题中,最难的问题是确定边界条件。下面以leetcode 704. 二分查找为例。

leetcode 704. 二分查找

下面是求解二分查找问题的步骤

  1. 确定区间,即是数据序号的取值范围,一般是左闭右闭左闭右开
  2. 循环结束的条件。在左闭右闭的条件下,左值等于右值是有意义的,此时通常表示取到中间值。
  3. 确定左右边界变化的条件和变化的形式。

下面是求解的代码

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        # 确定求解区间——左闭右闭
        l, r = 0, len(nums) - 1
        # 当l==r时是有意义的,因此取<=
        while l <= r:
            # 求取中间值,等同于(l + r) // 2
            mid = (r - l) // 2 + l
            # 如果在数组中找到目标值,直接返回
            if nums[mid] == target:
                return mid
            # 如果小于目标值,说明目标在右区间,因此需要将区间更改为[mid + 1, l]
            elif nums[mid] < target:
                l = mid + 1
             # 如果大于目标值,说明目标在左区间,因此需要将区间更改为[r, mid + 1] 
            else:
                r = mid - 1
        # 找不到,返回-1
        return -1
        
    def search_2(self, nums: List[int], target: int) -> int:
        # 确定求解区间——左闭右开
        l, r = 0, len(nums)
        # 这里l == r 没有意义
        while l < r:
            mid = (r - l) // 2 + l
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                l = mid + 1
            else:
                r = mid
        return -1

leetcode 35.搜索插入位置

这道题是上面那道题的简化版,只需考虑小于和其他两种情况。下面是求解代码。

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

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

这道题需要分别计算左位置和右位置,因此需要使用两次二分查找,并且更改判断条件。下面是求解代码

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def binarySearchLeft(nums: List[int], target: int) -> int:
            l = -1
            r = len(nums)
            while l + 1 != r:
                mid = l + (r - l) // 2
                if nums[mid] >= target:
                    r = mid
                else:
                    l = mid
            return r

        def binarySearchRight(nums: List[int], target: int) -> int:
            l = -1
            r = len(nums)
            while l + 1 != r:
                mid = l + (r - l) // 2
                if nums[mid] <= target:
                    l = mid
                else:
                    r = mid 
            return l

        leftIdx = binarySearchLeft(nums, target)
        rightIdx = binarySearchRight(nums, target)

        if leftIdx <= rightIdx < len(nums) and nums[leftIdx] == target and nums[rightIdx] == target:
            return [leftIdx, rightIdx]
        return [-1, -1]

移除元素

暴力法

求解的时候使用两遍循环对目标值进行覆盖即可。(p.s.python总是超时)

双指针

定义快慢指针,当快指针的值是目标值的时候,直接跳过。不是的时候,将快指针的值赋给慢指针。如下图所示。

27.移除元素-双指针法.gif

下面是求解代码

class Solution: 
    def removeElement(self, nums: List[int], val: int) -> int: 
        # 双指针 
        f,s = 0, 0 
        num = len(nums)
        while f < num: 
            if nums[f] != val: 
                nums[s] = nums[f] 
                s += 1 
            f += 1 
        return s

感想

今天的题都是基础题,只要掌握了其中的思想,就可以很容易的求解。