【LeetCode热题100】

LeetCode热题100

  • 22. 括号生成()
  • 76. 最小覆盖子串()
  • 15. 三数之和()
  • 105. 从前序与中序遍历序列构造二叉树()
  • 56. 合并区间()
  • 200. 岛屿数量
  • 105. 岛屿的最大面积
  • 463. 岛屿的周长()
  • 827. 最大人工岛()
  • 239. 滑动窗口最大值
  • 17. 电话号码的字母组合
  • 75. 颜色分类
  • 79. 单词搜索
  • 32. 最长有效括号()
  • 33. 搜索旋转排序数组()
  • 39. 组合总和
  • 48. 旋转图像()
  • 64. 最小路径和
  • 70. 爬楼梯
  • 72. 编辑距离( 原问题很简单,注意变体,添加了转换成本)
  • 78. 子集
  • 49. 字母异位词分组
  • 98. 验证二叉搜索树()
  • 114. 二叉树展开为链表
  • 121. 买卖股票的最佳时机
  • 128. 最长连续序列()
  • 136. 只出现一次的数字
  • 139. 单词拆分
  • 141. 环形链表
  • 142. 环形链表 II
  • 152. 乘积最大子数组()
  • 160. 相交链表
  • 169. 多数元素
  • 198. 打家劫舍()
  • 221. 最大正方形(注意最大矩形)
  • 剑指 Offer II 040. 矩阵中最大的矩形()
  • 234. 回文链表()
  • 238. 除自身以外数组的乘积()
  • 236. 二叉树的最近公共祖先()
  • 240. 搜索二维矩阵 II
  • 279. 完全平方数()
  • 283. 移动零
  • 287. 寻找重复数
  • 300. 最长递增子序列()
  • 309. 最佳买卖股票时机含冷冻期()
  • 322. 零钱兑换()
  • 347. 前 K 个高频元素
  • 617. 合并二叉树()
  • 461. 汉明距离
  • 448. 找到所有数组中消失的数字
  • 560. 和为 K 的子数组()
  • 739. 每日温度()
  • 309. 最佳买卖股票时机含冷冻期()
  • 647. 回文子串()
  • 438. 找到字符串中所有字母异位词
  • 406. 根据身高重建队列()
  • 338. 比特位计数()
  • 543. 二叉树的直径()
  • 337. 打家劫舍 III()
  • 84. 柱状图中最大的矩形()
  • 538. 把二叉搜索树转换为累加树
  • 208. 实现 Trie (前缀树)
  • 394. 字符串解码()
  • 416. 分割等和子集()
  • 494. 目标和()
  • 148. 排序链表(归并排序)
  • 96. 不同的二叉搜索树()
  • 450. 删除二叉搜索树中的节点()
  • 581. 最短无序连续子数组( 挺巧妙的)

22. 括号生成()

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        res = []

        def backtracking(S, left, right):
            if len(S) == 2*n:
                res.append(''.join(S))
                return
            
            if left < n:
                S.append('(')
                backtracking(S, left+1, right)
                S.pop()
            
            # 这里是右括号的数目要小于左括号的数目
            if right < left:
                S.append(')')
                backtracking(S, left, right+1)
                S.pop()
        
        backtracking([], 0, 0)
        return res

76. 最小覆盖子串()

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        m, n = len(s), len(t)
        if m < n: return ''
        
        dic_t = collections.Counter(t)

        def is_in(dic, dic_t):
            for k, v in dic_t.items():
                if dic[k] <= 0: return False
                if dic[k] < dic_t[k]: return False
            return True

        dic = collections.defaultdict(int)
        l, r = 0, 0
        min_length = float('inf')
        res = ''

        while r < m:
            while r < m and not is_in(dic, dic_t):
                dic[s[r]] += 1
                r += 1
            
            while l < r and is_in(dic, dic_t):
                if r-l < min_length:
                    min_length = r - l
                    res = s[l:r]
                dic[s[l]] -=1 
                l += 1
        
        return res

