力扣学习计划——数据结构与算法入门笔记

数据结构

  1. 数组

    • 很简单,也很常用,一般结合其他知识点考察

    题目:217. 存在重复元素

    代码模板:

    class Solution:
        def containsDuplicate(self, nums: List[int]) -> bool:
            # 遍历
            dct = set()
            for n in nums:
                if n not in dct:
                    dct.add(n)
                else:
                    return True
            return False
    

    题目:1. 两数之和

    代码模板:

    class Solution:
        def twoSum(self, nums: List[int], target: int) -> List[int]:
            dct = {
          
          }
            for i, v in enumerate(nums):
                if v in dct:
                    return [dct[v], i]
                else:
                    dct[target-v] = i
    

    力扣第一题,很经典了

    题目:88. 合并两个有序数组

    代码模板:

    class Solution:
        def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
            """
            Do not return anything, modify nums1 in-place instead.
            """
            # 从后往前遍历,双指针
            k = len(nums1)-1
            m -= 1
            n -= 1
            while k > -1:
                if n > -1 and m > -1:
                    if nums1[m] < nums2[n]:
                        nums1[k] = nums2[n]
                        n -= 1
                    else:
                        nums1[k] = nums1[m]
                        m -= 1
                elif n > -1:
                    nums1[k] = nums2[n]
                    n -= 1
                # 如果 n== -1,可以直接return
                else:
                    return
                k -= 1
    

    经典的思路转变,从后往前遍历

    题目:350. 两个数组的交集 II

    代码模板:

    # 法1
    class Solution:
        def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
            # 不借助Counter
            nums1.sort()
            nums2.sort()
            res = []
            n1, n2 = len(nums1), len(nums2)
            i = j = 0
            while i < n1 and j < n2:
                if nums1[i] < nums2[j]:
                    i += 1
                elif nums1[i] > nums2[j]:
                    j += 1
                else:
                    res.append(nums1[i])
                    i += 1
                    j += 1
            return res
    
    # 法2
    class Solution:
        def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
            # 借助Counter,在二者长度差异大的时候效率高
            if len(nums1) > len(nums2):
                return self.intersect(nums2, nums1)
            
            res = []
            ctr = Counter(nums1)
            for n in nums2:
                if n in ctr:
                    ctr[n] -= 1
                    res.append(n)
                    if ctr[n] == 0:
                        ctr.pop(n)
            return res
    

    这道题还是使用哈希表查询效率更好,尤其是nums1和nums2长度差异大的时候

    题目:36. 有效的数独

    代码模板:

    class Solution:
        def isValidSudoku(self, board: List[List[str]]) -> bool:
            row = [[0] * 9 for _ in range(9)]
            col = [[0] * 9 for _ in range(9)]
            mat = [[[0] * 9 for _ in range(3)] for _ in range(3)]
            # mat最先定义的在最后索引
            for i in range(9):
                for j in range(9):
                    c = board[i][j]
                    if c != '.':
                        idx = int(c) - 1
                        row[i][idx] += 1
                        col[idx][j] += 1
                        mat[i//3][j//3][idx] += 1
                        if row[i][idx]>1 or col[idx][j]>1 or mat[i//3][j//3][idx]>1:
                            return False
            return True
    

    如果种类固定,使用数组也可以实现哈希表类似的查重功能

  2. 字符串

    • 在python中,字符串是不可变

    题目:387. 字符串中的第一个唯一字符

    代码模板:

    class Solution:
        def firstUniqChar(self, s: str) -> int:
            # 2次遍历,查询字典中的次数
            ctr = Counter(s)
            for idx, i in enumerate(s):
                if ctr[i] == 1:
                    return idx
            return -1
    

    这道题方法很多,重点理解一下队列的方法,不能局限于一种思路

  3. 链表

    题目:206. 反转链表(经典题)

    代码模板:

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def reverseList(self, head: ListNode) -> ListNode:
            if not head or not head.next:
                return head
            
            newhead = self.reverseList(head.next)
            head.next.next = head
            head.next = None
            return newhead
    

    迭代和递归2种方式,都要熟悉

    题目:83. 删除排序链表中的重复元素

    代码模板:

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def deleteDuplicates(self, head: ListNode) -> ListNode:
            # 递归,原地算法
            if not head or not head.next:
                return head
            if head.val == head.next.val:
                head.next = head.next.next
                self.deleteDuplicates(head)
                return head
            else:
                self.deleteDuplicates(head.next)
                return head
    

    题目:203. 移除链表元素

    代码模板:

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def removeElements(self, head: ListNode, val: int) -> ListNode:
            # 递归
            if head is None:
                return None
            elif head.val == val:
                head.next = self.removeElements(head.next, val)
                return head.next
            else:
                head.next = self.removeElements(head.next, val)
                return head
    
  4. 栈和队列

    • 比较基本,无例题
    • Morris遍历(熟悉Morris序,其中后序需要注意是第二次回到自己的时候,逆序打印左子树右边界)
    • 一题多解,递归和迭代2个版本,dfs和bfs
    • 题目就不放了

算法

  1. 二分

    • 排序的,或者间接排序的
    • 注意边界条件,因为每次循环只触发一个条件,所以 i i i或者 j j j只能移动一步,循环条件是 i ≤ j i\le j ij
    训练计划

    题目:704. 二分查找

    代码模板:

    class Solution:
        def search(self, nums: List[int], target: int) -> int:
            i, j = 0, len(nums)-1
            while i <= j:
                # 取靠左的,即 1 2 3 4 取2,1 2 3 取2
                # 防止计算时溢出
                mid = i + ((j-i) >> 1)
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    i = mid+1
                else:
                    j = mid-1
            return -1
    

    题目:278. 第一个错误的版本

    代码模板:

    # The isBadVersion API is already defined for you.
    # @param version, an integer
    # @return an integer
    # def isBadVersion(version):
    
    class Solution:
        def firstBadVersion(self, n):
            """
            :type n: int
            :rtype: int
            """
            # 特殊之处在于只有2种状态,即当跳出循环时:
            # 如果是i未出错,i += 1,返回i;如果j未出错,一定是上一个版本出错,也返回i
            i, j = 1, n
            while i <= j:
                mid = i + ((j-i) >> 1)
                if isBadVersion(mid):
                    j = mid-1
                else:
                    i = mid+1
            return i
    

    这道题也可以使用 i < j i<j i<j的条件判断了,并且每次只有一个指针移动。

    **为什么会有这样的区别呢?**事实上是因为二分的区间条件不同,判断数字是3种情况,最终范围长度大于0时即是有效范围;但是判断正误只有2种情况,只有范围长度为1时,才是最终的答案,这里需要好好思考,将问题泛化到各种情况下。

    题目:35. 搜索插入位置

    代码模板:

    class Solution:
        def searchInsert(self, nums: List[int], target: int) -> int:
            # 3种情况,对比普通二分将返回-1变为返回合适的索引
            # 考虑找不到的情况,此时i=j,并且有一个变化使while条件不成立,大了j减1,小了i+1,所以返回i
            # 插入位置i表示插入到i索引之前
            i, j = 0, len(nums)-1
            while i <= j:
                mid = i + ((j-i) >> 1)
                if nums[mid] == target:
                    return mid
                elif nums[mid] < target:
                    i = mid+1
                else:
                    j = mid-1
            return i
    
    相似题目

    题目:34. 在排序数组中查找元素的第一个和最后一个位置(重点)

    代码模板:

    class Solution:
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            # i和j就是区间2端点
            # 3种情况
            i, j = 0, len(nums)-1
            while i <= j:
                mid = i + ((j-i) >> 1)
                if nums[mid] < target:
                    i = mid + 1
                elif nums[mid] > target:
                    j = mid - 1
                # mid到j都等于target
                elif nums[i] != target:
                    i += 1
                # i到mid都等于target
                elif nums[j] != target:
                    j -= 1
                else:
                    return [i, j]
            return [-1, -1]
    

    这道题就比较有趣了,我的实现并不是完全的二分查找,但是在一定程度上使用二分查找优化了整体的效率,有时间可以学习一下官方的题解

    题目:374. 猜数字大小

    代码模板:

    # The guess API is already defined for you.
    # @param num, your guess
    # @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
    # def guess(num: int) -> int:
    
    class Solution:
        def guessNumber(self, n: int) -> int:
            i, j = 1, n
            while i <= j:
                mid = i + ((j-i) >> 1)
                if guess(mid) == 0:
                    return mid
                elif guess(mid) == -1:
                    j = mid-1
                else:
                    i = mid+1
            # 返回0表示pick不在范围内
            return 0
    

    题目:658. 找到 K 个最接近的元素(考察coding能力)

    代码模板:

    class Solution:
        def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
            # 二分找到恰好大于x一点点的位置,等价于之前的查找插入位置
            i, j = 0, len(arr)-1
            idx = -100
            while i <= j:
                mid = i + ((j-i) >> 1)
                if arr[mid] == x:
                    idx = mid
                    break
                elif arr[mid] < x:
                    i = mid+1
                else:
                    j = mid-1
            if idx == -100:
                idx = i
            # print(idx)
            # 缩小区间
            if idx == 0:
                return arr[:k]
            elif idx == len(arr):
                return arr[-k:]
            else:
                i, j = max(idx-k, 0), min(idx+k, len(arr)-1)
                while j-i >= k:
                    if arr[j] - x > x - arr[i]:
                        j -= 1
                    elif arr[j] - x < x - arr[i]:
                        i += 1
                    else:
                        j -= 1
                return arr[i:j+1]
    
  2. 双指针

    • 相同数组,不同数组
    • 两边指针,快慢指针

    题目:977. 有序数组的平方

    代码模板:

    class Solution:
        def sortedSquares(self, nums: List[int]) -> List[int]:
            # 双指针,绝对值最大的在2边
            i, j = 0, len(nums)-1
            res = [0] * len(nums)
            k = j
            while k > -1:
                if nums[i]**2 < nums[j]**2:
                    res[k] = nums[j]**2
                    j -= 1
                else:
                    res[k] = nums[i]**2
                    i += 1
                k -= 1
            return res
    

    题目:189. 轮转数组(重点题)

    代码模板:

    class Solution:
        def rotate(self, nums: List[int], k: int) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            # 法2,使用额外空间
            n = len(nums)
            idx = n - k%n
            res = [0] * n
            i = j = 0
            while i < n:
                res[i] = nums[(i+idx) % n]
                i += 1
            while j < n:
                nums[j] = res[j]
                j += 1
    

    题目:283. 移动零(思路很清晰)

    代码模板:

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            # 同向指针,不同条件判断是否右移
            i = j = 0
            while j < len(nums):
                if nums[j] != 0:
                    # 左侧换完之后保证都是非0数
                    nums[i], nums[j] = nums[j], nums[i]
                    i += 1
                j += 1
    

    题目:167. 两数之和 II - 输入有序数组

    代码模板:

    class Solution:
        def twoSum(self, numbers: List[int], target: int) -> List[int]:
            # 首尾双指针,常量空间
            i, j = 0, len(numbers)-1
            while i < j:
                if numbers[i] + numbers[j] == target:
                    return [i+1, j+1]
                elif numbers[i] + numbers[j] > target:
                    j -= 1
                else:
                    i += 1
            return [-1, -1]
    

    题目:557. 反转字符串中的单词 III

    代码模板:

    class Solution:
        def reverseWords(self, s: str) -> str:
            # 将s转变为列表,原地修改
            s = list(s)
            i = j = 0
            while j < len(s):
                while j < len(s) and s[j] != ' ':
                    j += 1
                # 反转s[i:j+1]部分
                k = j
                j -= 1
                while i < j:
                    s[i], s[j] = s[j], s[i]
                    i += 1
                    j -= 1
                j = k+1
                i = k+1
            return ''.join(s)
    

    伪原地算法

    题目:876. 链表的中间结点

    代码模板:

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def middleNode(self, head: ListNode) -> ListNode:
            # 快慢指针
            low, fast = head, head
            while fast and fast.next:
                low = low.next
                fast = fast.next.next
            return low
    

    题目:19. 删除链表的倒数第 N 个结点

    代码模板:

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
            # 伪头,n在要求范围内
            ori = ListNode
            ori.next = head
            slow = fast = head
            while n > 0:
                fast = fast.next
                n -= 1
            slow = ori
            while fast:
                slow = slow.next
                fast = fast.next
            slow.next = slow.next.next
            return ori.next
    

    因为删除当前节点需要其上一个节点,所以添加哑节点

  3. 滑动窗口

    • 感觉和双指针差不多,考虑的范围在窗口内

    题目:3. 无重复字符的最长子串

    代码模板:

    class Solution:
        def lengthOfLongestSubstring(self, s: str) -> int:
            if not s:
                return 0
            dct = set()
            i = j = maxlen = 0
            while j < len(s):
                if s[j] not in dct:
                    dct.add(s[j])
                    j += 1
                else:
                    dct.remove(s[i])
                    i += 1
                maxlen = max(maxlen, j-i)
            return maxlen
    

    代码效率不高,待优化

    题目:567. 字符串的排列(重点题)

    代码模板:

    class Solution:
        def checkInclusion(self, s1: str, s2: str) -> bool:
            # 滑动窗口,对2个ctr的比较可以优化为对差值的比较
            # 这里有正有负,使用ctr不方便,因为字母是有限的,可以使用数组
            if len(s1) > len(s2):
                return False
            diffctr = [0] * 26
            for i in range(len(s2)):
                # 每次都加
                if i < len(s1):
                    diffctr[ord(s2[i])-ord('a')] -= 1
                    diffctr[ord(s1[i])-ord('a')] += 1
                else:
                    if all(k == 0 for k in diffctr):
                        return True
                    diffctr[ord(s2[i])-ord('a')] -= 1
                    diffctr[ord(s2[i-len(s1)])-ord('a')] += 1
            return all(k == 0 for k in diffctr)
    

    提交错了好多次,思绪已经很乱了;这道题可以将比较相同转变为比较差值

  4. dfs和bfs

    • 从简单的开始
    • 可以类比二叉树的遍历方式。bfs使用队列,将一个满足条件的点对应的其他所有满足条件的点加入队列,从左侧弹出最早的节点;dfs使用栈,有时候会借用字典防止重复遍历,当前步不满足的情况就会回溯到上一步

    题目:733. 图像渲染

    代码模板:

    class Solution:
        def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
            # 把所有连通值都更改
            def dfs(r, c):
                if -1 < r < m and -1 < c < n and (r, c) not in dct and image[r][c] == oldcolor:
                    image[r][c] = newColor
                    dct.add((r, c))
                    dfs(r-1, c)
                    dfs(r+1, c)
                    dfs(r, c+1)
                    dfs(r, c-1)
            m, n = len(image), len(image[0])
            oldcolor = image[sr][sc]
            dct = set()
            dfs(sr, sc)
            return image
    

    遇到更改原值的情况,就可以不使用哈希表避免重复

    • 岛屿类问题(重点题型)

    题目:200. 岛屿数量

    代码模板:

    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            def dfs(r, c):
                if -1 < r < m and -1 < c < n and grid[r][c] == '1':
                    grid[r][c] = '2'
                    dfs(r-1, c)
                    dfs(r+1, c)
                    dfs(r, c-1)
                    dfs(r, c+1)
            
            m, n = len(grid), len(grid[0])
            cnt = 0
            for r in range(m):
                for c in range(n):
                    if grid[r][c] == '1':
                        dfs(r, c)
                        cnt += 1
            return cnt
    

    题目:695. 岛屿的最大面积

    代码模板:

    class Solution:
        def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
            def dfs(r, c):
                nonlocal cnt
                if -1 < r < m and -1 < c < n and grid[r][c] == 1:
                    grid[r][c] = 2
                    cnt += 1
                    dfs(r-1, c)
                    dfs(r+1, c)
                    dfs(r, c-1)
                    dfs(r, c+1)
            
            m, n = len(grid), len(grid[0])
            maxcnt = 0
            for r in range(m):
                for c in range(n):
                    cnt = 0
                    dfs(r, c)
                    maxcnt = max(maxcnt, cnt)
            return maxcnt
    

    题目:463. 岛屿的周长(简单题,但是做的很乱)

    代码模板:

    class Solution:
        def islandPerimeter(self, grid: List[List[int]]) -> int:
            def dfs(r, c):
                nonlocal cnt
                if r < 0 or r >= m or c < 0 or c >= n or grid[r][c] == 0:
                    cnt += 1
                elif grid[r][c] == 1:
                    grid[r][c] = 2
                    for x, y in ((r-1, c), (r+1, c), (r, c+1), (r, c-1)):
                        dfs(x, y)
            
            m, n = len(grid), len(grid[0])
            cnt = 0
            for r in range(m):
                for c in range(n):
                    if grid[r][c] == 1:
                        dfs(r, c)
            return cnt
    

    题目:617. 合并二叉树(重要题)

    代码模板:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
            if not root1:
                return root2
            if not root2:
                return root1
            node = TreeNode(root1.val + root2.val)
            node.left = self.mergeTrees(root1.left, root2.left)
            node.right = self.mergeTrees(root1.right, root2.right)
            return node
    

    虽然是简单题,但是对递归还是梳理不够,可以通过这道题反思以下《剑指offer》的题:

    1. 剑指 Offer 27. 二叉树的镜像
    2. 剑指 Offer 28. 对称的二叉树
    • 以下是一些必须使用bfs的例子

    题目:542. 01 矩阵(重要题)

    代码模板:

    class Solution:
        def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
            m, n = len(matrix), len(matrix[0])
            res = [[0] * n for _ in range(m)]
            zreopos = [(i, j) for i in range(m) for j in range(n) if matrix[i][j] == 0]
            q = deque(zreopos)
            dct = set(zreopos)
            # bfs
            while q:
                r, c = q.popleft()
                for x, y in [(r-1, c), (r+1, c), (r, c-1), (r, c+1)]:
                    if -1 < x < m and -1 < y < n and (x, y) not in dct:
                        res[x][y] = res[r][c] + 1
                        dct.add((x, y))
                        q.append((x, y))
            return res
    

    题目:994. 腐烂的橘子

    代码模板:

    class Solution:
        def orangesRotting(self, grid: List[List[int]]) -> int:
            # 和01矩阵差不多,从所有腐烂的橘子开始一起扩散
            # 最后一个被扩散到需要的时间也就是其到最近腐烂句子的距离
            m, n = len(grid), len(grid[0])
            dist = [[0] * n for _ in range(m)]
            bad = [(i, j) for i in range(m) for j in range(n) if grid[i][j] == 2]
            good = [(i, j) for i in range(m) for j in range(n) if grid[i][j] == 1]
            
            # 没有新鲜的
            if not good:
                return 0
            # 如果全部新鲜
            if not bad:
                return -1
            
            q = deque(bad)
            dct = set(bad)
            while q:
                r, c = q.popleft()
                for x, y in [(r-1, c), (r+1, c), (r, c-1), (r, c+1)]:
                    # 这里增加条件,必须有橘子才能腐烂
                    if -1 < x < m and -1 < y < n and (x, y) not in dct and grid[x][y] == 1:
                        dist[x][y] = dist[r][c] + 1
                        grid[x][y] = 2
                        dct.add((x, y))
                        q.append((x, y))
            
            # 还有新鲜的
            good = [(i, j) for i in range(m) for j in range(n) if grid[i][j] == 1]
            if good:
                return -1
            
            return max(max(r) for r in dist)
    

    这2道题很有代表性,都是多源广度优先搜索的题目,和普通的bfs稍有差别

    要利用左侧pop的元素信息,来更新这一层新加入节点的信息

  5. 递归和回溯

    • 基于链表、树等数据结构而递归/回溯
    • 基于其他(难点,尤其是分辨排列和组合的区别联系)

    题目:77. 组合(重点)

    代码模板:

    class Solution:
        def combine(self, n: int, k: int) -> List[List[int]]:
            # 排列使用交换法,组合使用dp法
            # 1-n中取m,分解为2-n中取m-1(要1)和2-n中取m(不要1)
            def huisu(i, size):
                path.append(i)
                if size == k:
                    res.append(list(path))
                # 无后效性
                for j in range(i+1, n+1):
                    huisu(j, size+1)
                path.pop()
            
            path = []
            res = []
            for j in range(1, n-k+2):
                huisu(j, 1)
            return res
    

    题目:46. 全排列(重点)

    代码模板:

    class Solution:
        def permute(self, nums: List[int]) -> List[List[int]]:
            # 全排列,交换法;或者是用字典避免重复
            def huisu(k):
                if k == len(nums):
                    res.append(path[:])
                    return
                for j in range(len(nums)):
                    # 下一次遇到1的时候不添加1去dfs,否则添加进字典,与路径
                    if nums[j] in dct:
                        continue
                    dct.add(nums[j])
                    path.append(nums[j])
                    huisu(k+1)
                    path.pop()
                    dct.remove(nums[j])
            
            dct = set()
            res = []
            path = []
            huisu(0) # 当前长度为0
            return res
    
            # 也可以不加入参数k,直接判断path长度是否为nums长度也行
    

    题目:784. 字母大小写全排列

    代码模板:

    class Solution:
        def letterCasePermutation(self, s: str) -> List[str]:
            # 相当于dfs时有2种情况(大写、小写)
            def huisu(k):
                if k == len(s):
                    res.append(''.join(path))
                    return
                # 是数字也要回溯,这个的意义是什么
                if s[k].isdigit():
                    path.append(s[k])
                    huisu(k+1)
                    path.pop()
                else:
                    if s[k].islower():
                        path.append(s[k])
                        huisu(k+1)
                        path.pop()
                        path.append(s[k].upper())
                        huisu(k+1)
                        path.pop()
                    else:
                        path.append(s[k])
                        huisu(k+1)
                        path.pop()
                        path.append(s[k].lower())
                        huisu(k+1)
                        path.pop()
            
            path = []
            res = []
            huisu(0)
            return res
    
  6. 动态规划

    • 二维dp
    • 范围上考虑
    • 存在枚举

    题目:375. 猜数字大小 II

    代码模板:

    class Solution:
        def getMoneyAmount(self, n: int) -> int:
            # dp下标范围0~n,为避免歧义,多加一个没有意义的0列
            dp = [[0] * (n+1) for _ in range(n+1)]
            
            # 初始化,i,j相邻时,取小的
            for i in range(2, n+1):
                dp[i-1][i] = i-1
            
            # 过程
            for i in range(n-2, 0, -1):
                for j in range(i+2, n+1):
                    mini = i+1 + max(dp[i][i], dp[i+2][j])
                    for k in range(i+1, j):
                        mini = min(mini, k+max(dp[i][k-1], dp[k+1][j]))
                    dp[i][j] = mini
            # print(dp)
            return dp[1][n]
    
    • 一维dp
    • 无后效性

    题目:53. 最大子数组和(重点题)

    代码模板:

    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            # 子数组和小于0,重新开始
            # dp[i]表示以i结尾的连续子数组最大和
            dp = [0] * len(nums)
            summ = 0
            for i in range(len(nums)):
                summ += nums[i]
                dp[i] = summ
                if summ < 0:
                    summ = 0
            return max(dp)
    

    官方的答案更有动态规划的思想,即求: d p [ i ] = m a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) dp[i]=max(dp[i−1]+nums[i], nums[i]) dp[i]=max(dp[i1]+nums[i],nums[i])

    • 伪二维dp
    • 主要以买股票为主,即另一维的几个状态都会用到,并且是状态数是常量

    题目:121. 买卖股票的最佳时机

    代码模板:

    class Solution:
        def maxProfit(self, prices: List[int]) -> int:
            # 使用动态规划解题
            # 伪二维数组,其中一个维度是“持有股票”或者“不持有股票”,分别是0和1
            dp = [[0] * 2 for _ in range(len(prices))]
            dp[0][0], dp[0][1] = prices[0], 0
            for i in range(1, len(prices)):
                # 当天如果持有,则选择最低的时候买入
                dp[i][0] = min(dp[i-1][0], prices[i])
                # 当天如果不持有,则选择最高的时候卖出
                dp[i][1] = max(dp[i-1][1], prices[i]-dp[i-1][0])
            return dp[len(prices)-1][1]
    

    题目:122. 买卖股票的最佳时机 II

    代码模板:

    class Solution:
        def maxProfit(self, prices: List[int]) -> int:
            # 使用动态规划解题
            # 伪二维数组,其中一个维度是“持有股票”或者“不持有股票”,分别是0和1
            dp = [[0] * 2 for _ in range(len(prices))]
            dp[0][0], dp[0][1] = -prices[0], 0
            for i in range(1, len(prices)):
                # 持有的状态,如果之前持有,则为之前的状态;之前不持有,可以持有,选最大
                dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i])
                # 不持有的状态,可以将之前持有的卖出去,或者继续不持有
                dp[i][1] = max(dp[i-1][0]+prices[i], dp[i-1][1])
            return dp[len(prices)-1][1]
    

    这2道题的不同需要好好思考一下

    还有这2者的进阶版,既不是只交易一次,也不是可重复交易,而是交易2次

  7. 位运算

    • 这里就是要多记一些
    • n & ( n − 1 ) n \& (n-1) n&(n1)衍生,得到的结果是将最低位的1变为0
      1. 结果为0,说明之前只有一个1,即为2的幂
      2. 记录可以循环几次,则原本的二进制有几个1
    • n & 1 n\&1 n&1可以判断奇偶
    • 判断是不是交替1,可以用 n ⊕ ( n > > 1 ) n \oplus (n >> 1) n(n>>1)
    • 异或的性质
      1. 0 ⊕ a = a 0 \oplus a = a 0a=a
      2. a ⊕ a = 0 a \oplus a = 0 aa=0
      3. 交换律

猜你喜欢

转载自blog.csdn.net/qq_45510888/article/details/123895136
今日推荐