HOT 100(一)

1. 两数之和

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # # 排序 + 双指针
        # n = len(nums)
        # nums_new = [(nums[i], i) for i in range(n)]
        # nums_sort = sorted(nums_new, key=lambda x: x[0])
        # # print(nums_new)
        # i, j = 0, len(nums)-1
        # while i < j:
        #     s = nums_sort[i][0] + nums_sort[j][0]
        #     if s == target:
        #         return [nums_sort[i][1], nums_sort[j][1]]
        #     elif s < target:
        #         i += 1
        #     else:
        #         j -= 1
        # return []

        # hash 表
        hashtable = dict()
        for i, num in enumerate(nums):
            if target - num in hashtable:
                return [hashtable[target - num], i]
            hashtable[nums[i]] = i
        return []

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

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 滑动窗口
        # 一次遍历字符串,判断 s[i] 是否在滑动窗口内,是的话滑动窗口向右缩窄,直到 s[i]不在窗口内
        # 更新窗口长度和最长子串长度
        n = len(s)
        left = 0
        str_set = set()  # 窗口内的字符集合
        cursize = 0
        maxsize = 0
        for i in range(n):
            cursize += 1
            # 缩小窗口,直到窗口内不重复
            while s[i] in str_set:
                str_set.remove(s[left])
                cursize -= 1
                left += 1
            str_set.add(s[i])
            # 更新 maxsize
            maxsize = max(maxsize, cursize)
        return maxsize