15. 三数之和()

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 使用双指针的解法,可以使得复杂度降低到O(n^2)
        res, k = [], 0
        nums.sort()

        for k in range(len(nums) - 2):
            if nums[k] > 0: break
            if k > 0 and nums[k] == nums[k-1]: continue
            l, r = k+1, len(nums) - 1            
            while l < r:
                s = nums[k] + nums[l] + nums[r]
                if s < 0:
                    # 太小了,右移左指针
                    l += 1
                    while l < r and nums[l] == nums[l-1]: l += 1
                elif s > 0:
                    # 太大了,减小右指针
                    r -= 1
                    while l < r and nums[r] == nums[r+1]: r -= 1
                else:
                    # 刚刚好
                    res.append([nums[k], nums[l], nums[r]])
                    # 可以趁机继续剪枝
                    l += 1
                    r -= 1
                    while l < r and nums[l] == nums[l-1]: l += 1
                    while l < r and nums[r] == nums[r+1]: r -= 1
                    

        return res

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

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        # 先序遍历的第一个元素肯定是根节点, [根节点, 左子树, 右子树]
        # 中序遍历的结果是,[左子树,根节点,右子树]
        # 因此,第一步先通过先序遍历的第一个元素找到根节点,再在中序遍历中找到根节点所对应的位置,
        if not preorder: return None
        
        root_val = preorder[0]
        root = TreeNode(root_val)

        seperate_idx = inorder.index(root_val)
        left_inorder = inorder[:seperate_idx]
        right_inorder = inorder[seperate_idx+1:]

        left_preorder = preorder[1:1+len(left_inorder)]
        right_preorder = preorder[1+len(left_inorder):]

        root.left = self.buildTree(left_preorder, left_inorder)
        root.right = self.buildTree(right_preorder, right_inorder)

        return root

此外,从三种遍历中任意取两个出来,都可以恢复这个二叉树。

56. 合并区间()

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        n = len(intervals)
        if n < 2: return intervals

        intervals = sorted(intervals, key=lambda x: x[0]) # 按照左边界进行排序
        res = []
        l, r = 0, 1
        while r < n:
            left = intervals[l][0]
            right = intervals[l][1]

            while r < n and intervals[r][0] <= right:
                right = max(intervals[r][1], right)
                r += 1
            res.append([left, right])

            l = r
            r += 1
        
        if l == n - 1:
            res.append(intervals[l])
        
        return res

200. 岛屿数量

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        def dfs(i, j):
            # 终止条件
            if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return
            if grid[i][j] == '0': return


            # 内部逻辑
            grid[i][j] = '0' # 淹没该陆地
            up = dfs(i-1, j)
            down = dfs(i+1, j)
            left = dfs(i, j-1)
            right = dfs(i, j+1)

            return 

        m, n = len(grid), len(grid[0])

        res = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '1':
                   dfs(i, j)
                   res += 1 

        return res

105. 岛屿的最大面积

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:

        def dfs(i, j):
            if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return 0
            if grid[i][j] == 0: return 0

            # 内部逻辑
            grid[i][j] = 0  # 淹没陆地
            return 1 + dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
        m, n = len(grid), len(grid[0])

        res = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    area = dfs(i, j)
                    res = max(res, area)
        
        return res

463. 岛屿的周长()

class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        res = 0
        m, n = len(grid), len(grid[0])
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 1:
                    res += 4
                    # 如果上下左右有相邻的陆地,有一块就要减去1
                    for dx, dy in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
                        x, y = i + dx, j + dy
                        if 0 <= x < m and 0 <= y < n and grid[x][y] == 1:
                            res -= 1
    
        return res

如果有多块陆地,下面的递归方法稍微改一下,价格计数器,每次dfs完把当前岛的周长加到计数器上,最后一起返回就行了。

class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        def dfs(i, j):
            if not (0 <= i < len(grid)) or not (0 <= j < len(grid[0])): return 1 # 边界
            if grid[i][j] == 0: return 1 # 水
            if grid[i][j] != 1: return 0 # 走过了
            
            grid[i][j] = 2 # 走过的路标记一下

            return dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
        
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == 1:
                    return dfs(i, j)

827. 最大人工岛()

239. 滑动窗口最大值

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        q = [(-nums[i], i) for i in range(k)]
        heapq.heapify(q)

        res = [-q[0][0]]
        n = len(nums)
        for i in range(k, n):
            heapq.heappush(q, (-nums[i], i))
            while q[0][1] <= i - k:
                heapq.heappop(q)

            res.append(-q[0][0])
        return res
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        q = []
        for i in range(k):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)
        
        res = [nums[q[0]]]
        for i in range(k, len(nums)):
            while q and nums[i] >= nums[q[-1]]:
                q.pop()
            q.append(i)
            while q and q[0] <= i - k:
                q.pop(0)
            
            res.append(nums[q[0]])
        
        return res

17. 电话号码的字母组合

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits: return []
        
        dic = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno',
        '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}

        paths, path = [], []
        def backtracking(i, path):
            if len(path) >= len(digits): 
                paths.append(''.join(path))
                return
            
            for digit in dic[digits[i]]:
                path.append(digit)
                backtracking(i+1, path)
                path.pop()
            
        backtracking(0, path)

        return paths

75. 颜色分类

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        def quickSort(i, j):
            if i >= j: return
            pivot = nums[i]
            low = i
            high = j

            while i < j:
                while i < j and nums[j] >= pivot: j -= 1
                nums[i] = nums[j]
                while i < j and nums[i] < pivot: i += 1
                nums[j] = nums[i]
            nums[j] = pivot
            quickSort(low, j-1)
            quickSort(j+1, high)
        
        quickSort(0, len(nums)-1)

79. 单词搜索

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        # dfs
        def dfs(i, j, k):
            if k >= len(word): return True
            if not (0 <= i < len(board)) or not (0 <= j < len(board[0])): return False
            if board[i][j] != word[k]: return False
            
            tmp = board[i][j]
            board[i][j] = ''
            res = dfs(i-1, j, k+1) or dfs(i+1, j, k+1) or dfs(i, j-1, k+1) or dfs(i, j+1, k+1)
            board[i][j] = tmp

            return res

        m, n = len(board), len(board[0])
        for i in range(m):
            for j in range(n):
                if board[i][j] == word[0]:
                    if dfs(i, j, 0):
                        return True
        
        return False

32. 最长有效括号()

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        # dp
        # dp[i]表示以i结尾的最长有效括号的长度
        if not s: return 0

        n = len(s)
        dp = [0] * n

        for i in range(1, n):
            # 遇到右括号,上前匹配左括号
            if s[i] == ')':
                pre = i - dp[i-1] - 1
                if pre >= 0 and s[pre] == '(':
                    dp[i] = dp[i-1] + 2
                    # 防止出现独立括号,比如()(),()(())的情况
                    if pre > 0:
                        dp[i] += dp[pre-1]
        return max(dp)

33. 搜索旋转排序数组()

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        l, r = 0, len(nums) - 1
        
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            # 前半截有序
            if nums[0] <= nums[mid]:
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            else:
                if nums[mid] < target <= nums[-1]:
                    l = mid + 1
                else:
                    r = mid - 1
        
        return -1

39. 组合总和

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        paths = []

        def backtracking(i, cur_sum, path):
            if cur_sum == target:
                paths.append(path[:])
                return
            if cur_sum > target:
                return

            
            for i in range(i, len(candidates)):
                path.append(candidates[i])
                cur_sum += candidates[i]
                backtracking(i, cur_sum, path)
                path.pop()
                cur_sum -= candidates[i]
        
        backtracking(0, 0, [])
        return paths

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):
                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]
        

64. 最小路径和

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0] * n for _ in range(m)]

        dp[0][0] = grid[0][0]
        # 初始化dp
        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        
        for i in range(1, n):
            dp[0][i] = dp[0][i-1] + grid[0][i]
        
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
        
        return dp[-1][-1]

70. 爬楼梯

class Solution:
    def climbStairs(self, n: int) -> int:
        if n < 3: return n
        l, r = 1, 2

        for i in range(2, n):
            next = l + r
            l = r
            r = next
        
        return r

72. 编辑距离( 原问题很简单,注意变体,添加了转换成本)

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        
        dp = [[0] * (n+1) for _ in range(m+1)]
        
        for i in range(1, m+1):
            dp[i][0] = dp[i-1][0] + 1
        
        for i in range(1, n+1):
            dp[0][i] = dp[0][i-1] + 1
        
        # 开始,假如插入,删除,替换都有不同的成本,那么直接把下面对应的1换成对应的成本就行了
        # 整体上不会有太大的变化
        for i in range(1, m+1):
            for j in range(1, n+1):
                left = dp[i-1][j] + 1  # xx变成xx#,新增一个字符
                down = dp[i][j-1] + 1  # xxx变成xx,删除一个字符
                left_down = dp[i-1][j-1]
                if word1[i-1] != word2[j-1]:
                    left_down += 1  # xx#变成xx*, 替换一个字符
                dp[i][j] = min(left_down, left, down)
        return dp[-1][-1]

78. 子集

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        paths = []
        path = []

        def backtrackeing(i, path):
            paths.append(path[:])
            if len(path) >= len(nums):
                return
            
            for j in range(i, len(nums)):
                path.append(nums[j])
                backtrackeing(j+1, path)
                path.pop()
            
        backtrackeing(0, path)
        return paths

49. 字母异位词分组

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        dic = collections.defaultdict(list)

        for s in strs:
            dic[''.join(sorted(s))].append(s)
        
        return list(dic.values())

98. 验证二叉搜索树()

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        
        def recur(root, lower=-float('inf'), upper=float('inf')):
            if not root: return True

            if root.val <= lower or root.val >= upper:
                return False
            
            left = recur(root.left, lower, root.val)
            right = recur(root.right, root.val, upper)

            return left and right
        
        return recur(root)
class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        nums = []
        def inorder(root):
            if not root: return
            inorder(root.left)
            nums.append(root.val)
            inorder(root.right)
        
        inorder(root)
        # 得到nums之后只需要判断它是不是有序的
        for i in range(len(nums) - 1):
            if nums[i+1] <= nums[i]:
                return False
        
        return True

114. 二叉树展开为链表