4. 寻找两个正序数组的中位数

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        # # 合并两个排序数组,时间复杂度 O(m+n)
        # ans = []
        # m, n = len(nums1), len(nums2)
        # i, j = 0, 0
        # while i < m or j < n:
        #     if i == m:
        #         ans.append(nums2[j])
        #         j += 1
        #     elif j == n:
        #         ans.append(nums1[i])
        #         i += 1
        #     elif nums1[i] <= nums2[j]:
        #         ans.append(nums1[i])
        #         i += 1
        #     else:
        #         ans.append(nums2[j])
        #         j += 1
        # if (m + n) % 2 == 0:
        #     return (ans[(m+n) // 2 - 1] + ans[(m+n) // 2]) / 2
        # else:
        #     return ans[(m+n) // 2]

        # 查找第 k 个小的元素
        """
            - 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
            - 这里的 "/" 表示整除
            - nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
            - nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
            - 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
            - 这样 pivot 本身最大也只能是第 k-1 小的元素
            - 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
            - 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
            - 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
            """
        def getKElement(k):
            index1, index2 = 0, 0
            # m, n = len(nums1), len(nums2)
            while True:
                # 截止条件
                if index1 == m:
                    return nums2[index2 + k - 1]
                if index2 == n:
                    return nums1[index1 + k - 1]
                if k == 1:
                    return min(nums1[index1], nums2[index2])
                # 正常判断
                newIndex1 = min(index1 + k // 2 - 1, m - 1)
                newIndex2 = min(index2 + k // 2 - 1, n - 1)
                pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
                if pivot1 <= pivot2:
                    # 更新 index1 和 k,先更新 k
                    k -= newIndex1 - index1 + 1
                    index1 = newIndex1 + 1
                else:
                    # 更新 index2 和 k,先更新 k
                    k -= newIndex2 - index2 + 1
                    index2 = newIndex2 + 1

        m, n = len(nums1), len(nums2)
        if (m+n) % 2 == 1:
            return getKElement((m+n)//2+1)
        else:
            return (getKElement((m+n)//2) + getKElement((m+n)//2+1)) / 2
            

10. 正则表达式匹配

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        s_len = len(s)
        p_len = len(p)

        # dp[i][j] 表示 s[:i] 与 p[:j] 是否匹配,各自前 i、j 个是否匹配
        dp = [[False] * (p_len + 1) for _ in range(s_len + 1)]
        dp[0][0] = True

        # s 为空串,更新第一行
        for j in range(1, p_len + 1):
            # 若 p 的第 j 个字符 p[j - 1] 是 '*'
            # 说明第 j - 1、j 个可有可无
            # 那么如果前 j - 2 个已经匹配上,前 j 个也可以匹配上
            if p[j - 1] == '*':
                dp[0][j] = dp[0][j - 2]

        for i in range(1, s_len + 1):
            for j in range(1, p_len + 1):
                # 当前字符可以匹配,转移到dp[i-1][j-1]
                if p[j - 1] in {s[i - 1], '.'}:
                    dp[i][j] = dp[i - 1][j - 1]
                # 当前字符是*,需要和 p 前一个字符一起判断
                elif p[j - 1] == '*':
                    # 如果 p 前一个字符可以匹配 s 最后一个字符,可以选择该[*组合]不匹配 或者 去掉 s 最后一个字符继续匹配
                    if p[j - 2] in {s[i - 1], '.'}:
                        dp[i][j] = dp[i][j - 2] or dp[i - 1][j]
                    # 如果 p 前一个字符不能匹配 s 最后一个字符,只能选择该 [*组合] 不匹配
                    else:
                        dp[i][j] = dp[i][j - 2]
        return dp[s_len][p_len]

11. 盛最多水的容器

class Solution:
    def maxArea(self, height: List[int]) -> int:
        # 双指针,每次移动短板,更新最大值
        # 移动短板,新的面积可能增大;移动长板,面积一定减小
        i, j = 0, len(height)-1
        res = 0
        while i < j:
            res = max(res, (j-i)*min(height[i], height[j]))
            if height[i] <= height[j]:
                i += 1
            else:
                j -= 1
        return res

20. 有效的括号

class Solution:
    def isValid(self, s: str) -> bool:
        # 栈,左括号入栈,遇到右括号出栈,判断是否匹配
        stack = []
        for i in range(len(s)):
            if s[i] in {'(', '[', '{'}:
                stack.append(s[i])
            else:
                if len(stack) == 0:
                    return False
                ch = stack.pop()
                if s[i] == ')' and ch == '(':
                    continue
                if s[i] == ']' and ch == '[':
                    continue
                if s[i] == '}' and ch == '{':
                    continue
                return False
        if len(stack) == 0:
            return True
        return False

31. 下一个排列

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # 两遍搜索,第一遍从后向前查找【小数】(第一个非升序的数),第二遍从后向前查找【大数】(大于小数的第一个数)
        # 交换【小数】和【大数】
        # 【大数】后的序列升序排列(双指针)
        n = len(nums)
        i = n-2
        # 找【小数】
        while i >= 0 and nums[i] >= nums[i+1]:
            i -= 1
        # 找【大数】(如果原序列为逆序排列,没有小数,直接返回倒序序列)
        if i >= 0:
            j = n-1
            while j > i and nums[j] <= nums[i]:
                j -= 1
            # print(i, j)
            # 交换
            nums[i], nums[j] = nums[j], nums[i]
            # print(nums)
        # 逆序 大数之后的序列
        i, j = i+1, n-1
        while i < j:
            nums[i], nums[j] = nums[j], nums[i]
            i += 1
            j -= 1
        return
        

 48. 旋转图像

力扣

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        # 找规律
        n = len(matrix)
        for i in range(n // 2):
            for j in range((n + 1) // 2):
                matrix[i][j], matrix[n - j - 1][i], matrix[n - i - 1][n - j - 1], matrix[j][n - i - 1] \
                    = matrix[n - j - 1][i], matrix[n - i - 1][n - j - 1], matrix[j][n - i - 1], matrix[i][j]

        # 水平翻转 + 对角线翻转
        n = len(matrix)
        # 水平翻转
        for i in range(n // 2):
            for j in range(n):
                matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j]
        # 主对角线翻转
        for i in range(n):
            for j in range(i):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

72. 编辑距离

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # 动态规划
        # dp[i][j]表示 word1[:i+1]转换到word2[:j+1]所需要的最少操作数
        # 如果 word1[i] = word2[j], dp[i][j] = dp[i-1][j-1];
        # 否则,dp[i][j] = min(dp[i-1][j-1](替换), dp[i][j-1](插入), dp[i-1][j](删除)) + 1
        # 第一行:表示 word1 为空,插入操作
        # 第一列:表示 word2 为空,删除操作
        m = len(word1)
        n = len(word2)
        dp = [[0]* (n+1) for _ in range(m+1)]
        # 单独处理第一行和第一列
        for j in range(n+1):
            dp[0][j] = j
        for i in range(m+1):
            dp[i][0] = i
        for i in range(1, m+1):
            for j in range(1, n+1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1
        return dp[m][n]

76. 最小覆盖子串

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        '''
        如果hs哈希表中包含ht哈希表中的所有字符,并且对应的个数都不小于ht哈希表中各个字符的个数,那么说明当前的窗口是可行的,可行中的长度最短的滑动窗口就是答案。
        '''
        if len(s) < len(t):
            return ""
        hs, ht = defaultdict(int), defaultdict(int)#初始化新加入key的value为0
        # 初始化 ht
        for char in t:
            ht[char] += 1
        res = "" 
        left, right = 0, 0 # 滑动窗口
        cnt = 0 # 当前窗口中满足ht的字符个数
        while right < len(s):
            hs[s[right]] += 1
            # 更新 cnt,之后用来判断是否当前窗口满足条件(包含 t 中所有字符)
            if hs[s[right]] <= ht[s[right]]:
                cnt += 1
            # 判断是否可以缩窄窗口
            while left <= right and hs[s[left]] > ht[s[left]]:
                hs[s[left]] -= 1
                left += 1
            # cnt 满足条件,更新 res
            if cnt == len(t):
                if not res or right-left+1 < len(res): #res为空或者遇到了更短的长度
                    res = s[left:right+1]
            right += 1
        return res

84. 柱状图中最大的矩形

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # # 暴力解法
        # size = len(heights)
        # res = 0

        # for i in range(size):
        #     left = i
        #     cur_height = heights[i]
        #     while left > 0 and heights[left - 1] >= cur_height:
        #         left -= 1

        #     right = i
        #     while right < size - 1 and heights[right + 1] >= cur_height:
        #         right += 1

        #     max_width = right - left + 1
        #     res = max(res, max_width * cur_height)
        # return res

        # 单调栈
        stack = []
        heights = [0] + heights + [0]
        res = 0
        for i in range(len(heights)):
            #print(stack)
            # 当遇到一个小的,说明找到了右边界
            # 由于是单调递增栈,左边界就是栈顶元素
            while stack and heights[i] < heights[stack[-1]]:
                tmp = stack.pop()
                res = max(res, (i - stack[-1] - 1) * heights[tmp])
            stack.append(i)
        return res

85. 最大矩形

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        # 暴力求解,时间复杂度 O(m^2*n)
        # 二维数组 dp[i][j],存储第 i 行第 j 个数,向左连续的 1 的个数
        # 遍历 dp,找到以matrix[i][j为右下角端点的最大矩形面积(向上找最小宽度 * 高度)
        m = len(matrix)
        n = len(matrix[0])
        dp = [[0]*n for _ in range(m)]
        # 构建 dp 数组
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '0':
                    dp[i][j] = 0
                else:
                    dp[i][j] = (dp[i][j-1] + 1) if j > 0 else 1
        # print(dp)

        # # 遍历右下角端点,计算最大面积
        # max_area = 0
        # for i in range(m):
        #     for j in range(n):
        #         if dp[i][j] == 0:
        #             continue
        #         min_width = dp[i][j]
        #         # 从当前行,向上寻找最小的宽度,同时计算高度,计算最大面积
        #         for k in range(i, -1, -1):
        #             min_width = min(min_width, dp[k][j])
        #             max_area = max(max_area, min_width * (i-k+1))
        # return max_area

        # 单调栈,和 84 题一样
        # 把 dp 的每一列,作为高度数组传入,可计算出每一列的最大面积,最后取 max
        def largestRectangleArea(heights: List[int]) -> int:
            # 单调栈
            stack = []
            heights = [0] + heights + [0]
            res = 0
            for i in range(len(heights)):
                #print(stack)
                # 当遇到一个小的,说明找到了右边界
                # 由于是单调递增栈,左边界就是栈顶元素
                while stack and heights[i] < heights[stack[-1]]:
                    tmp = stack.pop()
                    res = max(res, (i - stack[-1] - 1) * heights[tmp])
                stack.append(i)
            return res
        
        max_area = 0
        for j in range(n):
            heights = [x[j] for x in dp]
            max_area = max(max_area, largestRectangleArea(heights))
        return max_area

105. 从前序与中序遍历序列构造二叉树

# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        # # 递归,增加索引 map 减小 index 的复杂度
        # # 边界条件
        # if len(preorder) == 0:
        #     return None
        # if len(preorder) == 1:
        #     return TreeNode(preorder[0])
        # # 构造根节点
        # val = preorder[0]
        # index = inorder.index(val)
        # # 计算左右子树的大小,便于计算左右子树的preorder 和 inorder
        # left_size, right_size = index, len(inorder)-1-index
        # root = TreeNode(val)
        # # 分治,递归构造左右子树
        # root.left = self.buildTree(preorder[1:1+left_size], inorder[:index])
        # root.right = self.buildTree(preorder[1+left_size:], inorder[index+1:])
        # return root

        # 迭代
        # 用一个栈保存已经遍历过的节点
        # 遍历前序遍历的数组,一直作为当前根节点的左子树,直到当前节点和中序遍历的数组的节点相等
        # 然后正序遍历中序遍历的数组,倒着遍历已经遍历过的根节点(用栈的 pop 实现)
        # 找到最后一次相等的位置,把它作为该节点的右子树。
        if not preorder:
            return None

        root = TreeNode(preorder[0])
        stack = [root]  # 保存已遍历的节点
        inorderIndex = 0
        # 正序遍历【前序遍历】数组
        for i in range(1, len(preorder)):
            preorderVal = preorder[i]
            node = stack[-1]
            # 和中序遍历节点不相等,就一直作为左子树
            if node.val != inorder[inorderIndex]:
                node.left = TreeNode(preorderVal)
                stack.append(node.left)
            else:
                # 和中序遍历节点相等,倒着遍历已经遍历过的根节点
                while stack and stack[-1].val == inorder[inorderIndex]:
                    node = stack.pop()
                    inorderIndex += 1
                # 找到最后一次相等的位置,把它作为该节点的右子树
                node.right = TreeNode(preorderVal)
                stack.append(node.right)

        return root

128. 最长连续序列

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        # res = 0
        # nums_set = set(nums)  # 去重
        # for num in nums:
        #     length = 0
        #     # 不是最左端点,跳过
        #     if num-1 in nums_set:
        #         continue
        #     # 是最左端点,连续查找数字是否存在,更新长度
        #     cur_num = num
        #     while cur_num in nums_set:
        #         length += 1
        #         cur_num += 1
        #     # 更新最大长度
        #     res = max(res, length)
        # return res

        # 动态规划
        hash_dict = dict()
        
        max_length = 0
        for num in nums:
            if num not in hash_dict:
                left = hash_dict.get(num - 1, 0)
                right = hash_dict.get(num + 1, 0)
                
                cur_length = 1 + left + right
                if cur_length > max_length:
                    max_length = cur_length
                
                hash_dict[num] = cur_length
                hash_dict[num - left] = cur_length
                hash_dict[num + right] = cur_length
                
        return max_length

136. 只出现一次的数字

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        # 异或运算
        res = 0
        for num in nums:
            res ^= num
        return res

139. 单词拆分

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        # 回溯
        if len(s) == 0:
            return False
        
        def match(s1, s2):
            for i in range(len(s1)):
                if s1[i] != s2[i]:
                    return False
            return True

        import functools
        @functools.lru_cache(None)
        def dfs(s):
            if len(s) == 0:
                return True
            for word in wordDict:
                if len(s) < len(word):
                    continue
                if not match(word, s[:len(word)]):
                    continue
                # 前几个字符匹配,递归判断剩余字符串
                if dfs(s[len(word):]):
                    return True
            return False
        
        return dfs(s)

        # # 动态规划
        # n = len(s)
        # dp = [False] * (n+1)
        # dp[0] = True
        # for i in range(n+1):
        #     for j in range(i):
        #         if dp[j] and s[j:i] in wordDict:
        #             dp[i] = True
        #             break
        # return dp[n]

        # # 使用记忆化函数,保存出现过的 backtrack(s)backtrack(s),避免重复计算。
        # import functools
        # @functools.lru_cache(None)
        # def back_track(s):
        #     if(not s):
        #         return True
        #     for i in range(1,len(s)+1):
        #         if(s[:i] in wordDict):
        #             if back_track(s[i:]):
        #                 return True
        #     return False
        # return back_track(s)

198. 打家劫舍

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 动态规划
        # # 二维
        # # dp[i][0]表示截止到第 i 间,不偷,获得的最高金额
        # # dp[i][1]表示截止到第 i 间,偷,获得的最高金额
        # # 状态转移:dp[i][0] = max(dp[i-1][0], dp[i-1][1]); dp[i][1] = dp[i-1][0] + nums[i]
        # n = len(nums)
        # dp = [[0] * 2 for _ in range(n)]
        # dp[0][0] = 0
        # dp[0][1] = nums[0]
        # for i in range(1, n):
        #     dp[i][0] = max(dp[i-1][0], dp[i-1][1])
        #     dp[i][1] = dp[i-1][0] + nums[i]
        # print(dp)
        # return max(dp[n-1][0], dp[n-1][1])

        # # 一维
        # # dp[i] 表示截止到第 i 间所获取的最大金额
        # # 状态转移:dp[i] = max(dp[i-1], dp[i-2] + nums[i])
        # n = len(nums)
        # dp = [0 for _ in range(n)]
        # dp[0] = nums[0]
        # for i in range(1, n):
        #     dp[i] = max(dp[i-1], (dp[i-2] if i >= 2 else 0) + nums[i])
        # return dp[n-1]

        # 一维 空间优化
        # dp[i] 表示截止到第 i 间所获取的最大金额
        # 状态转移:dp[i] = max(dp[i-1], dp[i-2] + nums[i])
        n = len(nums)
        if n == 1:
            return nums[0]
        dp_2 = nums[0]
        dp_1 = max(nums[0], nums[1])
        dp = dp_1
        for i in range(2, n):
            dp = max(dp_1, dp_2 + nums[i])
            dp_2 = dp_1
            dp_1 = dp
        return dp

        

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值