# 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 flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        nums = []
        def dfs(root):
            if not root: return
            nums.append(root.val)
            dfs(root.left)
            dfs(root.right)
        
        dfs(root)

        cur = root
        for num in nums[1:]:
            cur.left = None
            if cur.right:
                cur.right.val = num
            else:
                cur.right = TreeNode(num)
            cur = cur.right

121. 买卖股票的最佳时机

假设第i天要卖出股票,那么能赚的最大的收益就是前面最低的价格与当前价格的差。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n < 2: return 0

        res = 0
        min_price = float('inf')
        for num in prices:
            res = max(res, num - min_price)
            min_price = min(min_price, num)
        return res

128. 最长连续序列()

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2: return n
        
        nums_set = set(nums) 
        longest = 0

        for num in nums_set:
            if num - 1 not in nums_set:
                cur = 1

                while num + 1 in nums_set:
                    cur += 1
                    num += 1
                longest = max(longest, cur)
        
        return longest

136. 只出现一次的数字

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

139. 单词拆分

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        # 动态规划
        wordDict = set(wordDict)

        n = len(s)
        dp = [False] * (n + 1)
        dp[0] = True

        for i in range(n):
            for j in range(i+1, n+1):
                if dp[i] and s[i:j] in wordDict:
                    dp[j] = True
        
        return dp[-1]

141. 环形链表

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        visited = set()
        cur = head

        while cur:
            if cur not in visited:
                visited.add(cur)
                cur = cur.next
            else:
                return True
        
        return False
class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head: return False

        slow, fast = head, head.next

        while fast != slow:
            if not fast or not fast.next: return False
            slow = slow.next
            fast = fast.next.next
        
        return True

142. 环形链表 II

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head: return

        dic = {}
        cur = head
        idx = 0
        while cur:
            if cur not in dic:
                dic[cur] = idx
                cur = cur.next
            else:
                return cur
        return 
class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head: return 
        slow, fast = head, head

        while True:
            if not fast or not fast.next: return 
            slow = slow.next
            fast = fast.next.next
            if fast == slow: break
            
        fast = head
        while fast != slow:
            fast, slow = fast.next, slow.next

        return slow

152. 乘积最大子数组()

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2: return nums[0]

        max_num, min_num = nums[0], nums[0]
        p = nums[0]
        for num in nums[1:]:
            mx, mn = max_num, min_num
            max_num = max(mx * num, num, mn * num)
            min_num = min(mn * num, num, mx * num)
            p = max(max_num, p)
        
        return p

160. 相交链表

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        l1, l2 = headA, headB
        
        while l1 != l2:
            l1 = l1.next if l1 else headB
            l2 = l2.next if l2 else headA

        return l1            

169. 多数元素

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        dic = collections.Counter(nums)

        n = len(nums)
        for k, v in dic.items():
            if v > n // 2:
                return k
        
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        # 使用哈希表,排序都不是最优解
        # 最优的是投票
        count = 0
        candiadate = -1

        for num in nums:
            if count == 0:
                candiadate = num
            count += 1 if num == candiadate else -1
        
        return candiadate

198. 打家劫舍()

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 3: return max(nums)

        dp = [0] * n
        dp[0] = nums[0]
        dp[1] = max(dp[0], nums[1])

        for i in range(2, n):
            dp[i] = max(dp[i-2]+nums[i], dp[i-1])
        
        return dp[-1]
class Solution:
    def rob(self, nums: List[int]) -> int:
        pre, cur = 0, 0 
        for num in nums:
            pre, cur = cur, max(pre+num, cur)
        
        return cur

221. 最大正方形(注意最大矩形)

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        m, n = len(matrix), len(matrix[0])

        dp = [[0] * n for _ in range(m)]
        max_len = 0

        for i in range(m):
            for j in range(n):
                if matrix[i][j] == '1':
                    if i == 0 or j == 0:
                        dp[i][j] = 1
                    else:
                        dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1
                    max_len = max(max_len, dp[i][j])
        return max_len ** 2

剑指 Offer II 040. 矩阵中最大的矩形()

最大矩形的解法和最大正方形的解法完全不一样。是一道很hard的题。他是直方图中最大矩形的扩展,直方图中最大矩形本身就是一个hard题了。

234. 回文链表()

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        nums = []
        cur = head

        while cur:
            nums.append(cur.val)
            cur = cur.next
        return nums[::-1] == nums

238. 除自身以外数组的乘积()

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        res = []
        n = len(nums)
        left, right = [1] * (n + 1), [1] * (n+1)
        
        for i in range(n):
            left[i+1] = left[i] * nums[i]
            right[n-i-1] = right[n-i] * nums[n-i-1]
        
        for i in range(n):
            res.append(left[i] * right[i+1])
        
        return res

最优解:

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        left, right = 1, 1
        res = [1] * n

        for i, num in enumerate(nums):
            res[i] *= left
            left *= num

            res[n-i-1] *= right
            right *= nums[n-i-1]
        
        return res

236. 二叉树的最近公共祖先()

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root: return
        if root.val == p.val or root.val == q.val: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right: return root # 1
        if not left: return right  # 2
        if not right: return left  # 3
        return root  # 4

240. 搜索二维矩阵 II

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m, n = len(matrix), len(matrix[0])

        i, j = 0, n - 1
        while 0 <= i < m and 0 <= j < n:
                if matrix[i][j] == target:
                    return True
                elif matrix[i][j] > target:
                    j -= 1
                elif matrix[i][j] < target:
                    i += 1
        
        return False

279. 完全平方数()

283. 移动零

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        if len(nums) < 2:
            return 
        
        l, r = 0, 1
        while r < len(nums):
            if nums[l] != 0:
                l += 1
            elif nums[r] != 0:
                nums[l], nums[r] = nums[r], nums[l]
                l += 1
            r += 1
        
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        if len(nums) < 2:
            return 
        
        l, r = 0, 0
        while r < len(nums):
            if nums[r] != 0:
                nums[l], nums[r] = nums[r], nums[l]
                l += 1
            r += 1
        

287. 寻找重复数

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
    	# O(n)时间复杂度。O(n)空间复杂度
        visited = set()
        for num in nums:
            if num in visited: return num
            visited.add(num)
        
        return -1
class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        # O(1)的空间复杂度,O(nlogn)时间复杂度
        n = len(nums)
        if n < 2: return nums[0]

        nums.sort()
        
        for l in range(n-1):
            if nums[l] == nums[l+1]:
                return nums[l]
        
        return -1

300. 最长递增子序列()

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2: return n
        # 每一个dp[i]表示以nums[i]为结尾的最长递增子序列的长度,以及子序列的最后一个值
        dp = [[1, nums[i]] for i in range(n)]

        res = 1
        for i in range(1, n):
            for j in range(i):
                if dp[j][1] < nums[i] and dp[j][0] + 1 > dp[i][0]:
                    dp[i][0] = dp[j][0] + 1
                    dp[i][1] = nums[i]
                    res = max(res, dp[i][0])
        return res

上面代码一个冗余的地方是,dp[i][1]就是nums[i]。那么根本没必要做两个元素的dp了,简化之后就是这样。但是时间复杂度依然是O(n^2)

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2: return n
        
        dp = [1] * n

        res = 1
        for i in range(1, n):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[j] + 1, dp[i])
                    res = max(res, dp[i])
        return res

还有一个O(nlogn)的解法,就是在dp的同时,加上二分。

309. 最佳买卖股票时机含冷冻期()

322. 零钱兑换()

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount+1):
           dp[i] = min(dp[i-c] if i - c >= 0 else float('inf') for c in coins) +  1
        
        return dp[-1] if dp[-1] != float('inf') else -1
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount+1):
            for c in coins:
                if i - c >= 0:
                    dp[i] = min(dp[i], dp[i-c] + 1)
        
        return dp[-1] if dp[-1] != float('inf') else -1
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        import functools
        @functools.lru_cache(None)
        def dfs(amount):
            if amount == 0: return 0
            return min(dfs(amount - c) if amount - c >= 0 else float('inf') for c in coins) + 1
        res = dfs(amount)
        return res if res != float('inf') else -1

347. 前 K 个高频元素

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        dic = collections.Counter(nums)
        
        tmp = sorted(dic.items(), key=lambda x: x[1], reverse=True)
        
        res = [t[0] for t in tmp[:k]]
        return res

617. 合并二叉树()

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if root1 and root2:
            root1.val += root2.val
            root1.left = self.mergeTrees(root1.left, root2.left)
            root1.right = self.mergeTrees(root1.right, root2.right)
            return root1
        else:
            return root1 or root2

461. 汉明距离

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        x = x ^ y 
        res = 0
        while x:
            if x & 1:
                res += 1
            x = x >> 1

        
        return res

448. 找到所有数组中消失的数字

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        nums_set = set(nums)
        n = len(nums)
        res = []
        for i in range(1, n+1):
            if i not in nums_set:
                res.append(i)
        
        return res
class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        for num in nums:    
            nums[abs(num) - 1] = -abs(nums[abs(num) - 1])
        
        res = []
        for i in range(len(nums)):
            if nums[i] > 0: res.append(i+1)
        return res

560. 和为 K 的子数组()

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        # 先计算前缀和数组
        n = len(nums)
        cul = [0] * (n + 1)
        for i in range(n):
            cul[i+1] = cul[i] + nums[i]
        
        res = 0
        # 两层循环遍历前缀和数组,
        for i in range(n+1):
            for j in range(i+1, n+1):
                if cul[j] - cul[i] == k:
                    res += 1
        
        return res
class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        dic = collections.defaultdict(int)
        dic[0] = 1
        
        res = 0
        sum = 0
        for i, num in enumerate(nums):
            sum += num
            if sum - k in dic:
                res += dic[sum - k]
            
            dic[sum] += 1
        return res

739. 每日温度()

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        res = [0] * n
        stack = []

        for i in range(n):
            while stack and temperatures[i] > temperatures[stack[-1]]:
                prev_index = stack.pop()
                res[prev_index] = i - prev_index
            stack.append(i)
        
        return res

309. 最佳买卖股票时机含冷冻期()

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        if n == 0: return 0

        # 定义一个dp数组,每个dp的元素都有四个元素,分别代表
        # dp[i][0]: 买入股票状态(或者之前已经买入股票,今天保持买入状态)
        # dp[i][1]: 前两天卖出了股票,已经度过了冷冻期,今天也不买入,保持卖出状态
        # dp[i][2]: 今天卖出股票
        # dp[i][3]: 今天为冷冻状态

        dp = [[0] * 4 for _ in range(n)]
        
        # 初始化dp,第一天不可能卖出,也不可能处于冷冻期,只有一种可能就是买入
        dp[0][0] = -prices[0]

        for i in range(1, n):
            dp[i][0] = max(dp[i-1][0], max(dp[i-1][3], dp[i-1][1]) - prices[i]) # 昨天买入股票和今天买入股票能获得的最大收益,其中今天买入股票的最大收益是max(昨天为冷冻状态,前两天卖出了股票)
            dp[i][1] = max(dp[i-1][1], dp[i-1][3])
            dp[i][2] = dp[i-1][0] + prices[i]
            dp[i][3] = dp[i-1][2]

        return max(dp[-1][1], dp[-1][2], dp[-1][3])

647. 回文子串()

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        if n < 2:
            return n

        # 可以使用中心扩展法,依次计算以i为中心的最长回文子串,当然要注意回文子串长度是偶数
        res = 0

        # 奇数
        for i in range(n):
            # 中心扩展
            l, r = i, i
            while 0 <= l < n and 0 <= r < n and s[l] == s[r]:
                res += 1
                l -= 1
                r += 1

        # 偶数
        for i in range(n):
            l, r = i, i + 1   
            while 0 <= l < n and 0 <= r < n and s[l] == s[r]:
                res += 1
                l -= 1
                r += 1
        
        return res

438. 找到字符串中所有字母异位词

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        n = len(s)
        p = sorted(p)
        m = len(p)

        res = []
        if m > n: return []
        if m == n: return [0] if sorted(s) == p else []

        for i in range(n - m + 1):
            if sorted(s[i:i+m]) == p:
                res.append(i)
        
        return res

406. 根据身高重建队列()

首先,为什么需要按照身高从高到底进行排序,因为,我们优先给高个子排队,这样之后排到矮个子的时候,即便矮个子要插队,也不会影响高个子的k。

举个例子,例如[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]],如果我们按照身高排序[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]。然后我们要按照从前往后的顺序进行插入,此时按照k进行插入的时候,会发现可能存在多个相同的k,比如k==0的有两个。因此,我们先插入7,再插入5的时候,还按照下标0来插入,他就会排在7的前面,而由于7比5高,所以,5排在前面也不会影响7。可以认为矮个子会被高个子自动无视。

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        # 先按照身高由高到低进行排序,如何身高相同,按照k从小到大进行排序
        people.sort(key=lambda x: (-x[0], x[1]))
        
        queue = []
        for pair in people:
            queue.insert(pair[1], pair)
        
        return queue

338. 比特位计数()

笨方法,在其他语言里,比如c++,这种方法速度也很快,但是在python里,这种方法速度比较慢。当然理论上的时间复杂度是一样的,这个是最暴力的方法。

class Solution:
    def countBits(self, n: int) -> List[int]:
        # 这里面是有规律的。假设n的二进制是k位,那么其实在前面n-1的维度上加上1,具体的首位需要加1,每个位置也要加1
        res = [self.countOne(i) for i in range(n+1)]

        return res

    def countOne(self, n):
        res = 0
        while n:
            res += n & 1
            n = n >> 1

        return res

最优解,在计算 i i i的时候,通过计算一个比 i i i小的值来间接计算出 i i i。比如,i & (i - 1)会消掉i的最右边的1,如果有一个1,就全部变成0了。i >> 1向右移动一位也可以把i变小,只是,此时i的最后一位不一定是0还是1,因此可以使用i & 1来判断。

class Solution:
    def countBits(self, n: int) -> List[int]:
        res = [0] * (n + 1)
        for i in range(1, n+1):
            res[i] = res[i & (i - 1)] + 1  # i & (i - 1) 可以去掉i最右边的一个1,如果只有一个1,就变成全0了,因为去掉了一个1,所以肯定比i小,那么肯定之前就计算过了,再加上去掉的右边那个1就行了
        
        return res
class Solution:
    def countBits(self, n: int) -> List[int]:
        res = [0] * (n + 1)
        for i in range(1, n+1):
            res[i] = res[i >> 1] + (i & 1)
        
        return res

543. 二叉树的直径()

class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        self.ma = 0
        if not root: return 0

        def dfs(root):
            # 叶子节点
            if not root.left and not root.right:
                return 0
            
            if root.left:
                leftSize = dfs(root.left) + 1
            else:
                leftSize = 0
            
            if root.right:
                rightSize = dfs(root.right) + 1
            else:
                rightSize = 0
            
            self.ma = max(self.ma, leftSize+rightSize) 
            
            return max(leftSize, rightSize)

        dfs(root)
        return self.ma
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        self.ma = 0
        if not root: return 0

        def dfs(root):
           if not root: return 0
           l = dfs(root.left)
           r = dfs(root.right)
           self.ma = max(l + r + 1, self.ma)
           return max(l, r) + 1
        
        dfs(root)
        return self.ma - 1

337. 打家劫舍 III()

class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        # 对于一个节点node,有两种选择,偷或者不偷
        # 偷:能获得的最大收益是不偷左右子节点的最大收益加上当前节点的最大收益
        # 不偷:那么子节点可以偷也可以不偷,因此node节点的最大收益就是计算出左右子节点偷或者不偷的最大收益

        def dfs(root):
            if not root: return (0, 0) # (偷的最大收益,不偷的最大收益)
            l, r = dfs(root.left), dfs(root.right)
            is_rob = l[1] + r[1] + root.val  # 偷该节点,那么该节点的最大收益就是该节点的值加上不偷左子节点,不偷右子节点的最大收益
            no_rob = max(l[0], l[1]) + max(r[0], r[1]) # 不偷该节点,那么该节点的最大收益就是左叶子节点的最大收益加上右叶子节点的最大收益(左右子节点偷或者不偷都可以)
            return (is_rob, no_rob)
        
        return max(dfs(root))

84. 柱状图中最大的矩形()

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # 思路就是找到每个height位置左右两边比他低的第一个柱子,然后他们之间的差减1就是以height作为短板最大的矩形 
        n = len(heights)
        left, right = [0] * n, [0] * n

        stack = []
        for i in range(n):
            while stack and heights[i] <= heights[stack[-1]]:
                stack.pop()
            # 当循环结束的时候,栈顶就是比heights[i]小的最近的柱子
            left[i] = stack[-1] if stack else -1
            stack.append(i)
        
        stack = []
        for i in range(n-1, -1, -1):
            while stack and heights[i] <= heights[stack[-1]]:
                stack.pop()
            # 当循环结束的时候,栈顶就是右边比heights[i]小的最近的柱子
            right[i] = stack[-1] if stack else n
            stack.append(i)
        
        res = max([(right[i] - left[i] - 1) * heights[i] for i in range(n)]) if n > 0 else 0
        return res

538. 把二叉搜索树转换为累加树

class Solution:
    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        self.total = 0

        def reverse_inorder(root):
            if not root: return
            # global total
            reverse_inorder(root.right)
            root.val = root.val + self.total
            self.total = root.val
            reverse_inorder(root.left)
        
        reverse_inorder(root)
        return root

208. 实现 Trie (前缀树)

class Trie:

    def __init__(self):
        self.data = set()

    def insert(self, word: str) -> None:
        self.data.add(word)

    def search(self, word: str) -> bool:
        return word in self.data

    def startsWith(self, prefix: str) -> bool:
        # 这一步是缩短时间的核心
        n = len(prefix)
        for word in self.data:
            if len(word) >= n:
                if word[:n] == prefix:
                    return True
        return False

# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

上面的是最暴力的解法,肯定不是最优解,其中搜索前缀这一步复杂度太高了,对于前缀树的应用场景,就是一次建树,多次查询。所以需要将查询前缀的复杂度降下去。

最优实现就是实现一个26叉树,因为有26个字母,第i层节点保存所有word的第i个位置的字母。

394. 字符串解码()

class Solution:
    def decodeString(self, s: str) -> str:
        # 不能简单循环模拟,因为可能有嵌套,项示例2那种
        n = len(s)

        stack, res, multi = [], '', 0
        for c in s:
            if c == '[':
                stack.append([res, multi])
                res, multi = '', 0
            elif c == ']':
                last_res, cur_multi = stack.pop()
                res = last_res + cur_multi * res
            elif '0' <= c <= '9':
                multi = 10 * multi + int(c)
            else:
                res += c
        
        return res

416. 分割等和子集()

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        # 0-1背包问题,背包容量是sum // 2,看看能否刚好装满一个sum // 2大小的背包,一旦可以装,那么剩下的自然可以装另一个了
        if sum(nums) % 2 == 1: return False

        total = sum(nums) // 2 # 背包容量
        n = len(nums) # 总的物品,其价值等于其体积
        # nums.sort()

        dp = [[0] * (total + 1) for _ in range(n)]
        for i in range(1, total+1):
            dp[0][i] = nums[0] if i >= nums[0] else 0
        
        # 开始dp
        for i in range(1, n):
            # 这里从后往前
            for j in range(1, total+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i]) # 这里都是用的是前一行的数据,因此可以使用滚动数组的方式,将二维dp转化到一维
        
        # 只要最后一列有一个等于target的,理论上下面应该全是等于target
        return dp[-1][-1] == total
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        if sum(nums) % 2 == 1: return False
        target = sum(nums) // 2
        n = len(nums)
        dp = [0] * (target + 1)

        # 初始化dp
        for i in range(target + 1):
            dp[i] = nums[0] if i >= nums[0] else 0
        
        # 开始dp
        for i in range(1, n):
            # 从后往前遍历是因为后面的数要用前面的数来算,所以如果先算前面的话,之前的数就被顶掉了,
            for j in range(target, nums[i]-1, -1): 
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
        
        return dp[-1] == target

494. 目标和()

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total = sum(nums)
        if abs(target) > total: return 0
        if (total + target) % 2 == 1: return 0
        n = len(nums)
        bagSize = (total + target) // 2  # 注意背包容量的计算,因为背包问题都是加法,因此我们也要转换成加法,假设加法总和是x,减法总和是sum-x,那么x-(sum - x) = target, x=(sum + target) // 2,如果是奇数,肯定无解,所以上面提前返回
        dp = [0] * (bagSize + 1)
        dp[0] = 1

        for i in range(n):
            for j in range(bagSize, nums[i]-1, -1):
                dp[j] += dp[j - nums[i]]
        
        return dp[-1]

148. 排序链表(归并排序)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next: return head
        slow, fast = head, head.next
        while fast and fast.next:
            slow, fast = slow.next, fast.next.next
        mid, slow.next = slow.next, None # 从中间断开
        left, right = self.sortList(head), self.sortList(mid)

        # 开始归并
        h = res = ListNode(0)
        while left and right:
            if left.val < right.val: h.next, left = left, left.next
            else: h.next, right = right, right.next
            h = h.next
        h.next = left if left else right
        return res.next

96. 不同的二叉搜索树()

class Solution:
    def numTrees(self, n: int) -> int:
        if n < 3: return n

        dp = [0] * (n + 1)
        dp[0] = 1
        dp[1] = 1

        for i in range(2, n+1):
            for j in range(1, i+1):
                dp[i] += dp[j-1] * dp[i-j]
        
        return dp[-1]

450. 删除二叉搜索树中的节点()

删除一个节点之后,重新建立的树可能有多种,只要找到一种就行。那么最简单的一种就是找到目标节点的右子树上的最小节点的值,用它代替被删除的节点,因为右子树上最小的几点肯定是沿着右子树一直往左走,一直走到没有左子节点。也就是说右子树上最小的节点一定是左子节点为空,因此再回溯删除这个值的时候就不会再次进入这个if。

# 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 deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        # dfs
        if not root: return 
        if root.val < key:
            root.right = self.deleteNode(root.right, key)
        elif root.val > key:
            root.left = self.deleteNode(root.left, key)
        else:
            # 如果子节点有一个是空的
            if not root.left or not root.right:
                root = root.left if root.left else root.right
            else:
                # 找到右子树最小的节点,将其赋给当前节点,然后删除右子树的最小节点
                cur = root.right
                while cur.left: cur = cur.left
                root.val = cur.val
                root.right = self.deleteNode(root.right, cur.val)
        
        return root

581. 最短无序连续子数组( 挺巧妙的)

非最优解法:

class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        # 先排序,排完序之后进行对比,找到同样位置不相同的最后一个位置,以及倒着找最后一个就可以了。
        n = len(nums)
        sort_nums = sorted(nums)
        
        l, r = -1, -1
        for i in range(n):
            if nums[i] != sort_nums[i] and l == -1:
                l = i
            if nums[i] != sort_nums[i]:
                r = i
        
        if l == r: return 0
        
        return r - l + 1
class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        n = len(nums)
        ma, mi = nums[0], nums[-1]
        l, r = -1, -1

        for i in range(n):
            ma = max(nums[i], ma)
            if nums[i] < ma:
                r = i
        
        for j in range(n-1, -1, -1):
            mi = min(nums[j], mi)
            if nums[j] > mi:
                l = j

        if l == r: return 0
        return r - l + 1

你可能感兴趣的:(leetcode,算法,深度优先)