0%

你一定听过基于连通性状态压缩的动态规划问题

关于动态规划

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果 [1] 。

LeetCode真题

70. Climbing Stairs

爬楼梯,一次可以爬一阶或两阶楼梯,爬上n阶楼梯有多少种方法?
查看原题

  • 解法

    1
    2
    3
    4
    5
    def fibonacci(n):
    a = b = 1
    for _ in range(n-1):
    a, b = b, a+b
    return b

746. Min Cost Climbing Stairs

楼梯上每层写了到达该层的卡路里,求上到顶层消耗的最小卡路里。
查看原题

1
2
3
4
5
6
7
Input: cost = [10, 15, 20]
Output: 15
Explanation: Cheapest is start on cost[1], pay that cost and go to the top.

Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
Output: 6
Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3].
  • 到达一层有两种选择,一种是上一层,一种是上两层。

    1
    2
    3
    4
    5
    def minCostClimbingStairs(self, cost: List[int]) -> int:
    f1 = f2 = 0
    for x in reversed(cost):
    f1, f2 = min(f1, f2) + x, f1
    return min(f1, f2)

121. Best Time to Buy and Sell Stock

买入卖出最大收益。原题
查看原题

1
2
3
4
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
  • 其实就是求最高峰点和前面最低谷点的差。

    1
    2
    3
    4
    5
    6
    7
    8
    def maxProfit(self, prices: List[int]) -> int:
    ans, min_buy = 0, float('inf')
    for price in prices:
    if price < min_buy:
    min_buy = price
    elif price-min_buy > ans:
    ans = price - min_buy
    return ans

122. Best Time to Buy and Sell Stock II

买入卖出,允许多次交易。
查看原题

1
2
3
4
Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
  • 比较每两天的价格,如果是涨价了,那就把收益计算进去,否则不出手交易。

    1
    2
    3
    4
    5
    6
    def max_profit(prices):
    profit = 0
    for i in range(1, len(prices)):
    if prices[i] > prices[i-1]:
    profit += prices[i] - prices[i-1]
    return profit
  • 方法二:弗洛伊德算法,这个时间复杂度为O(N^3),space: O(N^2)但是代码简单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def findTheCity(self, n: int, edges: List[List[int]], maxd: int) -> int:
    dis = [[float('inf')] * n for _ in range(n)]
    for i, j, w in edges:
    dis[i][j] = dis[j][i] = w
    for i in range(n):
    dis[i][i] = 0
    for k in range(n):
    for i in range(n):
    for j in range(n):
    dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j])
    ans = {sum(d<=maxd for d in dis[i]): i for i in range(n)} # 这里id大的会将小的覆盖
    return ans[min(ans)]

Best Time to Buy and Sell Stock III

最多允许交易两次。
查看原题

  • 先从左到右按照一次交易计算每天的利润。然后按照从右到左,判断如果进行第二次交易,最大的利润。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def maxProfit(self, prices: List[int]) -> int:
    min_buy = float('inf')
    profits = []
    max_profit = 0
    for p in prices:
    min_buy = min(min_buy, p)
    max_profit = max(max_profit, p-min_buy)
    profits.append(max_profit)

    max_profit = 0
    total_profit = 0
    max_sell = float('-inf')
    for i in range(len(prices)-1, -1, -1):
    max_sell = max(max_sell, prices[i])
    max_profit = max(max_profit, max_sell-prices[i])
    total_profit = max(total_profit, max_profit+profits[i])
    return total_profit

198. House Robber

抢劫房子问题。不能连续抢劫两个挨着的房间。
查看原题

1
2
3
4
Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.
  • 解法

    1
    2
    3
    4
    5
    def rob(self, nums):
    last, now = 0, 0
    for num in nums:
    last, now = now, max(last+num, now)
    return now

213. House Robber II

与上题不同的是,所有的房子连成一个环。
查看原题

1
2
3
4
Input: [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
because they are adjacent houses.
  • 注意nums长度为1的情况。

    1
    2
    3
    4
    5
    6
    7
    8
    def rob(self, nums: List[int]) -> int:

    def robber(nums):
    last = now = 0
    for num in nums:
    last, now = now, max(last+num, now)
    return now
    return max(robber(nums[:-1]), robber(nums[len(nums)!=1:]))

303. Range Sum Query - Immutable

给定一个数组,计算索引i, j之间的和。
查看原题

1
2
3
4
5
Given nums = [-2, 0, 3, -5, 2, -1]

sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
  • 思路:如果单纯采用切片计算,效率过低,题中要求sumRange调用多次。所以这里采用动态规划。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class NumArray:

    def __init__(self, nums):
    # self.sum_item = [0]
    # for num in nums:
    # self.sum_item.append(self.sum_item[-1] + num)
    from itertools import accumulate
    from operator import add
    self.sum_item = list(accumulate(nums, add))

    def sumRange(self, i, j):
    # return self.sum_item[j+1] - self.sum_item[i]
    return self.sum_item[j] - self.sum_item[i-1] if i > 0 else self.sum_item[j]

91. Decode Ways

将数字翻译成字母有多少种方式。
查看原题

1
2
3
Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def numDecodings(self, s: str) -> int:
    # w tells the number of ways
    # v tells the previous number of ways
    # d is the current digit
    # p is the previous digit
    v, w, p = 0, int(s>''), ''
    for d in s:
    v, w, p = w, int(d>'0')*w + (9<int(p+d)<27)*v, d
    return w

62. Unique Paths

一个矩阵中,从左上走到右下有多少种不同走法,每次只能向右或向下移动。
查看原题

  • 方法一:构建二维矩阵。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def uniquePaths(self, m: int, n: int) -> int:
    g = [[0] * m for _ in range(n)]
    for i in range(n):
    for j in range(m):
    if i==0 or j==0:
    g[i][j] = 1
    else:
    g[i][j] = g[i-1][j] + g[i][j-1]

    return g[-1][-1]
  • 方法二:二维数组时没有必要的,仔细观察发现每层都是累计的关系,accumulate为此而生。

    1
    2
    3
    4
    5
    def uniquePaths(self, m: int, n: int) -> int:
    row = [1] * m
    for _ in range(n-1):
    row = itertools.accumulate(row)
    return list(row)[-1]

63. Unique Paths II

和62一样,不同的是中间加了障碍1。
查看原题

1
2
3
4
5
6
7
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def uniquePathsWithObstacles(self, g: List[List[int]]) -> int:
    R, C = len(g), len(g[0])
    if g[0][0] == 1:
    return 0
    g[0][0] = 1
    for i in range(1, C):
    g[0][i] = int(g[0][i-1]==1 and g[0][i]==0)

    for j in range(1, R):
    g[j][0] = int(g[j-1][0]==1 and g[j][0]==0)

    for i in range(1, R):
    for j in range(1, C):
    if g[i][j] == 0:
    g[i][j] = g[i-1][j] + g[i][j-1]
    else:
    g[i][j] = 0
    return g[-1][-1]

120. Triangle

三角形从上到下最小路径。
查看原题

1
2
3
4
5
6
7
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
i.e., 2 + 3 + 5 + 1 = 11
  • 错位相加大法。

    1
    2
    3
    4
    5
    6
    7
    def minimumTotal(self, triangle: List[List[int]]) -> int:
    from functools import reduce
    def combine_rows(lower_row, upper_row):
    return [upper + min(lower_left, lower_right)
    for upper, lower_left, lower_right in
    zip(upper_row, lower_row, lower_row[1:])]
    return reduce(combine_rows, triangle[::-1])[0]

931. Minimum Falling Path Sum

和120相似,不过形状变成了矩形。
查看原题

1
2
3
4
Input: [[1,2,3],[4,5,6],[7,8,9]]
Output: 12
Explanation:
The possible falling paths are:
  • 方法一:常规写法。

    1
    2
    3
    4
    5
    6
    7
    def minFallingPathSum(self, A: List[List[int]]) -> int:
    R, C = len(A), len(A[0])
    for i in range(R-2, -1, -1):
    for j in range(C):
    path = slice(max(0, j-1), min(C, j+2))
    A[i][j] += min(A[i+1][path])
    return min(A[0])
  • 方法二:错位计算的方式,这个比120三角形的要复杂一点。需要填充无穷大来使生效。

    1
    2
    3
    4
    5
    6
    7
    8
    def minFallingPathSum(self, A: List[List[int]]) -> int:
    from functools import reduce
    padding = [float('inf')]
    def combine_rows(lower_row, upper_row):
    return [upper + min(lower_left, lower_mid, lower_right)
    for upper, lower_left, lower_mid, lower_right in
    zip(upper_row, lower_row[1:]+padding, lower_row, padding+lower_row[:-1])]
    return min(reduce(combine_rows, A[::-1]))

1289. Minimum Falling Path Sum II

上题变形,每行找到非自己那列的元素。
查看原题

  • 用堆记录2个最小的值。

    1
    2
    3
    4
    5
    6
    7
    def minFallingPathSum(self, arr: List[List[int]]) -> int:
    m, n = len(arr), len(arr[0])
    for i in range(1, m):
    r = heapq.nsmallest(2, arr[i-1])
    for j in range(n):
    arr[i][j] += r[1] if arr[i-1][j]==r[0] else r[0]
    return min(arr[-1])

279. Perfect Squares

完美平方,找出n的最少的能被几个平方数相加。
查看原题

1
2
3
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
  • f(n)表示n最少的个数。f(n)=min(f(n-1²), f(n-2²)…f(0)) + 1

    1
    2
    3
    4
    5
    6
    7
    8
    class Solution:
    _dp = [0]
    def numSquares(self, n: int) -> int:
    dp = self._dp
    while len(dp) <= n:
    # dp.append(min(dp[len(dp)-i*i] for i in range(1, int(len(dp)**0.5+1))) + 1)
    dp.append(min(dp[-i*i] for i in range(1, int(len(dp)**0.5+1))) + 1)
    return dp[n]

5. Longest Palindromic Substring

最长回文子字符串。
查看原题

1
2
3
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
  • 马拉车算法。Time: O(n).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def longestPalindrome(self, s: str) -> str:
    # Transform S into T.
    # For example, S = "abba", T = "^#a#b#b#a#$".
    # ^ and $ signs are sentinels appended to each end to avoid bounds checking
    T = '#'.join('^{}$'.format(s))
    n = len(T)
    P = [0] * n
    C = R = 0
    for i in range (1, n-1):
    P[i] = (R > i) and min(R - i, P[2*C - i]) # equals to i' = C - (i-C)
    # Attempt to expand palindrome centered at i
    while T[i + 1 + P[i]] == T[i - 1 - P[i]]:
    P[i] += 1

    # If palindrome centered at i expand past R,
    # adjust center based on expanded palindrome.
    if i + P[i] > R:
    C, R = i, i + P[i]

    # Find the maximum element in P.
    maxLen, centerIndex = max((n, i) for i, n in enumerate(P))
    return s[(centerIndex - maxLen)//2: (centerIndex + maxLen)//2]

1024. Video Stitching

影片剪辑,给定n组影片段,求能够拼出0~T完整影片所使用的最小段数。
查看原题

1
2
3
4
5
6
7
Input: clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], T = 10
Output: 3
Explanation:
We take the clips [0,2], [8,10], [1,9]; a total of 3 clips.
Then, we can reconstruct the sporting event as follows:
We cut [1,9] into segments [1,2] + [2,8] + [8,9].
Now we have segments [0,2] + [2,8] + [8,10] which cover the sporting event [0, 10].
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def videoStitching(self, clips: List[List[int]], T: int) -> int:
    end, end2, cnt = -1, 0, 0 # end 表示上一段最后截止点,end2表示当前可以最大延伸的最远地点。
    for s, e in sorted(clips):
    if end2 >= T or s > end2: # 完成或者接不上了
    break
    elif end < s <= end2: # 续1s
    cnt += 1
    end = end2
    end2 = max(end2, e)
    return cnt if end2 >= T else -1

1048. Longest String Chain

每个字符添加任意一个字符,可以组成一个字符串链。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def longestStrChain(self, words: List[str]) -> int:
    words2 = {i:set() for i in range(1, 17)}
    for word in words:
    words2[len(word)].add(word)
    dp = collections.defaultdict(lambda : 1)
    for k in range(2, 17):
    for w in words2[k]:
    for i in range(k):
    prev = w[:i] + w[i+1:]
    if prev in words2[k-1]:
    # dp[w] = max(dp[w], dp[prev]+1)
    dp[w] = dp[prev] + 1
    return max(dp.values() or [1])

1143. Longest Common Subsequence

最长公共子串的长度。
查看原题

1
2
3
Input: text1 = "abcde", text2 = "ace" 
Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.
  • 方法一:递归

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import functools
    class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
    @functools.lru_cache(None)
    def helper(i,j):
    if i<0 or j<0:
    return 0
    if text1[i]==text2[j]:
    return helper(i-1,j-1)+1
    return max(helper(i-1,j),helper(i,j-1))
    return helper(len(text1)-1,len(text2)-1)
  • 方法二:迭代。dp(i,j) means the longest common subsequence of text1[:i] and text2[:j].

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
    n1, n2 = len(text1), len(text2)
    dp = [[0]*(n2+1) for i in range(n1+1)]
    for i in range(n1):
    for j in range(n2):
    if text1[i] == text2[j]:
    dp[i+1][j+1] = dp[i][j] + 1
    else:
    dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j])
    return dp[-1][-1]

1312. Minimum Insertion Steps to Make a String Palindrome

将一个字符串变为回文串,最小插入字母步数。
查看原题

1
2
3
4
5
6
Input: s = "zzazz"
Output: 0
Explanation: The string "zzazz" is already palindrome we don't need any insertions.
Input: s = "leetcode"
Output: 5
Explanation: Inserting 5 characters the string becomes "leetcodocteel".
  • 和1134,Longest Common Subsequence一样,当这个字符串和他倒序的公共子串越多,需要添加的字母就越少。

    1
    2
    3
    4
    5
    6
    7
    def minInsertions(self, s: str) -> int:
    n = len(s)
    dp = [[0] * (n+1) for _ in range(n+1)]
    for i in range(n):
    for j in range(n):
    dp[i+1][j+1] = dp[i][j] + 1 if s[i] == s[~j] else max(dp[i+1][j], dp[i][j+1])
    return n - dp[n][n]

221. Maximal Square

最大的正方形岛屿面积。
查看原题

1
2
3
4
5
6
7
8
Input: 

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Output: 4
  • 方法一:此题看似和最大岛屿面积相似,但解法完全不同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def maximalSquare(self, matrix: List[List[str]]) -> int:
    if not matrix:
    return 0
    m, n = len(matrix), len(matrix[0])
    dp = [[0] * (n+1) for _ in range(m+1)]
    max_side = 0
    for i in range(m):
    for j in range(n):
    if matrix[i][j] == '1':
    dp[i+1][j+1] = min(dp[i][j], dp[i+1][j], dp[i][j+1]) + 1
    max_side = max(max_side, dp[i+1][j+1])
    return max_side**2

1340. Jump Game V

跳跃游戏,可以向左右d范围内矮的地方跳下。
查看原题

1
2
3
4
5
Input: arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
Output: 4
Explanation: You can start at index 10. You can jump 10 --> 8 --> 6 --> 7 as shown.
Note that if you start at index 6 you can only jump to index 7. You cannot jump to index 5 because 13 > 9. You cannot jump to index 4 because index 5 is between index 4 and 6 and 13 > 9.
Similarly You cannot jump from index 3 to index 2 or index 1.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def maxJumps(self, arr: List[int], d: int) -> int:
    n = len(arr)
    ans = [0] * n

    def jump(i):
    if ans[i]: return ans[i]
    ans[i] = 1
    for di in (-1, 1):
    for j in range(i+di, i+d*di+di, di):
    if not (0<=j<n and arr[j]<arr[i]): break
    ans[i] = max(ans[i], jump(j)+1)
    return ans[i]

1301. Number of Paths with Max Score

左上到右下,最大值,路径中存在障碍,并且需要返回路径的个数。
查看原题

1
2
Input: board = ["E23","2X2","12S"]
Output: [7,1]
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def pathsWithMaxScore(self, board: List[str]) -> List[int]:
    n, mod = len(board), 10**9+7
    dp = [[[float('-inf'), 0] for j in range(n+1)] for i in range(n+1)]
    dp[n-1][n-1] = [0, 1]
    for x in range(n)[::-1]:
    for y in range(n)[::-1]:
    if board[x][y] in 'XS': continue
    for i, j in ((0, 1), (1, 0), (1, 1)):
    if dp[x][y][0] < dp[x+i][y+j][0]:
    dp[x][y] = [dp[x+i][y+j][0], 0]
    if dp[x][y][0] == dp[x+i][y+j][0]:
    dp[x][y][1] += dp[x+i][y+j][1]
    dp[x][y][0] += int(board[x][y]) if x or y else 0
    return [dp[0][0][0] if dp[0][0][1] else 0, dp[0][0][1] % mod]

1277. Count Square Submatrices with All Ones

矩阵中最多有多少个1构成的正方形。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
Input: matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
Output: 15
Explanation:
There are 10 squares of side 1.
There are 4 squares of side 2.
There is 1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def countSquares(self, mat: List[List[int]]) -> int:
    m, n = len(mat), len(mat[0])
    # dp[i][j] 表示以i, j为右下点时,正方形的个数。
    dp = [[0] * (n) for i in range(m)]
    for i in range(m):
    for j in range(n):
    if mat[i][j] == 1:
    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
    return sum(map(sum, dp))

1269. Number of Ways to Stay in the Same Place After Some Steps

回到原点的走法一共有多少种,一次只能向右,向左或者停留,要求始终保持在数组范围。
查看原题

1
2
3
4
5
6
7
Input: steps = 3, arrLen = 2
Output: 4
Explanation: There are 4 differents ways to stay at index 0 after 3 steps.
Right, Left, Stay
Stay, Right, Left
Right, Stay, Left
Stay, Stay, Stay
  • 找到状态转移方程,dp[p][s] = dp[p-1][s-1] + dp[p][s-1] + dp[p+1, s-1]p代表位置,s代表步数。首部添加0方便求和。注意t+3这个范围。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def countSquares(self, mat: List[List[int]]) -> int:
    m, n = len(mat), len(mat[0])
    # dp[i][j] 表示以i, j为右下点时,正方形的个数。
    dp = [[0] * (n) for i in range(m)]
    for i in range(m):
    for j in range(n):
    if mat[i][j] == 1:
    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
    return sum(map(sum, dp))

338. Counting Bits

返回从0到num的数中,每个数二进制中含有1的个数。
查看原题

1
2
Input: 5
Output: [0,1,1,2,1,2]
  • dp 。f[i]=f[i//2]+i&1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def countSquares(self, mat: List[List[int]]) -> int:
    m, n = len(mat), len(mat[0])
    # dp[i][j] 表示以i, j为右下点时,正方形的个数。
    dp = [[0] * (n) for i in range(m)]
    for i in range(m):
    for j in range(n):
    if mat[i][j] == 1:
    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
    return sum(map(sum, dp))

1262. Greatest Sum Divisible by Three

最多的元素和能被3整除。
查看原题

1
2
3
Input: nums = [3,6,5,1,8]
Output: 18
Explanation: Pick numbers 3, 6, 1 and 8 their sum is 18 (maximum sum divisible by 3).
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def maxSumDivThree(self, nums: List[int]) -> int:
    # dp[pos][mod]
    # # 0 1 2
    # 0 3 0 0
    # 1 9 0 0
    # 2 9 0 14
    # 3 15 10 14
    # 4 18 22 23
    dp = [0] * 3
    for a in nums:
    for j in dp[:]:
    dp[(j+a) % 3] = max(dp[(j+a) % 3], j+a)
    return dp[0]

72. Edit Distance

两个单词,将a变成b的最小步数,可以添加、删除,替换一个字母。
查看原题

1
2
3
4
5
6
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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(m+1):
    dp[i][0] = i
    for j in range(n+1):
    dp[0][j] = j

    for i in range(m):
    for j in range(n):
    if word1[i] == word2[j]:
    dp[i+1][j+1] = dp[i][j]
    else:
    dp[i+1][j+1] = min(dp[i+1][j], dp[i][j+1], dp[i][j]) + 1
    return dp[-1][-1]

518. Coin Change 2

找钱问题,给你几种面值的硬币,不限制每种硬币的个数,问组成多少钱有多少种方法。
查看原题

1
2
3
4
5
6
7
Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
  • 背包问题

    1
    2
    3
    4
    5
    6
    7
    8
    def change(self, amount: int, coins: List[int]) -> int:
    dp = [0] * (amount + 1)
    dp[0] = 1
    for i in coins:
    for j in range(1, amount + 1):
    if j >= i:
    dp[j] += dp[j - i]
    return dp[amount]

1220. Count Vowels Permutation

元音字母的全排列,根据指定规则的,求全排列的个数。
查看原题

1
2
3
Input: n = 2
Output: 10
Explanation: All possible strings are: "ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou" and "ua".
  • 背包问题

    1
    2
    3
    4
    5
    def countVowelPermutation(self, n: int) -> int:
    a = e = i = o = u = 1
    for _ in range(n-1):
    a, e, i, o, u = e, a+i, a+e+o+u, i+u, a
    return (a+e+i+o+u) % (10**9+7)

368. Largest Divisible Subset

最大的整除子集。
查看原题

1
2
Input: [1,2,3]
Output: [1,2] (of course, [1,3] will also be ok)
  • 背包问题

    1
    2
    3
    4
    5
    def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
    S = {-1: set()}
    for x in sorted(nums):
    S[x] = max((S[d] for d in S if x % d == 0), key=len) | {x}
    return list(max(S.values(), key=len))

5456. Kth Ancestor of a Tree Node

找出一个树节点的k个祖先。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
Input:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]

Output:
[null,1,0,-1]

Explanation:
TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]);

treeAncestor.getKthAncestor(3, 1); // returns 1 which is the parent of 3
treeAncestor.getKthAncestor(5, 2); // returns 0 which is the grandparent of 5
treeAncestor.getKthAncestor(6, 3); // returns -1 because there is no such ancestor
  • 用的倍增法,binary lifting.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class TreeAncestor:

    step = 15
    def __init__(self, n, A):
    A = dict(enumerate(A))
    jump = [A]
    for s in range(self.step):
    B = {}
    for i in A:
    if A[i] in A:
    B[i] = A[A[i]]
    jump.append(B)
    A = B
    self.jump = jump
    print(jump)

    def getKthAncestor(self, x: int, k: int) -> int:
    step = self.step
    while k > 0 and x > -1:
    if k >= 1 << step:
    x = self.jump[step].get(x, -1)
    k -= 1 << step
    else:
    step -= 1
    return x

1477. Find Two Non-overlapping Sub-arrays Each With Target Sum

找到数组中等于目标值的两个不重叠子数组的最小长度和。
查看原题

1
2
3
Input: arr = [3,2,2,4,3], target = 3
Output: 2
Explanation: Only two sub-arrays have sum = 3 ([3] and [3]). The sum of their lengths is 2.
  • 方法一:看了提示后使用了前后遍历法做出来的。其实有一次遍历的方式。这个方法看了挺长时间,才明白,实际上记录了一个以end为结尾的前面的所有元素最好的长度是多少。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def minSumOfLengths(self, arr: List[int], target: int) -> int:
    prefix = {0: -1}
    best_till = [math.inf] * len(arr)
    ans = best = math.inf
    for i, curr in enumerate(itertools.accumulate(arr)):
    # print(i, curr)
    if curr - target in prefix:
    end = prefix[curr - target]
    if end > -1:
    ans = min(ans, i - end + best_till[end])
    best = min(best, i - end)
    # print('\t', best, i-end, best_till, ans)
    best_till[i] = best
    prefix[curr] = i
    return -1 if ans == math.inf else ans

494. Target Sum

给你一组数,用+或-连接起来最后等于target,问有多少种填法。
查看原题

1
2
3
4
5
6
7
8
9
Input: nums is [1, 1, 1, 1, 1], S is 3. 
Output: 5
Explanation:

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
    n = len(nums)

    self.memo = {}

    # @functools.lru_cache(maxsize=2**17)
    def dfs(i, total):
    # print(i, total)
    if (i, total) in self.memo:
    return self.memo[(i, total)]
    if i == n:
    return total==S
    ans = dfs(i+1, total+nums[i]) + dfs(i+1, total-nums[i])
    self.memo[(i, total)] = ans
    # return dfs(i+1, total+nums[i]) + dfs(i+1, total-nums[i])
    return ans

    return dfs(0, 0)

174. Dungeon Game

地牢游戏,从左上走到右下,每次只能像右或者向下,格子里会扣血和加血,问最少需要多少血,全程保持血量为1以上。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
    R, C = len(dungeon), len(dungeon[0])
    dp = [[0] * C for _ in range(R)]
    for i in range(R-1, -1, -1):
    for j in range(C-1, -1, -1):
    if i == R-1 and j == C-1:
    dp[i][j] = max(1, 1 - dungeon[i][j])
    elif i == R-1:
    dp[i][j] = max(1, dp[i][j+1] - dungeon[i][j])
    elif j == C-1:
    dp[i][j] = max(1, dp[i+1][j] - dungeon[i][j])
    else:
    dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j])
    return dp[0][0]

96. Unique Binary Search Trees

不重复的二叉搜索树,1~n节点。
查看原题

1
2
3
4
5
6
7
8
9
10
Input: 3
Output: 5
Explanation:
Given n = 3, there are a total of 5 unique BST's:

1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
  • 状态转移方程式这样的G(n)表示n个节点能组成的二叉搜索树节点个数。F(i, n)表示有n个节点时,以i为root的个数。G(n) = F(1, n) + F(2, n) + ... + F(n, n). F(3, 7)=G(2)*G(4)F(i, n) = G(i-1) * G(n-i), 所以最后G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)

    1
    2
    3
    4
    5
    6
    7
    def numTrees(self, n: int) -> int:
    G = [0] * (n+1)
    G[0] = G[1] = 1
    for i in range(2, n+1):
    for j in range(1, i+1):
    G[i] += G[j-1]*G[i-j]
    return G[n]

从Floyd开始学图

关于图

图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。

LeetCode真题

990. Satisfiability of Equality Equations

满足所有方程式,判断是否存在变量的值满足所有的等式与不等式。
查看原题

1
2
3
4
5
6
7
8
9
Input: ["a==b","b!=a"]
Output: false
Explanation: If we assign say, a = 1 and b = 1, then the first equation is satisfied, but not the second. There is no way to assign the variables to satisfy both equations.

Input: ["a==b","b==c","a==c"]
Output: true

Input: ["a==b","b!=c","c==a"]
Output: false
  • union find. 并查集。find方法可以想象成一个链表,返回的是链表末尾key,val相等的元素。同时建立连接关系。如a==b, b==c时fc={‘a’: ‘b’, ‘b’: ‘c’, ‘c’: ‘c’}比较a!=c时就会最终找到fc[‘a’] == ‘c’;如a==b, c==a时,fc={‘a’: ‘b’, ‘b’: ‘b’, ‘c’: ‘b’}。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def equationsPossible(self, equations: 'List[str]') -> 'bool':
    equations.sort(key=lambda e: e[1] == '!')
    uf = {a: a for a in string.ascii_lowercase}

    def find(x):
    if x != uf[x]:
    uf[x] = find(uf[x])
    return uf[x]

    for a, e, _, b in equations:
    if e == "=":
    uf[find(a)] = find(b)
    else:
    if find(a) == find(b):
    return False
    return True

997. Find the Town Judge

找到小镇审判长。审判长被除自己以外的所有人信任,并且不信任任何人。根据信任列表找出审判长。
查看原题

1
2
3
4
5
6
Input: N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
Output: 3
Input: N = 3, trust = [[1,3],[2,3],[3,1]]
Output: -1
Input: N = 3, trust = [[1,3],[2,3]]
Output: 3
  • 方法一:brute force.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def findJudge(self, N: int, trust: List[List[int]]) -> int:
    if not trust:
    return N
    a, b = zip(*trust)
    candidates = collections.Counter(b)
    villages = set(a)
    for c, votes in candidates.most_common():
    if votes < N - 1:
    return -1
    if c not in villages:
    return c
    return -1
  • 方法二:定向图。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def findJudge(self, N: int, trust: List[List[int]]) -> int:
    count = [0] * (N + 1)
    for i, j in trust:
    count[i] -= 1
    count[j] += 1
    print(count)
    for i in range(1, N + 1):
    if count[i] == N - 1:
    return i
    return -1

133. Clone Graph

深拷贝一个简单环。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def cloneGraph(self, node: 'Node') -> 'Node':
    cp = collections.defaultdict(lambda: Node(0, []))
    nodes = [node]
    seen = set()
    while nodes:
    n = nodes.pop()
    cp[n].val = n.val
    cp[n].neighbors = [cp[x] for x in n.neighbors]
    nodes.extend(x for x in n.neighbors if x not in seen)
    seen.add(n)
    return cp[node]

1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance

找到距离范围内邻居最少的城市。
查看原题

1
2
3
4
5
6
7
8
9
Input: n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4
Output: 3
Explanation: The figure above describes the graph.
The neighboring cities at a distanceThreshold = 4 for each city are:
City 0 -> [City 1, City 2]
City 1 -> [City 0, City 2, City 3]
City 2 -> [City 0, City 1, City 3]
City 3 -> [City 1, City 2]
Cities 0 and 3 have 2 neighboring cities at a distanceThreshold = 4, but we have to return city 3 since it has the greatest number.
  • 方法一:狄克斯特拉算法。这里没想到用一个堆来维持最小的距离。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int:
    g = collections.defaultdict(list)
    for u, v, w in edges:
    g[u].append((v, w))
    g[v].append((u, w))

    def count_neighbor(city):
    heap = [(0, city)]
    dist = {}

    while heap:
    cur_w, u = heapq.heappop(heap)
    if u in dist:
    continue
    if u != city:
    dist[u] = cur_w
    for v, w in g[u]:
    if v in dist:
    continue
    if cur_w + w <= distanceThreshold:
    heapq.heappush(heap, (cur_w+w, v))
    return len(dist)

    return min(range(n), key=lambda x: (count_neighbor(x), -x))
  • 方法二:弗洛伊德算法,这个时间复杂度为O(N^3),space: O(N^2)但是代码简单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def findTheCity(self, n: int, edges: List[List[int]], maxd: int) -> int:
    dis = [[float('inf')] * n for _ in range(n)]
    for i, j, w in edges:
    dis[i][j] = dis[j][i] = w
    for i in range(n):
    dis[i][i] = 0
    for k in range(n):
    for i in range(n):
    for j in range(n):
    dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j])
    ans = {sum(d<=maxd for d in dis[i]): i for i in range(n)} # 这里id大的会将小的覆盖
    return ans[min(ans)]

1267. Count Servers that Communicate

找到2个以上的服务器连接个数,服务器可以在一行或是一列算是连接上。
查看原题

1
2
3
Input: grid = [[1,1,0,0],[0,0,1,0],[0,0,1,0],[0,0,0,1]]
Output: 4
Explanation: The two servers in the first row can communicate with each other. The two servers in the third column can communicate with each other. The server at right bottom corner can't communicate with any other server.
  • 行列累计求和,但是只是用来判断而不是累加,然后遍历所有的元素。

    1
    2
    3
    def countServers(self, g: List[List[int]]) -> int:
    X, Y = tuple(map(sum, g)), tuple(map(sum, zip(*g)))
    return sum(X[i]+Y[j]>2 for i in range(len(g)) for j in range(len(g[0])) if g[i][j])

886. Possible Bipartition

将不喜欢的人放在两组中,根据关系是否能将其分为2组。
查看原题

1
2
3
4
5
6
Input: N = 4, dislikes = [[1,2],[1,3],[2,4]]
Output: true
Explanation: group1 [1,4], group2 [2,3]

Input: N = 3, dislikes = [[1,2],[1,3],[2,3]]
Output: false
  • 方法一:dfs。等同于在一个无向图中,寻找一个奇数边的环。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool:
    g = [[] for _ in range(N+1)]
    for a, b in dislikes:
    g[a].append(b)
    g[b].append(a)

    seen = set()
    def dfs(i, p, p_len):
    seen.add(i)
    p[i] = p_len
    for nxt in g[i]:
    if nxt not in seen:
    if dfs(nxt, p, p_len+1):
    return True
    elif nxt in p and (p_len-p[nxt])&1==0:
    return True
    p.pop(i)
    return False

    p = {}
    for i in range(1, N+1):
    if i not in seen and dfs(i, p, 0):
    return False
    return True

207. Course Schedule

课程调度,课程有依赖关系,问是否能完成所有的课程。
查看原题

1
2
3
4
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
  • 方法一:dfs。注意这里状态要用3中,1表示遍历过,-1表示正在遍历,0表未遍历。这样可以避免重复的遍历。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def canFinish(self, n: int, prerequisites: List[List[int]]) -> bool:
    g = [[] for _ in range(n)]
    for a, b in prerequisites:
    g[a].append(b)

    seen = [0] * n

    def dfs(i):
    if seen[i] in {1, -1}: return seen[i]==1
    seen[i] = -1
    if any(not dfs(j) for j in g[i]): return False
    seen[i] = 1
    return True

1462. Course Schedule IV

和上题差不多,问题不一样,问的是根据给定的依赖关系,判断两节课是否有依赖。
查看原题

  • bfs. 拓扑排序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def checkIfPrerequisite(self, n: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]:
    g = collections.defaultdict(list)
    degree = [0] * n
    pres = [set() for _ in range(n)]
    for u, v in prerequisites:
    g[u].append(v)
    degree[v] -= 1
    pres[v].add(u)
    bfs = [i for i in range(n) if degree[i]==0]
    for i in bfs:
    for j in g[i]:
    degree[j] += 1
    pres[j] |= pres[i]
    if degree[j] == 0:
    bfs.append(j)
    return [a in pres[b] for a, b in queries]

1466. Reorder Routes to Make All Paths Lead to the City Zero

有一个有向图,两个节点之间只有一条边,要求所有的边指向0,需要改多少条边的方向。
查看原题

1
2
3
Input: n = 6, connections = [[0,1],[1,3],[2,3],[4,0],[4,5]]
Output: 3
Explanation: Change the direction of edges show in red such that each node can reach the node 0 (capital).
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def minReorder(self, n: int, connections: List[List[int]]) -> int:
    g1 = collections.defaultdict(list)
    g2 = collections.defaultdict(list)
    for u, v in connections:
    g1[u].append(v)
    g2[v].append(u)

    seen = set()
    def dfs(i):
    seen.add(i)
    ans = 0
    for j in g1[i]:
    if j not in seen:
    ans += 1 + dfs(j)

    for k in g2[i]:
    if k not in seen:
    ans += dfs(k)

    return ans
    return dfs(0)

1210. Minimum Moves to Reach Target with Rotations

一个占位2格的蛇,从左上走到右下需要最少的步数,可以旋转。
查看原题

1
2
3
4
5
6
7
8
9
Input: grid = [[0,0,0,0,0,1],
[1,1,0,0,1,0],
[0,0,0,0,1,1],
[0,0,1,0,1,0],
[0,1,1,0,0,0],
[0,1,1,0,0,0]]
Output: 11
Explanation:
One possible solution is [right, right, rotate clockwise, right, down, down, down, down, rotate counterclockwise, right, down].
  • 将蛇的横竖状态记录,这样一个点也能表示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def minimumMoves(self, g: List[List[int]]) -> int:
    n = len(g)
    q, seen, target = [(0, 0, 0, 0)], set(), (n-1, n-2, 0)
    for r, c, dr, step in q:
    if (r, c, dr) == target: return step
    if (r, c, dr) not in seen:
    seen.add((r, c, dr))
    if dr:
    if c+1<n and g[r][c+1]==g[r+1][c+1]==0:
    q += [(r, c+1, 1, step+1), (r, c, 0, step+1)]
    if r+2<n and g[r+2][c]==0:
    q += [(r+1, c, 1, step+1)]
    else:
    if r+1<n and g[r+1][c]==g[r+1][c+1]==0:
    q += [(r+1, c, 0, step+1), (r, c, 1, step+1)]
    if c+2<n and g[r][c+2]==0:
    q += [(r, c+1, 0, step+1)]
    return -1

1202. Smallest String With Swaps

给定一组pairs表明索引对可以互换,求这个字符串能换的最小值时多少,同一对可以进行多次互换。
查看原题

1
2
3
4
5
Input: s = "dcab", pairs = [[0,3],[1,2]]
Output: "bacd"
Explaination:
Swap s[0] and s[3], s = "bcad"
Swap s[1] and s[2], s = "bacd"
  • union-find

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
    class UF:
    def __init__(self, n):
    self.p = list(range(n))
    def union(self, x, y):
    self.p[self.find(x)] = self.find(y)
    def find(self, x):
    if x!=self.p[x]:
    self.p[x] = self.find(self.p[x])
    return self.p[x]
    uf, res, m = UF(len(s)), [], defaultdict(list)
    for x, y in pairs:
    uf.union(x, y)
    for i in range(len(s)):
    m[uf.find(i)].append(s[i])
    for comp_id in m.keys():
    m[comp_id].sort(reverse=True)
    for i in range(len(s)):
    res.append(m[uf.find(i)].pop())
    return ''.join(res)

787. Cheapest Flights Within K Stops

经过K个站点的最便宜的航班。
查看原题

  • 狄克斯特拉算法,只不过多了一个条件,经过K个站点。不需要用seen记录已经去过的点,因为该点可能有更少步数的到达方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int:
    g = collections.defaultdict(list)
    for u, v, w in flights:
    g[u].append((v, w))

    q = [(0, src, 0)]
    heapq.heapify(q)
    while q:
    p, city, step = heapq.heappop(q)
    if city == dst:
    return p
    for v, w in g[city]:
    if step < K+1:
    heapq.heappush(q, (p+w, v, step+1))
    return -1

先进后出,后进先出

关于堆栈

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

LeetCode真题

1021. Remove Outermost Parentheses

删除最外层的括号。
查看原题

1
2
3
4
5
6
7
8
9
10
Input: "(()())(())"
Output: "()()()"
The input string is "(()())(())", with primitive decomposition "(()())" + "(())".
After removing outer parentheses of each part, this is "()()" + "()" = "()()()".
Input: "(()())(())(()(()))"
Output: "()()()()(())"
The input string is "(()())(())(()(()))", with primitive decomposition "(()())" + "(())" + "(()(()))".
After removing outer parentheses of each part, this is "()()" + "()" + "()(())" = "()()()()(())".
Input: "()()"
Output: ""
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def removeOuterParentheses(self, S: str) -> str:
    ans, opened = [], 0
    for s in S:
    if s == '(' and opened > 0:
    ans.append(s)
    if s == ')' and opened > 1:
    ans.append(s)
    opened += 1 if s=='(' else -1
    return ''.join(ans)

1047. Remove All Adjacent Duplicates In String

每两个相邻的相同字符串可以消掉。类似于连连看。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    def removeDuplicates(self, S: str) -> str:
    stack = []
    for c in S:
    if stack and stack[-1] == c:
    stack.pop()
    else:
    stack.append(c)
    return ''.join(stack)

1172. Dinner Plate Stacks

有这样一堆栈,每个栈有个容量,可以在指定索引下删除某个栈,每次push时需要在最左的不满的栈。
查看原题

1
2
3
4
5
Input: 
["DinnerPlates","push","push","push","push","push","popAtStack","push","push","popAtStack","popAtStack","pop","pop","pop","pop","pop"]
[[2],[1],[2],[3],[4],[5],[0],[20],[21],[0],[2],[],[],[],[],[]]
Output:
[null,null,null,null,null,null,2,null,null,20,21,5,4,3,1,-1]
  • 核心思想在于维护一个堆,记录不满的栈。以便插入时可以找到该索引。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class DinnerPlates:
    def __init__(self, capacity: int):
    self.c = capacity
    self.q = []
    self.emp = []

    def push(self, val: int) -> None:
    if self.emp:
    index = heapq.heappop(self.emp)
    self.q[index].append(val)
    else:
    if self.q and len(self.q[-1])!=self.c:
    self.q[-1].append(val)
    else:
    self.q.append([val])

    def pop(self) -> int:
    while self.q:
    if self.q[-1]:
    return self.q[-1].pop()
    self.q.pop()
    return -1

    def popAtStack(self, index: int) -> int:
    heapq.heappush(self.emp, index)
    if self.q[index]:
    return self.q[index].pop()
    else:
    return -1

901. Online Stock Span

实时找出数据流中连续的比不大于当前值的个数。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Input: ["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
Output: [null,1,1,1,2,1,4,6]
Explanation:
First, S = StockSpanner() is initialized. Then:
S.next(100) is called and returns 1,
S.next(80) is called and returns 1,
S.next(60) is called and returns 1,
S.next(70) is called and returns 2,
S.next(60) is called and returns 1,
S.next(75) is called and returns 4,
S.next(85) is called and returns 6.

Note that (for example) S.next(75) returned 4, because the last 4 prices
(including today's price of 75) were less than or equal to today's price.
  • <=可以累加。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class StockSpanner:

    def __init__(self):
    self.stack = []

    def next(self, price: int) -> int:
    cnt = 1
    while self.stack and self.stack[-1][0] <= price:
    cnt += self.stack.pop()[1]
    self.stack.append((price, cnt))
    return cnt

1299. Replace Elements with Greatest Element on Right Side

根据数组重新生成一个数组,每个元素对应原数组右侧最大的数字。
查看原题

1
2
Input: arr = [17,18,5,4,6,1]
Output: [18,6,6,6,1,-1]
  • 方法一:栈

    1
    2
    3
    4
    5
    6
    7
    8
    def replaceElements(self, arr: List[int]) -> List[int]:
    stack = [-1]
    for i in range(len(arr)-1, 0, -1):
    if arr[i] < stack[-1]:
    stack.append(stack[-1])
    else:
    stack.append(arr[i])
    return stack[::-1]
  • 方法二:Lee215.

    1
    2
    3
    4
    def replaceElements(self, arr: List[int], mx=-1) -> List[int]:
    for i in range(len(arr)-1, -1, -1):
    arr[i], mx = mx, max(mx, arr[i])
    return arr

1249. Minimum Remove to Make Valid Parentheses

删除字符串中多余的括号。
查看原题

1
2
3
Input: s = "lee(t(c)o)de)"
Output: "lee(t(c)o)de"
Explanation: "lee(t(co)de)" , "lee(t(c)ode)" would also be accepted.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def minRemoveToMakeValid(self, s: str) -> str:
    stack, cur = [], ''
    for c in s:
    if c == '(':
    stack.append(cur)
    cur = ''
    elif c == ')':
    if stack:
    cur = '{}({})'.format(stack.pop(), cur)
    else:
    cur += c

    while stack:
    cur = stack.pop() + cur
    return cur

1190. Reverse Substrings Between Each Pair of Parentheses

将括号内的字符串反转。
查看原题

1
2
3
Input: s = "(u(love)i)"
Output: "iloveu"
Explanation: The substring "love" is reversed first, then the whole string is reversed.
  • 和1249一样的解法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def reverseParentheses(self, s: str) -> str:
    stack, cur = [], ''
    for c in s:
    if c == '(':
    stack.append(cur)
    cur = ''
    elif c == ')':
    if stack:
    cur = '{}{}'.format(stack.pop(), cur[::-1])
    else:
    cur += c
    return cur

1209. Remove All Adjacent Duplicates in String II

将字符串中连续的k的字母全部删除,返回剩余的字符串。
查看原题

1
2
3
Input: s = "abcd", k = 2
Output: "abcd"
Explanation: There's nothing to delete.
  • 相同字符的记录个数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def removeDuplicates(self, s: str, k: int) -> str:
    stack = [['#', 0]]
    for c in s:
    if stack[-1][0] == c:
    stack[-1][1] += 1
    if stack[-1][1] == k:
    stack.pop()
    else:
    stack.append([c, 1])
    return ''.join(c*k for c, k in stack)

1475. Final Prices With a Special Discount in a Shop

找出数组中后面元素比当前元素小的差。
查看原题

1
2
3
4
5
6
7
Input: prices = [8,4,6,2,3]
Output: [4,2,4,2,3]
Explanation:
For item 0 with price[0]=8 you will receive a discount equivalent to prices[1]=4, therefore, the final price you will pay is 8 - 4 = 4.
For item 1 with price[1]=4 you will receive a discount equivalent to prices[3]=2, therefore, the final price you will pay is 4 - 2 = 2.
For item 2 with price[2]=6 you will receive a discount equivalent to prices[3]=2, therefore, the final price you will pay is 6 - 2 = 4.
For items 3 and 4 you will not receive any discount at all.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    def finalPrices(self, A: List[int]) -> List[int]:
    stack = []
    for i, a in enumerate(A):
    while stack and A[stack[-1]] >= a:
    A[stack.pop()] -= a
    stack.append(i)
    return A

739. Daily Temperature

找出比当前元素之后的大的值,计算索引差,没有则是0。
查看原题

  • 刚刚做完1475的题,一样的解法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def dailyTemperatures(self, T: List[int]) -> List[int]:
    stack = []
    ans = [0] * len(T)
    for i, t in enumerate(T):
    while stack and T[stack[-1]] < t:
    j = stack.pop()
    ans[j] = i - j
    stack.append(i)
    return ans

Math is the base.

LeetCode真题

7. Reverse Integer

倒置一个整数, 此答案忽略了原题中的范围判断。
查看原题

1
2
Input: -123
Output: -321
  • 方法一:str

    1
    2
    3
    4
    5
    def reverse_int(x):
    if x >= 0:
    return int(str(x)[::-1])
    else:
    return -int(str(x)[:0:-1])
  • 方法二:math

    1
    2
    3
    4
    5
    6
    7
    def reverse(self, x: int) -> int:
    sign = 1 if x >= 0 else -1
    ans, tail = 0, abs(x)
    while tail:
    ans = ans*10 + tail%10
    tail //= 10
    return ans * sign if ans < 2**31 else 0

9. Palindrome Number

判断一个数是否是回文数,这里把负数认为是不符合条件的。
查看原题

  • 方法一:str

    1
    2
    def is_palindrome(x):
    return str(x) == str(x)[::-1]
  • 方法二:math

    1
    2
    3
    4
    5
    6
    def is_palindrome(x):
    l, r = x, 0
    while l > 0:
    r = r*10 + l%10
    l //= 10
    return r == x

13. Roman to Integer

罗马数字转换整型。
查看原题

1
2
Input: [3,4,5,1,2] 
Output: 1
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def roman_to_int(s):
    roman = {'I': 1, 'V': 5, 'X': 10, 'L': 50,
    'C': 100, 'D': 500, 'M': 1000}
    total = 0
    for i in range(len(s)):
    if i == len(s)-1 or roman[s[i]] >= roman[s[i+1]]
    total += roman[s[i]]
    else:
    total -= roman[s[i]]
    return total

69. Sqrt(x)

实现开方,返回整数部分。
查看原题

1
2
3
4
Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since
the decimal part is truncated, 2 is returned.
  • 牛顿迭代法

    1
    2
    3
    4
    5
    def my_sqrt(x):
    r = x
    while r**2 > x:
    r = (r+x//r) // 2
    return r

367. Valid Perfect Square

判断一个数是不是某个数的平方。
查看原题

1
2
Input: 16
Output: true
  • 方法一:牛顿迭代法。同69。

    1
    2
    3
    4
    5
    def isPerfectSquare(self, num):
    r = num
    while r**2 > num:
    r = (r + num // r) // 2
    return r**2 == num

171. Excel Sheet Column Number

excel表格列表数字转换,二十六进制。
查看原题

1
2
3
4
5
6
7
A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28 A -> 1
  • 解法

    1
    2
    3
    def titleToNumber(self, s: str) -> int:
    OFFSET = ord('A')-1
    return sum((ord(x)-OFFSET)*26**i for i, x in enumerate(s[::-1]))

168. Excel Sheet Column Title

excel转换,数字转字母。十进制->26进制。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Input:
letters = ["c", "f", "j"]
target = "d"
Output: "f"

Input:
letters = ["c", "f", "j"]
target = "g"
Output: "j"

Input:
letters = ["c", "f", "j"]
target = "j"
Output: "c"
  • 解法

    1
    2
    3
    4
    5
    6
    7
    def convertToTitle(self, n):
    res = ''
    while n:
    res = chr((n-1)%26+65) + res
    # n //= 26
    n = (n-1) // 26
    return res

172. Factorial Trailing Zeroes

求n的阶乘末尾有几个0。
查看原题

1
2
3
Input: 5
Output: 1
Explanation: 5! = 120, one trailing zero.
  • 思路:每一对2和5可以产生一个0,在n的阶乘中,5比2多,所以问题变成求5的个数,而25这种数有两个5,所以递归求解

    1
    2
    def trailing_zeroes(n):
    return 0 if n == 0 else n//5 + trailing_zeroes(n//5)

204. Count Primes

求小于n的整数中,有多少个质数。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    def countPrimes(self, n):
    is_prime = [False]*2 + [True]*(n-2)
    for i in range(2, int(n ** 0.5)+1):
    if is_prime[i]:
    is_prime[i*i:n:i] = [False] * len(is_prime[i*i:n:i])
    return sum(is_prime)

50. Pow(x, n)

实现pow函数。
查看原题

1
2
3
4
5
Input: 2.00000, 10
Output: 1024.00000

Input: 2.00000, -2
Output: 0.25000 .
  • 说明:常规方法在Leetcode 上内存会爆掉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Solution(object):

    def myPow(self, x, n):
    if n < 0:
    return 1 / self.pow_with_unsigned(x, -n)
    else:
    return self.pow_with_unsigned(x, n)

    def pow_with_unsigned(self, x, n):
    if n == 1:
    return x
    if n == 0:
    return 1

    res = self.pow_with_unsigned(x, n >> 1)
    res *= res

    if n & 1 == 1:
    res *= x

    return res

233. Number of Digit One

1~n数字中1的个数。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    def countDigitOne(self, n):    
    countr, i = 0, 1
    while i <= n:
    divider = i * 10
    countr += (n // divider) * i + min(max(n % divider - i + 1, 0), i)
    i *= 10
    return countr

263. Ugly Number

判断一个数是否是丑数。
查看原题

  • 根据定义实现。< num是为了判断num=0的情况。

    1
    2
    3
    4
    5
    def isUgly(self, num):
    for f in 2, 3, 5:
    while num % f == 0 < num:
    num //= f
    return num == 1

264. Ugly Number II

输出第n个丑数。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def nthUglyNumber(self, n):
    q = [1]
    t2, t3, t5 = 0, 0, 0
    for i in range(n-1):
    a2, a3, a5 = q[t2]*2, q[t3]*3, q[t5]*5
    to_add = min(a2, a3, a5)
    q.append(to_add)
    if a2 == to_add:
    t2 += 1
    if a3 == to_add:
    t3 += 1
    if a5 == to_add:
    t5 += 1
    return q[-1]

67.Add Binary

实现二进制加法。
查看原题

1
2
Input: a = "11", b = "1"
Output: "100"
  • 方法一:按照加法的二进制思想来计算,不过Runtime大约100ms。后来试着将list comprehension拆成一个for循环,也并没有提高速度。居然beats只有4%,难道大部分人都用的bin。讨论区简单翻了了一下,没有找到一个高效的pythonic的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def addBinary(self, a, b):
    if len(a) > len(b):
    b = b.zfill(len(a))
    else:
    a = a.zfill(len(b))

    while int(b):
    sum_not_carry = ''.join([str(int(a[i]) ^ int(b[i])) for i in range(len(a))])
    carry = ''.join([str(int(a[i]) & int(b[i])) for i in range(len(a))])
    a, b = "0"+sum_not_carry, carry+'0'
    return a.lstrip('0') if a != '0' else '0'

202. Happy Number

判断是否是欢乐数。进行所有位的平方和运算,最后为1的是欢乐数。
查看原题

1
2
3
4
5
6
7
Input: 19
Output: true
Explanation:
1**2 + 9**2 = 82
8**2 + 2**2 = 68
6**2 + 8**2 = 100
1**2 + 0**2 + 0**2 = 1
  • 思路,使用一个字典映射0~9的平方值,然后如果死循环的话,各位数的和一定存在着一种循环,所以用一个set来判断是否重复。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def isHappy(self, n):
    squares = {str(k): k**2 for k in range(0, 10)}
    sum_digit = set()
    while n != 1:
    n = sum(squares[digit] for digit in str(n))
    if n in sum_digit:
    return False
    else:
    sum_digit.add(n)
    return True

231. Power of Two

判断一个数是否是2的n次方。思路也就是判断这个数的二进制形式是否只有一个’1’。
查看原题

  • 方法一:二进制统计1。

    1
    2
    def isPowerOfTwo(self, n):
    return n > 0 and bin(n).count('1') == 1
  • 方法三:如果一个数n的二进制只有一个1,那么n&(n-1)一定为0。

    1
    2
    def isPowerOfTwo(self, n):
    return n > 0 and (n&n-1) == 0

342. Power of Four

判断一个数是否是4的n次方。
查看原题

  • 方法一:从简单入手通过231题,了解到了2的n次方特点是,二进制形式只有一个’1’,那么4的n次方就是不但只有一个’1’,后面还跟了偶数个’0’。

    1
    2
    3
    def isPowerOfFour(self, num):
    # return num > 0 and (num & num-1)==0 and bin(num)[2:].count('0')&1==0
    return num > 0 and (num & num-1)==0 and len(bin(num))&1==1
  • 方法三:也可以使用正则。

    1
    2
    3
    def isPowerOfFour(self, num):
    import re
    return bool(re.match(r'^0b1(00)*$',bin(num)))

292. Nim Game

说,有这么一堆石头,一次只能拿1~3个,拿到最后一个石头的人获胜。求n堆石头,你先拿是否可以获胜。
查看原题

  • 思路:找规律,发现只有最后剩4个石头的时候,此时轮到谁,谁输。

    1
    2
    def canWinNim(self, n):
    return n % 4 != 0

400. Nth Digit

找出无限整数序列中的第n个数字。
查看原题

1
2
3
4
5
6
Input:
11
Output:
0
Explanation:
The 11th digit of the sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... is a 0, which is part of the number 10.
  • 思路,根据n的位数,将无限序列分为几个范围。

    1. 寻找范围。寻找n处于哪个范围,是19,还是1099,例如n=15。则需要跳过19的范围,而这个范围有size*step个数字,所以问题变成在1099范围上寻找第15-1*9=6个数。

    2. 定位数字。10~99范围中是从10开始的,每一个数都有两位数字,所以最终数字为10+(6-1)//2,因为索引从0开始,所以需要-1。

    3. 定位数字的位。上一步找到了数字为12,对size求余就可以知道,’12’[(6-1)%2]=’2’。

      1
      2
      3
      4
      5
      def findNthDigit(self, n):
      start, step, size = 1, 9, 1
      while n > size * step:
      n, start, step, size = n-size*step, start*10, step*10, size+1
      return int(str(start + (n-1)//size)[(n-1) % size])

415. Add Stings

给定两个字符串表示的数字,把它们相加,这两个数的长度小于5100,不能使用任何BitIntegr库或是直接将其转换为整数。ps: 题中要求不将输入直接转换成int,所以我个人认为int还是可以使用的,有一些答案中是使用了ord来做运算。
查看原题

  • 使用zip_longest。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def addStrings(self, num1, num2):
    from itertools import zip_longest
    nums = list(zip_longest(num1[::-1], num2[::-1], fillvalue='0'))
    carry, res = 0, ''
    for digits in nums:
    d1, d2 = map(int, digits)
    carry, val = divmod(d1+d2+carry, 10)
    res = res + str(val)
    res = res if carry==0 else res+str(carry)
    return res[::-1]

492. Construct the Rectangle

给定一个面积,求组成这个面积的长高差最小。
查看原题

1
2
3
4
Input: 4
Output: [2, 2]
Explanation: The target area is 4, and all the possible ways to construct it are [1,4], [2,2], [4,1].
But according to requirement 2, [1,4] is illegal; according to requirement 3, [4,1] is not optimal compared to [2,2]. So the length L is 2, and the width W is 2.
  • 解法

    1
    2
    3
    4
    5
    6
    def constructRectangle(self, area):
    import math
    w = int(math.sqrt(area))
    while area % w != 0:
    w -= 1
    return [area//w, w]

504. Base 7

10进制转7进制。
查看原题

1
2
3
4
Input: 100
Output: "202"
Input: -7
Output: "-10"
  • 需要注意负数。

    1
    2
    3
    4
    5
    6
    7
    def convertToBase7(self, num: int) -> str:
    if num == 0: return '0'
    n, ans = abs(num), ''
    while n:
    n, val = divmod(n, 7)
    ans = str(val) + ans
    return ans if num > 0 else '-'+ans

970. Powerful Integers

求满足x^i+y^j <= bound的所有和。
查看原题

1
2
3
4
5
6
7
8
9
10
Input: x = 2, y = 3, bound = 10
Output: [2,3,4,5,7,9,10]
Explanation:
2 = 2^0 + 3^0
3 = 2^1 + 3^0
4 = 2^0 + 3^1
5 = 2^1 + 3^1
7 = 2^2 + 3^1
9 = 2^3 + 3^0
10 = 2^0 + 3^2
  • 这题难得地方在于两个循环的临界值,貌似我这样写也不是最优解,原题的Solution中给定了2**18>bound的最大值。所以两个范围都是18。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def powerfulIntegers(self, x, y, bound):
    res = set()
    imax = self.get_max(x, bound) + 1
    jmax = self.get_max(y, bound) + 1
    for i in range(imax):
    for j in range(jmax):
    if x**i + y**j <= bound:
    res.add(x**i+y**j)
    return list(res)

    def get_max(self, n, bound):
    for i in range(bound//n + 1):
    if n ** i >= bound:
    return i
    return bound//n + 1

973. K Closest Points to Origin

求离原点最近的K个坐标点。
查看原题

1
2
3
4
5
6
7
Input: points = [[1,3],[-2,2]], K = 1
Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].
  • easy

    1
    2
    3
    def kClosest(self, points, K):
    res = sorted(points, key=lambda x: x[0]**2 + x[1]**2)
    return res[:K]

976. Largest Perimeter Triangle

给定一个边长数组,求能组成的三角形的最长周长。
查看原题

  • 就是长度为3的滑动窗口。

    1
    2
    3
    4
    5
    6
    def largestPerimeter(self, A):
    res = sorted(A, reverse=True)
    for i in range(len(res)-2):
    if sum(res[i+1:i+3]) > res[i]:
    return sum(res[i:i+3])
    return 0

628. Maximum Product of Three Numbers

数组中三个数的最大乘积。元素范围[-1000, 1000]。
查看原题

1
2
Input: [1,2,3,4]
Output: 24
  • 方法一:排序。在正数个数大于等于3的时候,显然最大的三个数就可以产生最大的乘积。而当正数个数不够的时候,那么必须需要两个最小的负数(即绝对值最大),和一个最大的正数。

    1
    2
    3
    def maximumProduct(self, nums):
    ary = sorted(nums)
    return max((ary[0]*ary[1]*ary[-1], ary[-3]*ary[-2]*ary[-1]))
  • 方法二:使用heapq.

    1
    2
    3
    4
    5
    6
    7
    def maximumProduct(self, nums):
    import heapq
    from operator import mul
    from functools import reduce
    three_max = heapq.nlargest(3, nums)
    two_min = heapq.nsmallest(2, nums)
    return max(reduce(mul, three_max), reduce(mul, two_min + three_max[:1]))

728. Self Dividing Numbers

自整除数字,一个数字能够被本身的每个数字整除,并且不能有0,求某个范围内所有的数。
查看原题

1
2
3
Input: 
left = 1, right = 22
Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]
  • Brute Force. 此题强行使用列表生成式没有意义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def selfDividingNumbers(self, left, right):
    res = []
    for i in range(left, right+1):
    for char in str(i):
    if int(char)==0 or i % int(char)!=0:
    break
    else:
    res.append(i)
    return res

836. Rectangle Overlap

矩形是否重叠,矩形的边平行于坐标轴。
查看原题

1
2
Input: rec1 = [0,0,2,2], rec2 = [1,1,3,3]
Output: true
  • 解法

    1
    2
    3
    def isRectangleOverlap(self, rec1: 'List[int]', rec2: 'List[int]') -> 'bool':
    return rec2[0] < rec1[2] and rec1[0] < rec2[2] and \
    rec2[1] < rec1[3] and rec1[1] < rec2[3]

991. Broken Calculator

坏掉的计算器,只能*2或者-1,使X变为Y。
查看原题

1
2
3
Input: X = 5, Y = 8
Output: 2
Explanation: Use decrement and then double {5 -> 4 -> 8}.
  • 如果从X到Y问题会变得复杂,不确定什么时候该*2或者是-1。所以逆向思维从Y变成X。因为如果Y是奇数,那么必定在+1操作后要/2,这里将其合并。

    1
    2
    def brokenCalc(self, X: 'int', Y: 'int') -> 'int':
    return X - Y if X >= Y else 1+(Y&1)+self.brokenCalc(X, (Y+1)//2)

908. Smallest Range I

给定一个数组,和一个K,数组里的数加上-k<=x<=k的任意一个数字后,求数组最大数和最小数的,最小差。
查看原题

1
2
3
Input: A = [0,10], K = 2
Output: 6
Explanation: B = [2,8]
  • 解法

    1
    2
    def smallestRangeI(self, A: 'List[int]', K: 'int') -> 'int':
    return max(max(A) - min(A) - 2*K, 0)

949. Largest Time for Given Digits

给定四个数字,返回能生成的最大时间。24小时制。
查看原题

1
2
Input: [1,2,3,4]
Output: "23:41"
  • 解法

    1
    2
    3
    4
    def largestTimeFromDigits(self, A: 'List[int]') -> 'str':
    p = itertools.permutations(A)
    return max(['{}{}:{}{}'.format(*d) for d in p
    if d[:2] < (2, 4) and d[2] < 6] or [''])

914. X of a Kind in a Deck of Cards

有这样一堆数字卡牌,问是否存在一个X>=2,使得将同样数字的卡牌分为每X个一组,并且刚好所有的卡牌分完。
查看原题

  • 使用Counter来统计每个数字的个数,然后求这些数字的最大公约数是否大于等于2,这里思路卡了一下,因为没想到最大公约数可以通过reduce来计算,没考虑到是可以累积的。

    1
    2
    3
    4
    5
    def hasGroupsSizeX(self, deck):
    from collections import Counter
    from math import gcd
    from functools import reduce
    return reduce(gcd, Counter(deck).values()) >= 2

470. Implement Rand10() Using Rand7()

使用rand7实现rand10
查看原题

1
2
Input: 3
Output: [8,1,10]
  • 解法

    1
    2
    3
    4
    5
    def rand10(self):
    while True:
    x = (rand7()-1)*7 + rand7()-1
    if x < 40:
    return x%10 + 1

1006. Clumsy Factorial

将一个阶乘的式子用*/+-替代,给出结果。
查看原题

1
2
3
Input: 10
Output: 12
Explanation: 12 = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1
  • 解法

    1
    2
    3
    4
    def clumsy(self, N: int) -> int:
    op = itertools.cycle(['*', '//', '+', '-'])
    return eval(''.join(str(n)+next(op) if n!=1 else str(n)
    for n in range(N, 0, -1)))

1022. Smallest Integer Divisible by K

最小的由1组成的能被K整除。
查看原题

1
2
3
Input: 2
Output: -1
Explanation: There is no such positive integer N divisible by 2.
  • 如果有2或5的质因数,那么不能整除。

    1
    2
    3
    4
    5
    6
    def smallestRepunitDivByK(self, K: int) -> int:
    if K % 2 == 0 or K % 5 == 0: return -1
    r = 0
    for N in range(1, K + 1):
    r = (r * 10 + 1) % K
    if not r: return N

1028. Convert to Base -2

10进制转成-2进制。
查看原题

  • 在二进制上加一个负号。

    1
    2
    3
    4
    5
    6
    def baseNeg2(self, N: int) -> str:
    ans = []
    while N:
    ans.append(N & 1)
    N = -(N >> 1)
    return ''.join(map(str, ans[::-1] or [0]))

313. Super Ugly Number

根据指定的质数序列,找出第n个超级丑数。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def nthSuperUglyNumber(self, n: int, primes: List[int]) -> int:
    import heapq as hq
    uglies = [1]

    def gen_ugly(prime):
    for ugly in uglies:
    yield ugly * prime

    merged = hq.merge(*map(gen_ugly, primes))
    while len(uglies) < n:
    ugly = next(merged)
    if ugly != uglies[-1]:
    uglies.append(ugly)
    return uglies[-1]

869. Reordered Power of 2

重新排列一个数字的各位数,判断是否能组成2的幂。
查看原题

  • 2的幂是指数上升的,所以,在范围内的数一共也没有几个。那么使用Counter来判断是否能组成这个数。

    1
    2
    3
    def reorderedPowerOf2(self, N: int) -> bool:
    c = Counter(str(N))
    return any(c==Counter(str(1<<i)) for i in range(0, 30))

1025. Divisor Game

两个人做游戏,黑板上有个数N,每次找到一个0 <x<N的数,并且N能被x整除,然后替换这个N,直到找不出这样x,就输了。问给出这样一个数N,第一个人是否能赢。
查看原题

  • 只要N为偶数就能赢

    1
    2
    def divisorGame(self, N: int) -> bool:
    return N & 1 == 0

1037. Valid Boomerang

验证三个坐标点是否共线。
查看原题

  • 需要注意的是,除数为0 的情况,所以这里改成了乘法。

    1
    2
    3
    def isBoomerang(self, points: List[List[int]]) -> bool:
    return (points[1][1]-points[0][1])*(points[2][0]-points[1][0]) != \
    (points[2][1]-points[1][1])*(points[1][0]-points[0][0])

1041. Robot Bounded In Circle

一个面向北的机器人进行三种操作,一种是前进,或者向左向右转。问一系列的操作中,无限循环时,机器人是否在绕圈。
查看原题

  • 在一次之后,如果面向的不再是北,那么最后将会绕圈。

    1
    2
    3
    4
    5
    6
    7
    def isRobotBounded(self, instructions: str) -> bool:
    x, y, dx, dy = 0, 0, 0, 1
    for inst in instructions:
    if inst == 'G': x, y = x+dx, y+dy
    elif inst == 'L': dx, dy = -dy, dx
    elif inst == 'R': dx, dy = dy, -dx
    return (x == y == 0) or (dx, dy) != (0, 1)

1137. N-th Tribonacci Number

三个数的斐波那契数列。
查看原题

1
2
3
4
5
Input: n = 4
Output: 4
Explanation:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
  • 解法

    1
    2
    3
    4
    5
    def tribonacci(self, n: int) -> int:
    a, b, c = 1, 0, 0
    for _ in range(n):
    a, b, c = b, c, a+b+c
    return c

1073. Adding Two Negabinary Numbers

两个-2进制的数相加。
查看原题

  • 转成十进制相加,再转回-2进制。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]:

    def to_ten(arr):
    return sum(d*(-2)**i for i, d in enumerate(arr[::-1]))

    def to_neg_binary(n):
    if not n:
    return '0'
    ans = ''
    while n:
    remainder = n % (-2)
    ans += str(abs(remainder))
    n //= -2
    n += (remainder < 0)
    return ans[::-1]

    return to_neg_binary(to_ten(arr1) + to_ten(arr2))

1154. Day of the Year

根据输入的日期,返回它是一年中的第几天。
查看原题

  • 使用了datetime库,开始还自己手动减

    1
    2
    3
    4
    def dayOfYear(self, date: str) -> int:
    import datetime
    date = datetime.datetime.strptime(date, '%Y-%m-%d')
    return date.timetuple().tm_yday

1155. Number of Dice Rolls With Target Sum

扔一个f面的 骰子d次,结果为target的次数。
查看原题

1
2
3
4
5
Input: d = 2, f = 6, target = 7
Output: 6
Explanation:
You throw two dice, each with 6 faces. There are 6 ways to get a sum of 7:
1+6, 2+5, 3+4, 4+3, 5+2, 6+1.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def numRollsToTarget(self, d: int, f: int, target: int) -> int:
    last_p = collections.defaultdict(int)
    last_p.update({d: 1 for d in range(1, f+1)})
    for i in range(2, d+1):
    new_p = collections.defaultdict(int)
    for j in range(i, i*f+1):
    new_p[j] = sum(last_p[j-k] for k in range(1, f+1))
    last_p = new_p
    return last_p[target] % (10**9+7)

1093. Statistics from a Large Sample

统计大量的样本数据,求最小值,最大值,平均值,众数。
查看原题

1
2
Input: count = [0,1,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Output: [1.00000,3.00000,2.37500,2.50000,3.00000]
  • 中位数的求法这里没想到,使用二分可以完美的解决奇偶问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def sampleStats(self, count: List[int]) -> List[float]:
    n = sum(count)
    mi = next(i for i in range(255) if count[i]) * 1.0
    ma = next(i for i in range(255, -1, -1) if count[i]) * 1.0
    mean = sum(i * val for i, val in enumerate(count)) * 1.0 / n
    mode = count.index(max(count)) * 1.0
    cc = list(itertools.accumulate(count))
    left = bisect.bisect(cc, (n-1)//2)
    right = bisect.bisect(cc, n//2)
    median = (left + right) / 2.0
    return mi, ma, mean, median, mode

1103. Distribute Candies to People

发糖果,按照顺序每个人比上一人多一颗,发到最后再循环。
查看原题

1
2
3
4
5
6
7
Input: candies = 7, num_people = 4
Output: [1,2,3,1]
Explanation:
On the first turn, ans[0] += 1, and the array is [1,0,0,0].
On the second turn, ans[1] += 2, and the array is [1,2,0,0].
On the third turn, ans[2] += 3, and the array is [1,2,3,0].
On the fourth turn, ans[3] += 1 (because there is only one candy left), and the final array is [1,2,3,1].
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    def distributeCandies(self, candies: int, n: int) -> List[int]:
    ans = [0] * n
    cur = 1
    while candies > 0:
    ans[cur%n-1] += min(candies, cur)
    candies -= cur
    cur += 1
    return ans

1109. Corporate Flight Bookings

通过给定的一些区间,确定每天的座位数。
查看原题

1
2
Input: bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
Output: [10,55,45,25,25]
  • 记录变化的状态,然后累加求结果。

    1
    2
    3
    4
    5
    6
    def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
    ans = [0] * (n+1)
    for s, e, v in bookings:
    ans[s-1] += v
    ans[e] -= v
    return list(itertools.accumulate(ans))[:-1]

1175. Prime Arrangements

质数排列。
查看原题

1
2
3
Input: n = 5
Output: 12
Explanation: For example [1,2,5,4,3] is a valid permutation, but [5,2,3,4,1] is not because the prime number 5 is at index 1.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def numPrimeArrangements(self, n: int) -> int:

    def countPrimes(n):
    is_prime = [False]*2 + [True]*(n-2)
    for i in range(2, int(n ** 0.5)+1):
    if is_prime[i]:
    is_prime[i*i:n:i] = [False] * len(is_prime[i*i:n:i])
    return sum(is_prime)
    c = countPrimes(n+1)
    ans = math.factorial(c) * math.factorial(n-c)

    return ans % (10**9+7)

1360. Number of Days Between Two Dates

计算两个日期之间的天数。
查看原题

1
2
Input: date1 = "2020-01-15", date2 = "2019-12-31"
Output: 15
  • 方法一:简单的datetime模块方式。

    1
    2
    3
    4
    5
    def daysBetweenDates(self, date1: str, date2: str) -> int:
    from datetime import datetime
    d1 = datetime.strptime(date1, '%Y-%m-%d')
    d2 = datetime.strptime(date2, '%Y-%m-%d')
    return abs((d2-d1).days)
  • 方法二:有个公式,如果将1月二月看成是13月和14月,那么月份转化天数有个公式(153 * m + 8) // 5

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def daysBetweenDates(self, date1: str, date2: str) -> int:
    def f(date):
    y, m, d = map(int, date.split('-'))
    if m < 3:
    m += 12
    y -= 1
    return 365 * y + y // 4 + y // 400 - y // 100 + d + (153 * m + 8) // 5

    return abs(f(date1) - f(date2))

1363. Largest Multiple of Three

组成的最大的3的倍数。
查看原题

1
2
Input: digits = [8,1,9]
Output: "981"
  • 方法一:简单的datetime模块方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def largestMultipleOfThree(self, A):
    total = sum(A)
    count = collections.Counter(A)
    A.sort(reverse=1)

    def f(i):
    if count[i]:
    A.remove(i)
    count[i] -= 1
    if not A: return ''
    if not any(A): return '0'
    if sum(A) % 3 == 0: return ''.join(map(str, A))

    if total % 3 == 0:
    return f(-1)
    if total % 3 == 1 and count[1] + count[4] + count[7]:
    return f(1) or f(4) or f(7)
    if total % 3 == 2 and count[2] + count[5] + count[8]:
    return f(2) or f(5) or f(8)
    if total % 3 == 2:
    return f(1) or f(1) or f(4) or f(4) or f(7) or f(7)
    return f(2) or f(2) or f(5) or f(5) or f(8) or f(8)

这才是计算机运算的本质

关于位运算

位运算就是直接对整数在内存中的二进制位进行操作。

LeetCode真题

191. Number of 1 Bits

计算数字的二进制中有多少个1。
查看原题

1
2
3
Input: 11
Output: 3
Explanation: Integer 11 has binary representation 00000000000000000000000000001011
  • 方法一:常规解法,使用1与n作与运算,如果不是0说明,含有一个1。

    1
    2
    3
    4
    5
    6
    7
    def hamming_weight(n):
    bits, mask = 0, 1
    for _ in range(32):
    if n&mask != 0:
    bits += 1
    mask <<= 1
    return bits
  • 方法二:关键点是,一个数n和n-1的与运算操作,相当于去掉了最右面的1。

    1
    2
    3
    4
    5
    6
    def hamming_weigth(n):
    bits = 0
    while n:
    bits += 1
    n = (n-1) & n
    return bits

136. Single Number

找出数组中不重复的元素。其它元素出现两次。
查看原题

1
2
Input: [4,1,2,1,2]
Output: 4
  • 1
    2
    def single_num(nums):
    return reduce(lambda x, y: x ^ y, nums)

137. Single Number II

找出数组中出现一次的元素,其它元素出现三次。
查看原题

1
2
Input: [2,2,3,2]
Output: 3
  • 方法一:找出单独元素每一位的值。如果把所有数字的二进制每一位加起来,如果某一位可以被3整除,则表示单独元素的该位为0,否则为1。以下使用count来表示每一位1的个数。假设count%3!=0为True,说明该元素i位为1,然后是用|=更新ans在第i个位置的值,这里也可以使用+=,但是效率稍慢。convert的作用是因为python中的int是个对象,且没有最大限制,不是在第32位使用1来表示负数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def singleNumber(self, nums, n=3):
    ans = 0
    for i in range(32):
    count = 0
    for num in nums:
    if ((num >> i) & 1):
    count += 1
    ans |= ((count%n!=0) << i)
    return self.convert(ans)

    def convert(self, x):
    if x >= 2**31:
    x -= 2**32
    return x
  • 方法2:状态机解法

    1
    2
    3
    4
    5
    6
    def singleNumber(self, nums):
    ones, twos = 0, 0;
    for i in range(len(nums)):
    ones = (ones ^ nums[i]) & ~twos
    twos = (twos ^ nums[i]) & ~ones
    return ones

260. Single Number III

找出数组中两个唯一出现一次的元素,其余元素均出现两次。
查看原题

1
2
Input:  [1,2,1,3,2,5]
Output: [3,5]
  • 思想:将这两个元素分到两个组,由于这两个数不相等,所以亦或结果不为0,也就是说二进制中至少有一位1,记为第n位。我们以第n位是否为1,把数组分为两个子数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def singleNumber(self, nums):
    total_xor = self.get_xor(nums)
    mask = 1
    while total_xor&mask == 0:
    mask <<= 1
    p1 = [num for num in nums if num&mask==0]
    p2 = [num for num in nums if num&mask!=0]
    return [self.get_xor(p1), self.get_xor(p2)]

    def get_xor(self, nums):
    from functools import reduce
    return reduce(lambda x, y: x ^ y, nums)

371. Sum of Two Integers

不用加减乘除做加法。
查看原题

  • 实际上加法分为三个步骤

    相加但不进位,1^0=1,1^1=0,0^0=0,所以第一步用异或。
    只求进位的结果,只有两个1才会进位,所以用&,然后左移1位,表示要进的位。
    把前两步的结果再重复1,2步,直到没有进位产生,即b=0。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def getSum(self, a, b):
    # 32 bits integer max
    MAX = 0x7FFFFFFF # 2**31-1
    # 32 bits interger min
    MIN = 0x80000000 # -2**31
    # mask to get last 32 bits
    mask = 0xFFFFFFFF # 2*32-1
    while b != 0:
    # ^ get different bits and & gets double 1s, << moves carry
    a, b = (a ^ b) & mask, ((a & b) << 1) & mask
    # if a is negative, get a's 32 bits complement positive first
    # then get 32-bit positive's Python complement negative
    return a if a <= MAX else ~(a ^ mask)

190. Reverse Bits

返回一个数的二进制的倒序的十进制。
查看原题

1
2
3
4
Input: 43261596
Output: 964176192
Explanation: 43261596 represented in binary as 00000010100101000001111010011100,
return 964176192 represented in binary as 00111001011110000010100101000000.
  • 方法一:使用原生库。ljust表示在右侧补’0’。或者使用format来补0。

    1
    2
    3
    def reverseBits(self, n):
    return int(bin(n)[:1:-1].ljust(32, '0'), 2)
    # return int('{:0<32s}'.format(bin(n)[:1:-1]), 2)
  • 方法二:自己实现进制转换,使用位运算优化。

    1
    2
    3
    4
    5
    6
    def reverseBits(self, n):
    code = 0
    for _ in range(32):
    code = (code<<1) + (n&1)
    n >>= 1
    return code

389. Find the Difference

s和t两个由小写字母组成的字符串,t是由s打乱顺序并再随机添加一个小写字母组成。
查看原题

  • 方法一:使用Collection。

    1
    2
    3
    def findTheDifference(self, s, t):
    from collections import Counter
    return next((Counter(t) - Counter(s)).elements())
  • 方法二:使用异或。

    1
    2
    3
    4
    def findTheDifference(self, s, t):
    from operator import xor
    from functools import reduce
    return chr(reduce(xor, map(ord, s+t)))

401. Binary Watch

有这样一个二进制的手表,输入一个n,表示有几个亮着的灯,返回所有可能出现的时间。时间范围为12小时制,即hours(0-11),minutes(0-59)。
查看原题

1
2
Input: n = 1
Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]
  • 遍历所有可能的时间,找到符合条件的。因为表中的数组都是二进制,所以’1’的个数就是亮灯的个数。

    1
    2
    3
    4
    def readBinaryWatch(self, num):
    return ['{:d}:{:0>2d}'.format(h, m)
    for h in range(12) for m in range(60)
    if (bin(h)+bin(m)).count('1') == num]

405. Convert a Number to Hexadecimal

把一个32位有符号的整数转换成16进制。
查看原题

1
2
3
4
5
6
7
8
9
Input:
26
Output:
"1a"

Input:
-1
Output:
"ffffffff"
  • 解法

    1
    2
    3
    def toHex(self, num):
    return ''.join(['0123456789abcdef'[(num >> 4 * i) & 15]
    for i in range(8)])[::-1].lstrip('0') or '0'

461. Hamming Distance

求两个正数的原码中不同位的个数。
查看原题

1
2
3
4
5
6
7
Input: x = 1, y = 4
Output: 2
Explanation:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
The above arrows point to positions where the corresponding bits are different.
  • 解法

    1
    2
    def hammingDistance(self, x, y):
    return bin(x ^ y).count('1')

476. Number Complement

给定一个正数,求其原码的按位取反后的数。
查看原题

1
2
3
Input: 5
Output: 2
Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2.
  • 方法一:其实就是求101和111的异或。所以先找到111。

    1
    2
    3
    4
    5
    def findComplement(self, num):
    i = 1
    while i <= num:
    i <<= 1
    return (i-1) ^ num
  • 方法二:更少的位移。核心思想还是找到111。比如一个8位数,最高代表符号:1000000,先将其右移1位,使得左边两位都变成1。然后再右移2位,使得左边四位变成1,以此类推,8位数最多移动3次就可以得到1111111,32位则还需要再移动2次。

    1
    2
    3
    4
    5
    def findComplement(self, num):
    mask = num
    for i in range(5):
    mask |= mask >> (2**i)
    return num ^ mask

693. Binary Number with Alternating Bits

二进制是否是交替的0和1。
查看原题

1
2
3
4
Input: 5
Output: True
Explanation:
The binary representation of 5 is: 101
  • 方法一:除2法。

    1
    2
    3
    4
    5
    6
    7
    def hasAlternatingBits(self, n):
    n, cur = divmod(n, 2)
    while n:
    if cur == n % 2:
    return False
    n, cur = divmod(n, 2)
    return True
  • 方法二:异或。

    1
    2
    3
    4
    5
    def hasAlternatingBits(self, n):
    if not n:
    return False
    num = n ^ (n >> 1)
    return not (num & num+1)

762. Prime Number of Set Bits in Binary Representation

求某范围的所有自然数中,二进制中1的个数是质数的个数。
查看原题

1
2
3
4
5
6
7
8
9
Input: L = 10, R = 15
Output: 5
Explanation:
10 -> 1010 (2 set bits, 2 is prime)
11 -> 1011 (3 set bits, 3 is prime)
12 -> 1100 (2 set bits, 2 is prime)
13 -> 1101 (3 set bits, 3 is prime)
14 -> 1110 (3 set bits, 3 is prime)
15 -> 1111 (4 set bits, 4 is not prime)
  • 方法一:direct.

    1
    2
    3
    4
    5
    6
    7
    8
    def countPrimeSetBits(self, L: 'int', R: 'int') -> 'int':
    primes = {2, 3, 5, 7, 11, 13, 17, 19}
    # ans = 0
    # for num in range(L, R+1):
    # if bin(num)[2:].count('1') in primes:
    # ans += 1
    # return ans
    return sum(bin(n)[2:].count('1') in primes for n in range(L, R+1))
  • 方法二:位运算。p 的2,3,5,7。。位是1,其余是0,这样在右移后,可&1就可以判断这个数是否是质数。

    1
    2
    3
    def countPrimeSetBits(self, L: 'int', R: 'int') -> 'int':
    p = int('10100010100010101100', 2)
    return sum(p >> bin(i).count('1') & 1 for i in range(L, R+1))

868. Binary Gap

二进制两个1的最大距离。
查看原题

1
2
3
4
5
6
7
8
Input: 22
Output: 2
Explanation:
22 in binary is 0b10110.
In the binary representation of 22, there are three ones, and two consecutive pairs of 1's.
The first consecutive pair of 1's have distance 2.
The second consecutive pair of 1's have distance 1.
The answer is the largest of these two distances, which is 2.
  • 列表生成式。

    1
    2
    3
    4
    def binaryGap(self, N: 'int') -> 'int':
    one = [i for i, v in enumerate(bin(N)) if v == '1']
    # return max([one[i+1] - one[i] for i in range(len(one)-1)] or [0])
    return max([b-a for a, b in zip(one, one[1:])] or [0])

268. Missing Number

0~n中缺失的数字。
查看原题

  • 方法一:数学公式。

    1
    2
    3
    4
    5
    def missingNumber(self, nums):
    n = len(nums)
    expected_sum = n*(n+1) // 2
    actual_sum = sum(nums)
    return expected_sum - actual_sum
  • 方法二:XOR.

    1
    2
    3
    4
    5
    def missingNumber(self, nums: 'List[int]') -> 'int':
    missing = len(nums)
    for i, num in enumerate(nums):
    missing ^= i ^ num
    return missing

1012. Complement of Base 10 Integer

非负数的反码。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    def bitwiseComplement(self, N: int) -> int:
    mask = 1
    while mask < N:
    mask = (mask << 1) + 1
    # return mask - N
    return N ^ mask

1404. Number of Steps to Reduce a Number in Binary Representation to One

几下操作可以将其变为1。偶数除以2,奇数+1.
查看原题

1
2
3
4
5
6
7
8
9
Input: s = "1101"
Output: 6
Explanation: "1101" corressponds to number 13 in their decimal representation.
Step 1) 13 is odd, add 1 and obtain 14.
Step 2) 14 is even, divide by 2 and obtain 7.
Step 3) 7 is odd, add 1 and obtain 8.
Step 4) 8 is even, divide by 2 and obtain 4.
Step 5) 4 is even, divide by 2 and obtain 2.
Step 6) 2 is even, divide by 2 and obtain 1.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    def numSteps(self, s: str) -> int:
    i, mid_0 = 0, 0
    for j in range(1, len(s)):
    if s[j] == '1':
    mid_0 += j - i - 1
    i = j
    if i == 0: return len(s) - 1
    return mid_0 + 1 + len(s)

201. Bitwise AND of Numbers Range

范围内的数字求与运算和。
查看原题

1
2
Input: [5,7]
Output: 4
  • 解法

    1
    2
    3
    4
    5
    6
    7
    def rangeBitwiseAnd(self, m: int, n: int) -> int:
    i = 0
    while m != n:
    m >>= 1
    n >>= 1
    i += 1
    return n << i

1442. Count Triplets That Can Form Two Arrays of Equal XOR

数组中找出两段的异或和相等。
查看原题

  • 找规律

    1
    2
    3
    4
    5
    6
    7
    8
    def countTriplets(self, arr: List[int]) -> int:
    m = list(itertools.accumulate([0] + arr, operator.xor))
    count = 0
    for i in range(len(m)):
    for j in range(i+1, len(m)):
    if m[i] == m[j]:
    count += j-i-1
    return count

1238. Circular Permutation in Binary Representation

返回指定为位数的二进制环,每两个数的二进制只有1位不同。
查看原题

1
2
3
4
Input: n = 2, start = 3
Output: [3,2,0,1]
Explanation: The binary representation of the permutation is (11,10,00,01).
All the adjacent element differ by one bit. Another valid permutation is [3,1,0,2]
  • 我想了半天这道题,以为和二进制无关,是个数学题,没想到最后还得用异或来解决。这是个 gray code的问题,有一个公式。

    1
    2
    def circularPermutation(self, n, start):
    return [start ^ i ^ i >> 1 for i in range(1 << n)]

用时间换取空间

关于链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

LeetCode真题

2. Add Two Numbers

两个链表相加。
查看原题

1
2
3
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
  • 注意边界处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def addTwoNumbers(l1, l2):
    l = head = ListNode(0)
    carry = 0
    while l1 or l2 or carry:
    v1 = v2 = 0
    if l1:
    v1 = l1.val
    l1 = l1.next
    if l2:
    v2 = l2.val
    l2 = l2.next
    carry, val = divmod(v1+v2+carry, 10)
    l.next = ListNode(val)
    l = l.next
    return head.next

445. Add Two Numbers II

跟上题类似,只不过是进位方式不同。
查看原题

1
2
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
  • 方法一:先reverse再相加,最后再reverse。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:

    def reverse(head):
    prev = None
    while head:
    head.next, prev, head = prev, head, head.next
    return prev

    ans = head = ListNode(0)
    l1, l2 = reverse(l1), reverse(l2)
    carry = 0
    while l1 or l2 or carry:
    v1 = v2 = 0
    if l1:
    v1 = l1.val
    l1 = l1.next
    if l2:
    v2 = l2.val
    l2 = l2.next
    carry, val = divmod(v1+v2+carry, 10)
    head.next = ListNode(val)
    head = head.next
    return reverse(ans.next)
  • 方法二:由于Python int没有限制,所以可以遍历相加,再从尾到头还原节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
    v1 = v2 = 0
    while l1:
    v1 = v1*10 + l1.val
    l1 = l1.next
    while l2:
    v2 = v2*10 + l2.val
    l2 = l2.next
    val = v1 + v2
    tail, head = None, None
    while val > 0:
    head = ListNode(val % 10)
    head.next = tail
    tail = head
    val //= 10
    return head if head else ListNode(0)

21. Merge Two Sorted Lists

合并两个有序链表。
查看原题

1
2
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
  • 方法1:iteratively 迭代

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def mergeTwoLists(l1, l2):
    l = head = ListNode(0)
    while l1 and l2:
    if l1.val <= l2.val:
    l.next, l1 = l1, l1.next
    else:
    l.next, l2 = l2, l2.next
    l = l.next
    l.next = l1 or l2
    return head.next
  • 方法2:recursively 递归

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def mergeTwoLists(l1, l2):
    # 判断是否存在None
    if not l1 or not l2:
    return l1 or l2
    if l1.val < l2.val:
    l1.next = mergeTwoLists(l1.next, l2)
    return l1
    else:
    l2.next = mergeTwoLists(l1, l2.next)
    return l2

23. Merge k Sorted Lists

合并k个有序列表。
查看原题

1
2
3
4
5
6
7
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
  • 方法一:Brute Force. 时间效率O(NlogN)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def searchRange(self, nums, target):

    left_idx = self.search_edge(nums, target, True)
    if left_idx == len(nums) or nums[left_idx] != target:
    return [-1, -1]
    return [left_idx, self.search_edge(nums, target, False)-1]

    def search_edge(self, nums, target, left):
    l, r = 0, len(nums)
    while l < r:
    mid = (l+r) // 2
    if nums[mid] > target or (left and nums[mid]==target):
    r = mid
    else:
    l = mid + 1
    return l
  • 方法二:优先级队列。本来优先级就没有方法一快,再加上Python3中的比较符机制不同,导致要实现lt方法,就更慢了。不过理论时间复杂度是比方法一小的。Time: O(Nlogk)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class CmpNode:

    def __init__(self, node):
    self.node = node

    def __lt__(self, other):
    return self.node.val < other.node.val

    class Solution:

    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
    from queue import PriorityQueue
    head = h = ListNode(0)
    q = PriorityQueue()
    for l in lists:
    if l:
    q.put(CmpNode(l))
    while not q.empty():
    to_add = q.get().node
    h.next = to_add
    h = h.next
    if to_add.next:
    q.put(CmpNode(to_add.next))
    return head.next
  • 方法三:规避ListNode的比较,以解决上述问题。只要加上该链表在原数组中的索引位置,就一定不会重复,从而忽略对ListNode的比较。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
    from queue import PriorityQueue
    q = PriorityQueue()
    for idx, l in enumerate(lists):
    if l:
    q.put((l.val, idx, l))
    h = head = ListNode(0)
    while not q.empty():
    val, idx, node = q.get()
    h.next = node
    h, node = h.next, node.next
    if node:
    q.put((node.val, idx, node))
    return head.next
  • 方法四:俩俩合并。Time: O(Nlogk)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:

    def merge_both(l1, l2):
    if not l1 or not l2:
    return l1 or l2
    if l1.val <= l2.val:
    l1.next = merge_both(l1.next, l2)
    return l1
    else:
    l2.next = merge_both(l1, l2.next)
    return l2

    pairs = list(lists)
    while len(pairs) > 1:
    n = len(pairs)
    if n & 1 == 1:
    pairs.append(None)
    pairs = [merge_both(pairs[i*2], pairs[i*2+1])
    for i in range(((n+1)//2))]
    return pairs[0] if pairs else None

141. Linked List Cycle

判断一个链表是否有环。
查看原题

  • 经典的一道题,看成两个人在赛跑,如果有环,快的人会和慢的人相遇

    1
    2
    3
    4
    5
    6
    7
    def hasCycle(self, head):
    slow = fast = head:
    while fast and fast.next:
    slow, fast = slow.next, fast.next.next
    if fast is slow:
    return True
    return False

142. Linked List Cycle II

求链表中环的入口节点。
查看原题

  • 首先判断此链表是否有环。然后在相交点和头结点一起走,一定会在入口相遇。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def detectCycle(self, head):        
    fast = slow = head
    # 检测是否有环
    while fast and fast.next:
    slow, fast = slow.next, fast.next.next
    if slow is fast:
    break
    else:
    return None
    # 找出入口节点
    while head is not slow:
    head, slow = head.next, slow.next
    return head

206. Reverse Linked List

倒置一个链表。
查看原题

1
2
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
  • 方法一: iteratively

    1
    2
    3
    4
    5
    6
    7
    8
    def reverseList(head):
    prev = None
    while head:
    cur = head
    head = head.next
    cur.next = prev
    prev = cur
    return prev
  • 方法二:使用一行赋值

    1
    2
    3
    4
    5
    def reverseList(self, head):
    prev = None
    while head:
    head.next, prev, head = prev, head, head.next
    return prev
  • 方法三:递归

    1
    2
    3
    4
    5
    6
    def reverseList(self, head, prev=None):
    if not head:
    return prev

    cur, head.next = head.next, prev
    return self.reverseList(cur, head)

92. Reverse Linked List II

跟上题不同的是,只倒置指定区间的部分。
查看原题

1
2
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
  • iteratively

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:

    root = h = ListNode(0)
    h.next = head

    for _ in range(m-1):
    h = h.next
    cur_head = h
    p1 = p2 = cur_head.next
    for _ in range(n-m):
    p2 = p2.next
    prev = p2.next if p2 else None
    if p2:
    p2.next = None
    while p1:
    p1.next, prev, p1 = prev, p1, p1.next
    cur_head.next = prev
    return root.next

160. Intersection of Two Linked Lists

两个链表求相交。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    def getIntersectionNode(self, headA, headB):
    p1, p2 = headA, headB
    while p1 is not p2:
    p1 = p1.next if p1 else headB
    p2 = p2.next if p2 else headA
    return p1

138. Copy List with Random Pointer

深拷贝一个复杂链表,链表多包含了一个随机指针。
查看原题

  • 第一次迭代的过程委托给了defaultdict,通过创建一个默认的对象,再去修改它的label值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def copyRandomList(self, head):
    from collections import defaultdict
    cp = defaultdict(lambda: RandomListNode(0))
    cp[None] = None
    n = head
    while n:
    cp[n].label = n.label
    cp[n].next = cp[n.next]
    cp[n].random = cp[n.random]
    n = n.next
    return cp[head]

237. Delete Node in a Linked List

在链表中删除节点。给定的节点不是尾节点。
查看原题

1
2
Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
  • 这道题关键在于复制

    1
    2
    3
    def deleteNode(self, node):
    node.val = node.next.val # 4->1->1->9
    node.next = node.next.next # 4->1->9

203. Remove Linked List Elements

删除链表中值为val的元素。
查看原题

  • 方法一:遍历head并构建新的ListNode。

    1
    2
    3
    4
    5
    6
    7
    8
    def removeElements(self, head, val):
    l = res = ListNode(0)
    while head:
    if head.val != val:
    l.next = ListNode(head.val)
    l = l.next
    head = head.next
    return res.next
  • 方法二:更喜欢这个方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def removeElements(self, head: 'ListNode', val: 'int') -> 'ListNode':
    l = ListNode(0)
    l.next, ans = head, l
    while l and l.next:
    if l.next.val == val:
    l.next = l.next.next
    else:
    l = l.next
    return ans.next

83. Remove Duplicates from Sorted List

删除有序链表中重复的节点。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    def delete_duplicates(head):
    root = head
    while head and head.next:
    if head.val == head.next.val:
    head.next = head.next.next
    else:
    head = head.next
    return root

82. Remove Duplicates from Sorted List II

和上题不同的是,重复的节点要全部删除。
查看原题

1
2
Input: 1->2->3->3->4->4->5
Output: 1->2->5
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def deleteDuplicates(self, head: ListNode) -> ListNode:
    prev = ans = ListNode(0)
    prev.next = h = head

    while h and h.next:
    remove = False
    while h.next and h.val == h.next.val:
    h.next = h.next.next
    remove = True
    if remove:
    prev.next = h.next
    else:
    prev = prev.next
    h = h.next
    return ans.next

876. Middle of the Linked List

链表中点,如果偶数个,则返回第二个节点。
查看原题

1
2
3
4
5
6
7
8
Input: [1,2,3,4,5]
Output: Node 3 from this list (Serialization: [3,4,5])
The returned node has value 3. (The judge's serialization of this node is [3,4,5]).
Note that we returned a ListNode object ans, such that:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL.

Output: Node 4 from this list (Serialization: [4,5,6])
Since the list has two middle nodes with values 3 and 4, we return the second one.
  • 解法

    1
    2
    3
    4
    5
    def middleNode(self, head: 'ListNode') -> 'ListNode':
    fast = slow = head
    while fast and fast.next:
    slow, fast = slow.next, fast.next.next
    return slow

234. Palindrome Linked List

判断一个链表是否是回文链表。
查看原题

1
2
Input: 1->2->2->1
Output: true
  • 方法一:此题为倒置链表和快慢指针的总和应用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def isPalindrome(self, head: 'ListNode') -> 'bool':
    rev = None
    slow = fast = head
    while fast and fast.next:
    fast = fast.next.next
    slow.next, rev, slow = rev, slow, slow.next
    if fast:
    slow = slow.next
    while rev and rev.val == slow.val:
    rev, slow = rev.next, slow.next
    return rev is None
  • 方法二:上述方法有一个缺点就是改变了原始的head,这里进行一些改进。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def isPalindrome(self, head):
    rev = None
    fast = head
    while fast and fast.next:
    fast = fast.next.next
    rev, rev.next, head = head, rev, head.next
    tail = head.next if fast else head
    isPali = True
    while rev:
    isPali = isPali and rev.val == tail.val
    head, head.next, rev = rev, head, rev.next
    tail = tail.next
    return isPali

24. Swap Nodes in Pairs

成对转换链表。
查看原题

1
Given 1->2->3->4, you should return the list as 2->1->4->3.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    def swapPairs(self, head: ListNode) -> ListNode:
    prev, prev.next = self, head
    while prev.next and prev.next.next:
    a = prev.next # current
    b = a.next
    prev.next, b.next, a.next = b, a, b.next
    prev = a
    return self.next

19. Remove Nth Node From End of List

删除倒数第N个节点。
查看原题

1
2
3
Given linked list: 1->2->3->4->5, and n = 2.

After removing the second node from the end, the linked list becomes 1->2->3->5.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
    root = slow = fast = ListNode(0)
    slow.next = head
    while n >= 0 and fast:
    fast = fast.next
    n -= 1
    while fast:
    slow, fast = slow.next, fast.next
    slow.next = slow.next.next if slow.next else None
    return root.next

328. Odd Even Linked List

重排链表,使奇数位节点在前,偶数位节点在后,就地排序。
查看原题

1
2
Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def oddEvenList(self, head: ListNode) -> ListNode:
    if not head:
    return None
    odd = head
    even_h = even = head.next
    while even and even.next:
    odd.next = odd.next.next
    odd = odd.next
    even.next = even.next.next
    even = even.next
    odd.next = even_h
    return head

148. Sort List

给链表排序。
查看原题

1
2
Input: 4->2->1->3
Output: 1->2->3->4
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def sortList(self, head: ListNode) -> ListNode:

    def merge_both(l1, l2):
    l = h = ListNode(0)
    while l1 and l2:
    if l1.val <= l2.val:
    l.next, l1 = l1, l1.next
    else:
    l.next, l2 = l2, l2.next
    l = l.next
    l.next = l1 or l2
    return h.next

    def merge_sort(h):
    if not h or not h.next:
    return h
    slow = fast = h
    prev = None
    while fast and fast.next:
    prev, slow, fast = slow, slow.next, fast.next.next
    prev.next = None
    left = merge_sort(h)
    right = merge_sort(slow)
    return merge_both(left, right)

    return merge_sort(head)

817. Linked List Components

链表的组件。给定一个集合G,然后根据是否在G中分成若干部分,求连起来在G中的部分的个数。
查看原题

1
2
3
4
5
6
Input: 
head: 0->1->2->3->4
G = [0, 3, 1, 4]
Output: 2
Explanation:
0 and 1 are connected, 3 and 4 are connected, so [0, 1] and [3, 4] are the two connected components.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def numComponents(self, head: ListNode, G: List[int]) -> int:
    SET_G = set(G)
    h = head
    count = 0
    while h:
    if h.val in SET_G:
    if (h.next and h.next.val not in SET_G or
    not h.next):
    count += 1
    h = h.next
    return count

86. Partition List

链表分区,将比x小的节点放到前面,其余节点放到后面,并保持原有顺序。
查看原题

1
2
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def partition(self, head: ListNode, x: int) -> ListNode:
    lt = letter = ListNode(0)
    gt = greater = ListNode(0)
    h = head
    while h:
    if h.val < x:
    lt.next = h
    lt = h
    else:
    gt.next = h
    gt = h
    h = h.next
    gt.next = None # important !!
    lt.next = greater.next
    return letter.next

61. Rotate List

向右旋转链表k次。
查看原题

1
2
3
4
5
6
7
Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
    n, cur, prev = 0, head, None
    while cur:
    n += 1
    prev, cur = cur, cur.next

    if n==0 or k%n==0:
    return head
    k = k % n
    tail = head
    for _ in range(n-k-1):
    tail = tail.next
    ans, tail.next, prev.next = tail.next, None, head
    return ans

725. Split Linked List in Parts

按部分拆分链表。如果不能整除,要保证前面部分的大。
查看原题

1
2
3
4
5
6
Input: 
root = [1, 2, 3], k = 5
Output: [[1],[2],[3],[],[]]
Input:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
    n, cur = 0, root
    ans = []
    while cur:
    n += 1
    cur = cur.next
    parts, remain = divmod(n, k)
    h = root
    for i in range(k):
    head = h
    for i in range(parts-1+(i<remain)):
    h = h.next
    if h:
    h.next, h = None, h.next
    ans.append(head)
    return ans

143. Reorder List

链表头尾捡取直至结束。
查看原题

1
Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def reorderList(self, head: ListNode) -> None:
    if not head:
    return
    slow = fast = head
    while fast and fast.next:
    slow, fast = slow.next, fast.next.next

    tail, slow.next = slow.next, None
    def reverse(node):
    prev = None
    while node:
    node.next, prev, node = prev, node, node.next
    return prev
    tail = reverse(tail)
    h = head
    while h and tail:
    h.next, tail.next, tail, h = tail, h.next, tail.next, h.next

1030. Next Greater Node In Linked List

链表中下一个比当前节点大的值。和503题类似。
查看原题

1
2
Input: [2,7,4,3,5]
Output: [7,0,5,5,0]
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def nextLargerNodes(self, head: ListNode) -> List[int]:
    ans, stack = [], []
    while head:
    while stack and stack[-1][1] < head.val:
    ans[stack.pop()[0]] = head.val
    stack.append((len(ans), head.val))
    ans.append(0)
    head = head.next
    return ans

1171. Remove Zero Sum Consecutive Nodes from Linked List

移除相连和为0的节点。像祖玛一样,连续地删除。答案不唯一。
查看原题

1
2
3
Input: head = [1,2,-3,3,1]
Output: [3,1]
Note: The answer [1,2,1] would also be accepted.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def removeZeroSumSublists(self, head: ListNode) -> ListNode:
    p = dummy = ListNode(0)
    dummy.next = head
    s = 0
    s_sum = [s]
    vals = {}
    while p:
    s += p.val
    s_sum.append(s)
    if s not in vals:
    vals[s] = p
    else:
    vals[s].next = p.next
    s_sum.pop() # remove cur, keep the last
    while s_sum[-1] != s:
    vals.pop(s_sum.pop())
    p = p.next
    return dummy.next

博客使用Markdown,记录一下基本语法的使用。

APPEARANCE

1
# CODE

HEADING

1
# HEADING
HEADING
1
###### HEADING

HEADING

1
2
HEADING
=======

HEADING

1
2
HEADING
-------

DELETED

DELETED

1
2
3
<s>DELETED</s>

~~DELETED~~

ITALIC

ITALIC

1
2
3
*ITALIC*

_ITALIC_

BOLD

BOLD

1
2
3
**BOLD**

__BOLD__

BOLD ITALIC

BOLD ITALIC

1
2
3
***BOLD ITALIC***

___BOLD ITALIC___

2H2 + 02 -> 2H20

1
2H<sub>2</sub> + 0<sub>2</sub> -> 2H<sub>2</sub>0

A2 + B2 = C2

1
A<sup>2</sup> + B<sup>2</sup> = C<sup>2</sup>

PRC

1
<abbr title="People's Republic of China">PRC</abbr>

鲁迅

1
2
3
> 早
>
> -- <cite>鲁迅</cite>
  • POINT ONE

  • POINT TWO

  • POINT …

1
2
3
4
5
- POINT ONE

- POINT TWO

- POINT ...
  • POINT ONE

  • POINT TWO

  • POINT …

1
2
3
4
5
* POINT ONE

* POINT TWO

* POINT ...
  • POINT
    • POINT
    • POINT
  • POINT
    • POINT
1
2
3
4
5
+ POINT
+ POINT
+ POINT
+ POINT
+ POINT
  1. POINT ONE
  2. POINT TWO
  3. POINT …
1
2
3
1. POINT ONE
2. POINT TWO
3. POINT ...
  • TASK 1
  • TASK 2
    • TASK 2.1
    • TASK 2.2
1
2
3
4
- [x] TASK 1
- [ ] TASK 2
- [x] TASK 2.1
- [ ] TASK 2.2

git status

1
`git status`

https://github.com/wadeee/

1
<https://github.com/wadeee/>

GITHUB

1
[GITHUB](https://github.com/wadeee/)

GITHUB

1
2
3
[GITHUB][TARGET]

[TARGET]: https://github.com/wadeee/ "wade's github"

1
![](/images/posts/learn-markdown/pic.jpg "pic")

pic

1
2
3
4
5
[![pic]][pic link]

[pic]: /images/posts/learn-markdown/pic.jpg "pic"

[pic link]: https://github.com/wadeee/
TEXT LIKE `<pre>`
1
TEXT LIKE `<pre>`
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<mate charest="utf-8" />
<title>Hello world!</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
```html 
<!DOCTYPE html>
<html>
<head>
<mate charest="utf-8" />
<title>Hello world!</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
```
Function name Description
help() Display the help window.
destroy() Destroy your computer!
1
2
3
4
| Function name | Description                    |
| ------------- | ------------------------------ |
| `help()` | Display the help window. |
| `destroy()` | **Destroy your computer!** |




1
2
3
4
---
----
***
*****

加速为你找到目标

关于哈希表

哈希表(Hash table),是根据关键码值(Key value)而直接进行访问的数据结构。
也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
这个映射函数叫做散列函数,存放记录的数组叫做散列表。

LeetCode真题

1. Two Sum

给定一个数组,找出数组两个元素相加为目标值,假定只有唯一解。
查看原题

1
2
3
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
  • 解法

    1
    2
    3
    4
    5
    6
    7
    def two_sum(nums, target):
    buff_dict = {}
    for i, num in enumerate(nums):
    if num not in buff_dict:
    buff_dict[target-num] = i
    else:
    return [buff_dict[num], i]

720. Longest Word in Dictionary

字典中的最长单词,找出一个列表中的一个单词,该单词的子单词也必须在字典中。相同长度的单词,返回字典序最前的一个。
查看原题

1
2
3
4
5
Input: 
words = ["w","wo","wor","worl", "world"]
Output: "world"
Explanation:
The word "world" can be built one character at a time by "w", "wo", "wor", and "worl".
  • 解法:Brute Force.

    1
    2
    3
    4
    5
    6
    7
    8
    def longestWord(self, words):
    res = ''
    wordset = set(words)
    for word in words:
    if len(word)>len(res) or len(word)==len(res) and word<res:
    if all(word[:k] in wordset for k in range(1, len(word))):
    res = word
    return res

748. Shortest Completing Word

最短的完整匹配单词。包含licensePlate中的所有字母,大小写不敏感。假设答案一定存在。
查看原题

1
2
3
4
5
Input: licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"]
Output: "steps"
Explanation: The smallest length word that contains the letters "S", "P", "S", and "T".
Note that the answer is not "step", because the letter "s" must occur in the word twice.
Also note that we ignored case for the purposes of comparing whether a letter exists in the word.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def shortestCompletingWord(self, licensePlate: 'str', words: 'List[str]') -> 'str':
    ans = ''
    lp = ''.join(x for x in licensePlate.lower() if x.isalpha())
    for w in words:
    temp = list(w.lower())
    for l in lp:
    if l in temp:
    temp.remove(l)
    else:
    break
    else:
    if len(w)<len(ans) or ans=='':
    ans = w
    return ans

811. Subdomain Visit Count

子域名访问量。给定一个三级或二级域名列表,统计所有三级、二级和顶级域名的访问量。
查看原题

1
https://leetcode.com/problems/subdomain-visit-count/
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def subdomainVisits(self, cpdomains: 'List[str]') -> 'List[str]':
    ans = collections.defaultdict(int)
    for domain in cpdomains:
    count, d = domain.split()
    count = int(count)
    frags = d.split('.')
    for i in range(len(frags)):
    ans['.'.join(frags[i:])] += count
    return ['{} {}'.format(c, d) for d, c in ans.items()]

884. Uncommon Words from Two Sentences

求两句话中的单词,在本句中出现一次,并不在另一句中的单词。也就是在两句中出现一次。
查看原题

1
2
Input: A = "this apple is sweet", B = "this apple is sour"
Output: ["sweet","sour"]
  • counter

    1
    2
    3
    4
    def uncommonFromSentences(self, A: 'str', B: 'str') -> 'List[str]':
    from collections import Counter
    count = Counter((A + ' ' + B).split())
    return [word for word, c in count.items() if c == 1]

1010. Pairs of Songs With Total Durations Divisible by 60

和能被60整除的为一对,求有多少对。
查看原题

1
2
3
4
5
6
Input: [30,20,150,100,40]
Output: 3
Explanation: Three pairs have a total duration divisible by 60:
(time[0] = 30, time[2] = 150): total duration 180
(time[1] = 20, time[3] = 100): total duration 120
(time[1] = 20, time[4] = 40): total duration 60
  • 首先判断此链表是否有环。然后在相交点和头结点一起走,一定会在入口相遇。

    1
    2
    3
    4
    5
    6
    7
    8
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
    c = collections.defaultdict(int)
    ans = 0
    for t in time:
    # ans += c[(60-t%60)%60]
    ans += c[-t % 60]
    c[t%60] += 1
    return ans

1138. Alphabet Board Path

小写字母排列的键盘,要打出目标字母需要移动的操作。
查看原题

1
2
Input: target = "leet"
Output: "DDR!UURRR!!DDD!"
  • 此题需要注意z,然后按照一个优先的顺序移动即可。另外使用字典可以快速定位坐标,而不用每个字符做比较

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def alphabetBoardPath(self, target: str) -> str:
    import string
    m = {c: (i//5, i%5) for i, c in enumerate(string.ascii_lowercase)}
    ans = ''
    x0 = y0 = 0
    for c in target:
    x, y = m[c]
    if y < y0: ans += 'L' * (y0-y)
    if x < x0: ans += 'U' * (x0-x)
    if y > y0: ans += 'R' * (y-y0)
    if x > x0: ans += 'D' * (x-x0)
    x0, y0 = x, y
    ans += '!'
    return ans

1072. Flip Columns For Maximum Number of Equal Rows

二维数组,翻转某几列可以最多使多少行内的元素都相同。
查看原题

1
2
3
4
5
6
7
Input: [[0,1],[1,1]]
Output: 1
Explanation: After flipping no values, 1 row has all values equal.

Input: [[0,0,0],[0,0,1],[1,1,0]]
Output: 2
Explanation: After flipping values in the first two columns, the last two rows have equal values.
  • 方法一:核心思想在于找到每行的模式,具有相同模式的行,最终可变成同样的数值。

    1
    2
    3
    4
    5
    6
    def maxEqualRowsAfterFlips(self, matrix: List[List[int]]) -> int:
    c = collections.Counter()
    for row in matrix:
    c[tuple([x for x in row])] += 1
    c[tuple([1-x for x in row])] += 1
    return max(c.values())
  • 方法二:使用异或。方法一中其实有多余的部分,模式与反模式都求了出来,其实没有必要。

    1
    2
    def maxEqualRowsAfterFlips(self, matrix: List[List[int]]) -> int:
    return max(collections.Counter(tuple(r ^ row[0] for r in row) for row in matrix).values())

1160. Find Words That Can Be Formed by Characters

找出能被目标字符串组成的子串长度和。
查看原题

  • 解法

    1
    2
    3
    def countCharacters(self, words: List[str], chars: str) -> int:
    ma = collections.Counter(chars)
    return sum(len(w) for w in words if not collections.Counter(w)-ma)

你好,树先生

关于二叉树

二叉树是每个结点最多有两个子树的树结构。

  • 树节点结构

    1
    2
    3
    4
    5
    class TreeNode:
    def __init__(self, x):
    self.val = x
    self.left = None
    self.right = None

LeetCode真题

144. Binary Tree Preorder Traversal

二叉树前序遍历
查看原题

1
2
3
4
5
6
7
8
Input: [1,null,2,3]
1
\
2
/
3

Output: [1,2,3]
  • 方法一:iteratively

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def preorderTraversal(self, root: 'TreeNode') -> 'List[int]':
    ans, stack = [], root and [root]
    while stack:
    node = stack.pop()
    if node:
    ans.append(node.val)
    stack.append(node.right)
    stack.append(node.left)
    return ans
  • 方法二:recursively

    1
    2
    3
    4
    5
    def preorder_traversal(root):
    if not root:
    return []
    return [root.val] + self.preorderTraversal(root.left) + \
    self.preorderTraversal(root.right)

589. N-ary Tree Preorder Traversal

N-叉树的前序遍历。N叉树和二叉树有个区别,就是N叉树不需要考虑子节点知否为空,做单独的判断。
查看原题

  • 方法一:recursively.

    1
    2
    3
    4
    5
    6
    7
    def preorder(self, root):
    if not root:
    return []
    res = [root.val]
    for child in root.children:
    res += self.preorder(child)
    return res
  • 方法二:iteratively.

    1
    2
    3
    4
    5
    6
    7
    def preorder(self, root):
    res, stack = [], root and [root]
    while stack:
    node = stack.pop()
    res.append(node.val)
    stack.extend(reversed(node.children))
    return res

94. Binary Tree Inorder Traversal

中序遍历二叉树
查看原题

1
2
3
4
5
6
7
8
Input: [1,null,2,3]
1
\
2
/
3

Output: [1,3,2]
  • 方法一:使用栈迭代。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def inorderTraversal(self, root: TreeNode) -> List[int]:
    stack, ans = [], []
    while stack or root:
    while root:
    stack.append(root)
    root = root.left
    root = stack.pop()
    ans.append(root.val)
    root = root.right
    return ans
  • 方法二:Morris Traversal.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    def inorderTraversal(self, root: TreeNode) -> List[int]:
    cur, ans = root, []
    while cur:
    if not cur.left:
    ans.append(cur.val)
    cur = cur.right
    else:
    pre = cur.left
    # 找到当前节点左子树中最右的右节点
    while pre.right and pre.right != cur:
    pre = pre.right

    if not pre.right:
    # 找到最右的节点,连接到根节点
    pre.right = cur
    cur = cur.left
    # 恢复节点
    else:
    pre.right = None
    ans.append(cur.val)
    cur = cur.right

    return ans

145. Binary Tree Postorder Traversal

后序遍历二叉树
查看原题

1
2
3
4
5
6
7
8
Input: [1,null,2,3]
1
\
2
/
3

Output: [3,2,1]
  • 方法一:根右左,再倒序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def postorder_traversal(root):
    res, stack = [], [root]
    while stack:
    node = stack.pop()
    if node:
    res.append(node.val)
    stack.append(node.left)
    stack.append(node.right)
    return res[::-1]
  • 方法二:思想: 使用last作为判断是否该节点的右子树完成遍历,如果一个node.right已经刚刚遍历完毕,那么将last==node.right,否则将会寻找node.right。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def postorderTraversal(self, root):
    res, stack, node, last = [], [], root, None
    while stack or node:
    if node:
    stack.append(node)
    node = node.left
    else:
    node = stack[-1]
    if not node.right or last == node.right:
    node = stack.pop()
    res.append(node.val)
    last, node = node, None
    else:
    node = node.right
    return res
  • 方法三:使用boolean判断一个节点是否被遍历过

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def postorderTraversal(self, root):
    res, stack = [], [(root, False)]
    while stack:
    node, visited = stack.pop()
    if node:
    if visited:
    res.append(node.val)
    else:
    stack.append((node, True))
    stack.append((node.right, False))
    stack.append((node.left, False))
    return res
  • 方法四:dfs.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def postorderTraversal(self, root: 'TreeNode') -> 'List[int]':
    ans = []

    def dfs(node):
    if not node:
    return
    dfs(node.left)
    dfs(node.right)
    ans.append(node.val)

    dfs(root)
    return ans

590. N-ary Tree Postorder Traversal

N-叉树的后序遍历。
查看原题

  • 方法一:recursively.

    1
    2
    3
    4
    def postorder(self, root):
    if not root:
    return []
    return sum([self.postorder(child) for child in root.children], []) + [root.val]
  • 方法二:iteratively and reversed.

    1
    2
    3
    4
    5
    6
    7
    def postorder(self, root):
    res, stack = [], root and [root]
    while stack:
    node = stack.pop()
    res.append(node.val)
    stack.extend(node.children)
    return res[::-1]
  • 方法三:iteratively and flag.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def postorder(self, root):
    res, stack = [], root and [(root, False)]
    while stack:
    node, visited = stack.pop()
    if visited:
    res.append(node.val)
    else:
    stack.append((node, True))
    stack.extend((n, False) for n in reversed(node.children))
    return res

100. Same Tree

判断相同的二叉树。
查看原题

1
2
3
4
5
6
7
Input:     1         1
/ \ / \
2 3 2 3

[1,2,3], [1,2,3]

Output: true
  • 方法一:recursively

    1
    2
    3
    4
    5
    6
    def isSameTree(self, p: 'TreeNode', q: 'TreeNode') -> 'bool':
    if p and q:
    return (p.val==q.val and self.isSameTree(p.left, q.left) and
    self.isSameTree(p.right, q.right))
    else:
    return p is q
  • 方法二:recursively, tuple

    1
    2
    3
    4
    def is_same_tree(p, q):
    def t(n):
    return n and (n.val, t(n.left), t(n.right))
    return t(p) == t(q)
  • 方法三:iteratively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def isSameTree(self, p: 'TreeNode', q: 'TreeNode') -> 'bool':
    stack = [(p, q)]
    while stack:
    p1, p2 = stack.pop()
    if not p1 and not p2:
    continue
    if not p1 or not p2:
    return False
    if p1.val != p2.val:
    return False
    stack.append((p1.left, p2.left))
    stack.append((p1.right, p2.right))
    return True

101. Symmetric Tree

判断二叉树是否对称。
查看原题

1
2
3
4
5
    1
/ \
2 2
/ \ / \
3 4 4 3
  • 方法一:recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def isSymmetric(self, root: 'TreeNode') -> 'bool':

    def symmetric(p1, p2):
    if p1 and p2:
    return (p1.val == p2.val and symmetric(p1.left, p2.right) and
    symmetric(p1.right, p2.left))
    else:
    return p1 is p2

    if not root:
    return True
    return symmetric(root.left, root.right)
  • 方法二:iteratively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def isSymmetric(self, root: 'TreeNode') -> 'bool':
    stack = root and [(root.left, root.right)]
    while stack:
    p1, p2 = stack.pop()
    if not p1 and not p2: continue
    if not p1 or not p2: return False
    if p1.val != p2.val: return False
    stack.append((p1.left, p2.right))
    stack.append((p1.right, p2.left))
    return True

104. Maximum Depth of Binary Tree

二叉树最大深度。
查看原题

1
2
3
4
5
6
    3
/ \
9 20
/ \
15 7
return 3
  • 方法一:recursively

    1
    2
    3
    4
    def max_depth(root):
    if not root:
    return 0
    return max(max_depth(root.left), max_depth(root.right)) + 1
  • 方法二:iteratively. BFS with deque

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def maxDepth(self, root: 'TreeNode') -> 'int':
    q = root and collections.deque([(root, 1)])
    d = 0
    while q:
    node, d = q.popleft()
    if node.right:
    q.append((node.right, d+1))
    if node.left:
    q.append((node.left, d+1))
    return d

559. Maximum Depth of N-ary Tree

N-叉树的最大深度。
查看原题

  • 方法一:BFS with deque.

    1
    2
    3
    4
    5
    6
    7
    8
    def maxDepth(self, root: 'Node') -> 'int':
    q = root and collections.deque([(root, 1)])
    d = 0
    while q:
    node, d = q.popleft()
    for child in node.children:
    q.append((child, d + 1))
    return d
  • 方法二:BFS.

    1
    2
    3
    4
    5
    def maxDepth(self, root):
    q, level = root and [root], 0
    while q:
    q, level = [child for node in q for child in node.children], level+1
    return level
  • 方法三:recursively.

    1
    2
    3
    4
    def maxDepth(self, root: 'Node') -> 'int':
    if not root:
    return 0
    return max(list(map(self.maxDepth, root.children)) or [0]) + 1

111. Minimum Depth of Binary Tree

求根节点到叶子节点的最小深度。
查看原题

  • 方法一:recursively

    1
    2
    3
    4
    5
    6
    7
    def minDepth(self, root):
    if not root:
    return 0
    if root.left and root.right:
    return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
    else:
    return self.minDepth(root.left) + self.minDepth(root.right) + 1
  • 方法二:对上述方法修改,更加Pythonic. 注意一点,Python3中要加list,否则max因为空值报错。

    1
    2
    3
    4
    def minDepth(self, root: 'TreeNode') -> 'int':
    if not root: return 0
    d = list(map(self.minDepth, (root.left, root.right)))
    return 1 + (min(d) or max(d))
  • 方法三:迭代法,BFS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def minDepth(self, root: 'TreeNode') -> 'int':
    q = root and collections.deque([(root, 1)])
    d = 0
    while q:
    node, d = q.popleft()
    if not node.left and not node.right:
    return d
    if node.left:
    q.append((node.left, d+1))
    if node.right:
    q.append((node.right, d+1))
    return d

105. Construct Binary Tree from Preorder and Inorder Traversal

根据前序遍历和中序遍历重建二叉树。
查看原题

1
2
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
  • 方法一:切片。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def buildTree(preorder, inorder):
    if preorder == []:
    return None
    root_val = preorder[0]
    root = TreeNode(root_val)
    cut = inorder.index(root_val)
    root.left = buildTree(preorder[1:cut+1], inorder[:cut])
    root.right = buildTree(preorder[cut+1:], inorder[cut+1:])
    return root
  • 方法二:上述方法在极端情况下,如只有左子树的情况,由于index会将时间复杂度上升到O(n²),而且切片产生了一些不必要的内存,pop和reverse是为了增加效率。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def buildTree(self, preorder: 'List[int]', inorder: 'List[int]') -> 'TreeNode':
    def build(stop):
    if inorder and inorder[-1] != stop:
    root = TreeNode(preorder.pop())
    root.left = build(root.val)
    inorder.pop()
    root.right = build(stop)
    return root
    preorder.reverse()
    inorder.reverse()
    return build(None)

572. Subtree of Another Tree

判断是否是树的子结构。
查看原题

  • 思路:这道题是遍历加判断相同树的结合。这里采用前序遍历和递归判断相同树。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def isSubtree(self, s: 'TreeNode', t: 'TreeNode') -> 'bool':

    def is_same(s, t):
    if s and t:
    return (s.val==t.val and is_same(s.left, t.left) and
    is_same(s.right, t.right))
    else:
    return s is t

    stack = s and [s]
    while stack:
    node = stack.pop()
    if node:
    if is_same(node, t):
    return True
    stack.append(node.right)
    stack.append(node.left)
    return False

102. Binary Tree Level Order Traversal

分层遍历二叉树。
查看原题

  • 注意:循环条件要加上root,以防止root is None

    1
    2
    3
    4
    5
    6
    def levelOrder(self, root: 'TreeNode') -> 'List[List[int]]':
    ans, level = [], root and [root]
    while level:
    ans.append([n.val for n in level])
    level = [k for n in level for k in (n.left, n.right) if k]
    return ans

103. Binary Tree Zigzag Level Order Traversal

之字形打印二叉树。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    def zigzagLevelOrder(self, root: 'TreeNode') -> 'List[List[int]]':
    ans, level, order = [], root and [root], 1
    while level:
    ans.append([n.val for n in level][::order])
    order *= -1
    level = [kid for n in level for kid in (n.left, n.right) if kid]
    return ans

107. Binary Tree Level Order Traversal II

和102题不同的是,从下到上分层打印。
查看原题

  • 方法一:将结果倒序输出。

    1
    2
    3
    4
    5
    6
    def levelOrderBottom(self, root):
    res, level = [], [root]
    while root and level:
    res.append([n.val for n in level])
    level = [kid for n in level for kid in (n.left, n.right) if kid]
    return res[::-1]
  • 方法二:也可以从前面插入元素。

    1
    2
    3
    4
    5
    6
    def levelOrderBottom(self, root):
    res, level = [], [root]
    while root and level:
    res.insert(0, [n.val for n in level])
    level = [kid for n in level for kid in (n.left, n.right) if kid]
    return res

429. N-ary Tree Level Order Traversal

分层打印N叉树。
查看原题

  • 方法一:将结果倒序输出。

    1
    2
    3
    4
    5
    6
    def levelOrder(self, root: 'Node') -> 'List[List[int]]':
    ans, level = [], root and [root]
    while level:
    ans.append([n.val for n in level])
    level = [k for n in level for k in n.children if k]
    return ans

637. Average of Levels in Binary Tree

遍历一个二叉树,求每层节点的平均值,按照节点不为空的个数。
查看原题

1
2
3
4
5
6
7
8
9
Input:
3
/ \
9 20
/ \
15 7
Output: [3, 14.5, 11]
Explanation:
The average value of nodes on level 0 is 3, on level 1 is 14.5, and on level 2 is 11. Hence return [3, 14.5, 11].
  • 解法

    1
    2
    3
    4
    5
    6
    def averageOfLevels(self, root: 'TreeNode') -> 'List[float]':
    ans, level = [], root and [root]
    while level:
    ans.append(sum(n.val for n in level) / len(level))
    level = [k for n in level for k in (n.left, n.right) if k]
    return ans

515. Find Largest Value in Each Tree Row

找到树每层的最大值。
查看原题

  • BFS.

    1
    2
    3
    4
    5
    6
    def largestValues(self, root: TreeNode) -> List[int]:
    ans, levels = [], root and [root]
    while levels:
    ans.append(max(x.val for x in levels))
    levels = [k for n in levels for k in (n.left, n.right) if k]
    return ans

987. Vertical Order Traversal of a Binary Tree

垂直遍历二叉树,从左到右,从上到下,如果节点具有相同位置,按照值从小到大。
查看原题

1
2
3
4
5
Input: [1,2,3,4,5,6,7]
Output: [[4],[2],[1,5,6],[3],[7]]
Explanation:
The node with value 5 and the node with value 6 have the same position according to the given scheme.
However, in the report "[1,5,6]", the node value of 5 comes first since 5 is smaller than 6.
  • dfs. 通过建立一个字典数组,将对应的节点使用深度优先遍历初始化数组。然后按照x, y, val三个优先级进行排序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def verticalTraversal(self, root: 'TreeNode') -> 'List[List[int]]':
    seen = collections.defaultdict(
    lambda: collections.defaultdict(list)
    )

    def dfs(node, x=0, y=0):
    if node:
    seen[x][y].append(node.val)
    dfs(node.left, x-1, y+1)
    dfs(node.right, x+1, y+1)

    dfs(root)
    ans = []
    for x in sorted(seen):
    inner = []
    for y in sorted(seen[x]):
    inner.extend(sorted(n for n in seen[x][y]))
    ans.append(inner)
    return ans

257. Binary Tree Paths

打印二叉树从根节点到叶子节点全部路径。
查看原题

1
2
3
4
5
6
7
8
9
10
11
Input:

1
/ \
2 3
\
5

Output: ["1->2->5", "1->3"]

Explanation: All root-to-leaf paths are: 1->2->5, 1->3
  • 方法一:iteratively。思路:采用前序遍历二叉树,使用tuple保存节点当前路径,如果是叶子节点,则添加到结果中。开始老是想着用’->’.join(),这样反而麻烦,直接使用字符串保存就好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def binaryTreePaths(self, root: 'TreeNode') -> 'List[str]':
    ans, stack = [], root and [(root, str(root.val))]
    while stack:
    n, p = stack.pop()
    if not n.left and not n.right:
    ans.append(p)
    if n.right:
    stack.append((n.right, p+'->'+str(n.right.val)))
    if n.left:
    stack.append((n.left, p+'->'+str(n.left.val)))
    return ans
  • 方法二:dfs.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def binaryTreePaths(self, root: 'TreeNode') -> 'List[str]':
    ans = []
    def dfs(n, path):
    if n:
    path.append(str(n.val))
    if not n.left and not n.right:
    ans.append('->'.join(path))
    dfs(n.left, path)
    dfs(n.right, path)
    path.pop()
    dfs(root, [])
    return ans
  • 方法三:recursively

    1
    2
    3
    4
    5
    6
    def binaryTreePaths(self, root): 
    if not root:
    return []
    return [str(root.val) + '->' + path
    for kid in (root.left, root.right) if kid
    for path in self.binaryTreePaths(kid)] or [str(root.val)]

257. Binary Tree Paths

求字典顺序最小的路径,路径指叶子节点到根节点的路径。0对应a,1对应b。
查看原题

1
2
Input: [0,1,2,3,4,3,4]
Output: "dba"
  • 方法一:先列出所有根到叶子的路径,再reverse求最小值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def smallestFromLeaf(self, root: 'TreeNode') -> 'str':
    OFFSET = ord('a')
    stack = root and [(root, chr(root.val+OFFSET))]
    ans = '~'
    while stack:
    n, p = stack.pop()
    if not n.left and not n.right:
    ans = min(ans, p[::-1])
    if n.right:
    stack.append((n.right, p+chr(n.right.val+OFFSET)))
    if n.left:
    stack.append((n.left, p+chr(n.left.val+OFFSET)))
    return ans
  • 方法二:dfs. 递归计算完左右节点,然后再将根节点pop掉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def smallestFromLeaf(self, root: 'TreeNode') -> 'str':
    self.ans = '~'

    def dfs(node, A):
    if node:
    A.append(chr(node.val + ord('a')))
    if not node.left and not node.right:
    self.ans = min(self.ans, ''.join(reversed(A)))
    dfs(node.left, A)
    dfs(node.right, A)
    A.pop()

    dfs(root, [])
    return self.ans

112. Path Sum

判断是否具有从根节点到叶子节点上的值和为sum。
查看原题

  • 方法一:recursively

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def hasPathSum(self, root: 'TreeNode', total: 'int') -> 'bool':
    if not root:
    return False
    elif (not root.left and not root.right and
    root.val==total):
    return True
    else:
    return (self.hasPathSum(root.left, total-root.val) or
    self.hasPathSum(root.right, total-root.val))
  • 方法二:iteratively

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def hasPathSum(self, root: 'TreeNode', total: 'int') -> 'bool':
    stack = root and [(root, total)]
    while stack:
    n, t = stack.pop()
    if not n.left and not n.right and n.val==t:
    return True
    if n.right:
    stack.append((n.right, t-n.val))
    if n.left:
    stack.append((n.left, t-n.val))
    return False

113. Path Sum II

上题的升级版,要求二维数组返回所有路径。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sum = 22

5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1

[
[5,4,11,2],
[5,8,4,5]
]
  • 方法一:iteratively. 举一反三。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def pathSum(self, root: 'TreeNode', total: 'int') -> 'List[List[int]]':
    stack = root and [(root, [root.val], total)]
    ans = []
    while stack:
    n, v, t = stack.pop()
    if not n.left and not n.right and n.val==t:
    ans.append(v)
    if n.right:
    stack.append((n.right, v+[n.right.val], t-n.val))
    if n.left:
    stack.append((n.left, v+[n.left.val], t-n.val))
    return ans
  • 方法二:recursively. 先找出所有路径,再过滤,实际上和257题一样。不过这并没有把这道题的特性涵盖进去。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def pathSum(self, root, sum_val):
    paths = self.all_paths(root)
    return [path for path in paths if sum(path)==sum_val]

    def all_paths(self, root):
    if not root:
    return []
    return [[root.val]+path
    for kid in (root.left, root.right) if kid
    for path in self.all_paths(kid)] or [[root.val]]
  • 方法三:recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def pathSum(self, root, sum):
    if not root:
    return []
    val, *kids = root.val, root.left, root.right
    if any(kids):
    return [[val] + path
    for kid in kids if kid
    for path in self.pathSum(kid, sum-val)]
    return [[val]] if val==sum else []

297. Serialize and Deserialize Binary Tree

序列化反序列化二叉树。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Codec:

    def serialize(self, root):
    if not root:
    return '$'
    return (str(root.val) + ',' + self.serialize(root.left) +
    ',' + self.serialize(root.right))

    def deserialize(self, data):
    nodes = data.split(',')[::-1]
    return self.deserialize_tree(nodes)

    def deserialize_tree(self, nodes):
    val = nodes.pop()
    if val == '$':
    return None
    root = TreeNode(val)
    root.left = self.deserialize_tree(nodes)
    root.right = self.deserialize_tree(nodes)
    return root

110. Balanced Binary Tree

判断是否是平衡二叉树。
查看原题

  • 方法一:递归+递归。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def isBalanced(self, root):
    if not root:
    return True
    return self.isBalanced(root.left) and self.isBalanced(root.right) and \
    abs(self.max_depth(root.left)-self.max_depth(root.right)) <= 1

    def max_depth(self, root):
    if not root:
    return 0
    return max(self.max_depth(root.left), self.max_depth(root.right)) + 1
  • 方法二:dfs. 算深度的时候判断左右是否深度超过1. 这里变量不能把self去掉,否则[1,2,2,3,3,null,null,4,4]会错误的返回True而不是False

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def isBalanced(self, root: 'TreeNode') -> 'bool':
    self.balanced = True

    def dfs(node):
    if not node:
    return 0
    left = dfs(node.left)
    right = dfs(node.right)
    if not self.balanced or abs(left - right) > 1:
    self.balanced = False
    return max(left, right) + 1

    dfs(root)
    return self.balanced

108. Convert Sorted Array to Binary Search Tree

将有序数组转换成二叉搜索树。
查看原题

1
2
3
4
5
6
7
8
Given the sorted array: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5],
0
/ \
-3 9
/ /
-10 5
  • 方法一:递归。

    1
    2
    3
    4
    5
    6
    7
    8
    def sortedArrayToBST(self, nums):
    if not nums:
    return None
    mid = len(nums) // 2
    root = TreeNode(nums[mid])
    root.left = self.sortedArrayToBST(nums[:mid])
    root.right = self.sortedArrayToBST(nums[mid+1:])
    return root
  • 方法二:不使用切片。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def sortedArrayToBST(self, nums: 'List[int]') -> 'TreeNode':

    def convert(lo, hi):
    if lo > hi:
    return None
    mid = (lo+hi) // 2
    root = TreeNode(nums[mid])
    root.left = convert(lo, mid-1)
    root.right = convert(mid+1, hi)
    return root

    return convert(0, len(nums)-1)

235. Lowest Common Ancestor of a Binary Search Tree

寻找二叉搜索树的最小公共祖先。
查看原题

  • 方法一:iteratively.

    1
    2
    3
    4
    def lowestCommonAncestor(self, root, p, q):
    while (root.val-p.val) * (root.val-q.val) > 0:
    root = (root.left, root.right)[root.val < p.val]
    return root
  • 方法二:recursively.

    1
    2
    3
    4
    5
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    if (root.val-p.val) * (root.val-q.val) <= 0:
    return root
    return self.lowestCommonAncestor(
    (root.left, root.right)[root.val < p.val], p, q)

404. Sum of Left Leaves

求一个二叉树所有左叶子节点的和。
查看原题

  • 方法一:iteratively.这里使用了tuple记录是否为左叶子节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def sumOfLeftLeaves(self, root: 'TreeNode') -> 'int':
    ans, stack = 0, root and [(root, False)]
    while stack:
    n, isleft = stack.pop()
    if n:
    if not n.left and not n.right and isleft:
    ans += n.val
    stack.append((n.right, False))
    stack.append((n.left, True))
    return ans
  • 方法二:recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    def sumOfLeftLeaves(self, root: 'TreeNode') -> 'int':
    if not root:
    return 0
    if (root.left and not root.left.left and not root.left.right):
    return root.left.val + self.sumOfLeftLeaves(root.right)
    else:
    return (self.sumOfLeftLeaves(root.left) +
    self.sumOfLeftLeaves(root.right))

938. Range Sum of BST

给两个节点的值,求二叉搜索树在这两个值之间的节点和。每个节点的值唯一。
查看原题

1
2
3
4
Input: root = [10,5,15,3,7,null,18], L = 7, R = 15
Output: 32
Input: root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10
Output: 23
  • 方法一:先前序遍历了一下,再根据条件求和。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def rangeSumBST(self, root, L, R):
    traverse, stack = [], [root]
    while stack:
    node = stack.pop()
    if node:
    traverse.append(node.val)
    if node.right:
    stack.append(node.right)
    if node.left:
    stack.append(node.left)
    return sum([x for x in traverse if L <= x <= R])
  • 方法二:利用二叉搜索树的特性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def rangeSumBST(self, root: 'TreeNode', L: 'int', R: 'int') -> 'int':
    ans, stack = 0, root and [root]
    while stack:
    node = stack.pop()
    if node.val > L and node.left:
    stack.append(node.left)
    if node.val < R and node.right:
    stack.append(node.right)
    if L <= node.val <= R:
    ans += node.val
    return ans

530. Minimum Absolute Difference in BST

求二叉搜索树任意两个节点的最小差。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
Input:

1
\
3
/
2

Output:
1

Explanation:
The minimum absolute difference is 1, which is the difference between 2 and 1 (or between 2 and 3).
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def getMinimumDifference(self, root: 'TreeNode') -> 'int':

    def inorder(n):
    if not n:
    return []
    return inorder(n.left) + [n.val] + inorder(n.right)

    nums = inorder(root)
    # return min(nums[i+1]-nums[i] for i in range(len(nums)-1))
    return min(b-a for a, b in zip(nums, nums[1:]))

783. Minimum Distance Between BST Nodes

二叉搜索树两个节点的最小值。和530是一道题。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Input: root = [4,2,6,1,3,null,null]
Output: 1
Explanation:
Note that root is a TreeNode object, not an array.

The given tree [4,2,6,1,3,null,null] is represented by the following diagram:

4
/ \
2 6
/ \
1 3

while the minimum difference in this tree is 1, it occurs between node 1 and node 2, also between node 3 and node 2.
  • 方法一:递归 + 生成器, 遍历了两次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def minDiffInBST(self, root: 'TreeNode') -> 'int':

    def inorder(node):
    if not node:
    return []
    return inorder(node.left) + [node.val] + inorder(node.right)

    t = inorder(root)
    return min(t[x]-t[x-1] for x in range(1, len(t)))
  • 方法二:一次遍历,没有保存整个遍历数组,效率高。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def minDiffInBST(self, root: TreeNode) -> int:
    ans, last, stack = float('inf'), float('-inf'), []
    while stack or root:
    while root:
    stack.append(root)
    root = root.left
    root = stack.pop()
    ans, last = min(ans, root.val-last), root.val
    root = root.right
    return ans
  • 方法三:一次递归。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Solution:
    pre = float('-inf')
    ans = float('inf')

    def minDiffInBST(self, root: 'TreeNode') -> 'int':
    if root.left:
    self.minDiffInBST(root.left)
    self.ans = min(self.ans, root.val-self.pre)
    self.pre = root.val
    if root.right:
    self.minDiffInBST(root.right)
    return self.ans

538. Convert BST to Greater Tree

二叉搜索树转换。使得节点的值等于所有比它大的节点的和。
查看原题

1
2
3
4
5
6
7
8
9
Input: The root of a Binary Search Tree like this:
5
/ \
2 13

Output: The root of a Greater Tree like this:
18
/ \
20 13
  • 方法一:recursively。这里使用了一个变量来保存当前的累加和,然后递归中采用先右后左的方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def convertBST(self, root: 'TreeNode') -> 'TreeNode':
    self.sum_val = 0

    def convert(node):
    if node:
    convert(node.right)
    self.sum_val += node.val
    node.val = self.sum_val
    convert(node.left)

    convert(root)
    return root
  • 方法二:iteratively。94题中的中序遍历迭代方式不能实现,因为迭代时改变了根节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def convertBST(self, root):
    stack = [(root, False)]
    sum_val = 0
    while stack:
    node, visited = stack.pop()
    if node:
    if visited:
    node.val += sum_val
    sum_val = node.val
    else:
    stack.append((node.left, False))
    stack.append((node, True))
    stack.append((node.right, False))
    return root

958. Check Completeness of a Binary Tree

判断二叉树是否是完整二叉树。完整二叉树为:除了最后一层所有节点不能为空,最后一层节点全部去靠左。
查看原题

1
2
3
4
5
6
7
Input: [1,2,3,4,5,6]
Output: true
Explanation: Every level before the last is full (ie. levels with node-values {1} and {2, 3}), and all nodes in the last level ({4, 5, 6}) are as far left as possible.

Input: [1,2,3,4,5,null,7]
Output: false
Explanation: The node with value 7 isn't as far left as possible.
  • 方法一:采用分层遍历的方式,判断每层的节点是否是2**level。最后一层采用切片的方式判断最左原则。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Solution:
    def isCompleteTree(self, root):
    if not root:
    return True
    levels = [root]
    last_full = True
    level = 0
    while levels:
    value_nodes = [n for n in levels if n]
    if value_nodes != levels[:len(value_nodes)]:
    return False
    else:
    print(len(levels), 2**level)
    if len(levels) != 2**level:
    if not last_full:
    return False
    last_full = False

    levels = [kid for n in levels if n for kid in (n.left, n.right)]
    level += 1
    return True
  • 方法二:遇见第一个None时,后面如果再有非None的值就不是玩整树了。

    1
    2
    3
    4
    5
    6
    7
    def isCompleteTree(self, root: 'TreeNode') -> 'bool':
    i, bfs = 0, [root]
    while bfs[i]:
    bfs.append(bfs[i].left)
    bfs.append(bfs[i].right)
    i += 1
    return not any(bfs[i:])

543. Diameter of Binary Tree

求二叉树的最大直径,即任意两节点的长度。
查看原题

1
2
3
4
5
6
          1
/ \
2 3
/ \
4 5
Return **3**, which is the length of the path [4,2,1,3] or [5,2,1,3].
  • recursively, 使用一个实例变量计算了最大值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def diameterOfBinaryTree(self, root: 'TreeNode') -> 'int':
    self.diameter = 0

    def dfs(node):
    if not node:
    return 0
    left = dfs(node.left)
    right = dfs(node.right)
    self.diameter = max(self.diameter, left+right)
    return max(left, right) + 1

    dfs(root)
    return self.diameter

965. Univalued Binary Tree

判断一个二叉树是否所有节点具有相同的值。
查看原题

  • 方法一:recursively。

    1
    2
    3
    4
    5
    def isUnivalTree(self, root: 'TreeNode') -> 'bool':
    def dfs(node):
    return (not node or root.val==node.val and
    dfs(node.left) and dfs(node.right))
    return dfs(root)
  • 方法二:iteratively.常规写法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def isUnivalTree(self, root: 'TreeNode') -> 'bool':
    r_val, stack = root.val, [root]
    while stack:
    n = stack.pop()
    if n:
    if n.val != r_val:
    return False
    stack.append(n.right)
    stack.append(n.left)
    return True
  • 方法三:前序遍历,生成器方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def isUnivalTree(self, root: 'TreeNode') -> 'bool':

    def bfs(node):
    if node:
    yield node.val
    yield from bfs(node.left)
    yield from bfs(node.right)

    it = bfs(root)
    root_val = next(it)
    for val in it:
    if val != root_val:
    return False
    return True

563. Binary Tree Tilt

返回一个二叉树整个树的倾斜度。所有节点倾斜度的总和。节点的倾斜度等于左子树和右子树所有和差的绝对值。
查看原题

1
2
3
4
5
6
7
8
9
10
Input: 
1
/ \
2 3
Output: 1
Explanation:
Tilt of node 2 : 0
Tilt of node 3 : 0
Tilt of node 1 : |2-3| = 1
Tilt of binary tree : 0 + 0 + 1 = 1
  • 方法一:recursively. 这里用tuple记录了节点总和和倾斜度总和。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def findTilt(self, root):
    self.res = 0
    _, top_res = self.sum_and_diff(root)
    return self.res + top_res

    def sum_and_diff(self, node):
    if not node:
    return 0, 0
    l_sum, l_diff = self.sum_and_diff(node.left)
    r_sum, r_diff = self.sum_and_diff(node.right)
    self.res += l_diff + r_diff
    return node.val+l_sum+r_sum, abs(l_sum-r_sum)
  • 方法二: 想了一会后序遍历的迭代法,没想出来,貌似需要维护很多的变量。这里还是优化一下方法一。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def findTilt(self, root: 'TreeNode') -> 'int':

    def dfs(node):
    if not node:
    return 0, 0
    l_sum, l_diff = dfs(node.left)
    r_sum, r_diff = dfs(node.right)
    return (node.val + l_sum + r_sum,
    abs(l_sum-r_sum) + l_diff + r_diff)

    return dfs(root)[1]

606. Construct String from Binary Tree

根据二叉树重建字符串,使用()表示嵌套关系。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Input: Binary tree: [1,2,3,4]
1
/ \
2 3
/
4
Output: "1(2(4))(3)"

Input: Binary tree: [1,2,3,null,4]
1
/ \
2 3
\
4
Output: "1(2()(4))(3)"
  • recursively. 左右节点有一点区别,在于如果左节点为空,右节点不为空,要保留左节点的括号。

    1
    2
    3
    4
    5
    def tree2str(self, t):
    if not t: return ''
    left = '({})'.format(self.tree2str(t.left)) if (t.left or t.right) else ''
    right = '({})'.format(self.tree2str(t.right)) if t.right else ''
    return '{}{}{}'.format(t.val, left, right)

617. Merge Two Binary Trees

合并两个二叉树,相同位置的节点值相加,空节点算0。
查看原题

  • 方法一:recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def mergeTrees(self, t1, t2):
    if not t1:
    return t2
    if not t2:
    return t1
    t = TreeNode(t1.val+t2.val)
    t.left = self.mergeTrees(t1.left, t2.left)
    t.right = self.mergeTrees(t1.right, t2.right)
    return t
  • 方法二:iteratively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def mergeTrees(self, t1, t2):
    if not t1 and not t2:
    return []
    t = TreeNode(0)
    stack = [(t, t1, t2)]
    while stack:
    n, n1, n2 = stack.pop()
    if n1 or n2:
    n.val = (n1.val if n1 else 0) + (n2.val if n2 else 0)
    if (n1 and n1.right) or (n2 and n2.right):
    n.right = TreeNode(None)
    stack.append((n.right, n1.right if n1 else None, n2.right if n2 else None))
    if (n1 and n1.left) or (n2 and n2.left):
    n.left = TreeNode(None)
    stack.append((n.left, n1.left if n1 else None, n2.left if n2 else None))
    return t

653. Two Sum IV - Input is a BST

判断二叉树中是否有两个节点相加为k。
查看原题

1
2
3
4
5
6
7
8
9
10
Input: 
5
/ \
3 6
/ \ \
2 4 7

Target = 9

Output: True
  • preorder + set.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def findTarget(self, root, k):
    seen, stack = set(), root and [root]
    while stack:
    node = stack.pop()
    if node:
    if k-node.val in seen:
    return True
    seen.add(node.val)
    stack.append(node.right)
    stack.append(node.left)
    return False

669. Trim a Binary Search Tree

根据范围修剪二叉搜索树,注意是二叉搜索树,不是普通的二叉树。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
Input: 
1
/ \
0 2

L = 1
R = 2

Output:
1
\
2
  • recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def trimBST(self, root, L, R):
    def trim_node(node):
    if not node:
    return None
    elif node.val > R:
    return trim_node(node.left)
    elif node.val < L:
    return trim_node(node.right)
    else:
    node.left = trim_node(node.left)
    node.right = trim_node(node.right)
    return node
    return trim_node(root)

671. Second Minimum Node In a Binary Tree

找出二叉树中第二小的节点值。左右子节点同时存在或同时不存在,根节点小于等于任意子节点。
查看原题

1
2
3
4
5
6
7
8
9
Input: 
2
/ \
2 5
/ \
5 7

Output: 5
Explanation: The smallest value is 2, the second smallest value is 5.
  • 方法一:先放到set里.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def findSecondMinimumValue(self, root: 'TreeNode') -> 'int':
    self.uniques = set()

    def dfs(node):
    if node:
    self.uniques.add(node.val)
    dfs(node.left)
    dfs(node.right)

    dfs(root)
    min1, ans = root.val, float('inf')
    for v in self.uniques:
    if min1 < v < ans:
    ans = v
    return ans if ans < float('inf') else -1
  • 方法二: iteratively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def findSecondMinimumValue(self, root):
    min1 = root.val if root else -1
    res = float('inf')
    stack = root and [root]
    while stack:
    node = stack.pop()
    if node:
    if min1 < node.val < res:
    res = node.val
    stack.extend([node.right, node.left])
    return res if res < float('inf') else -1

687. Longest Univalue Path

相同节点最长路径,路径长度按照两个节点之间的边距,也就是节点数-1。
查看原题

1
2
3
4
5
6
              5
/ \
4 5
/ \ \
1 1 5
output: 2
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def longestUnivaluePath(self, root):
    self.res = 0
    def traverse(node):
    if not node:
    return 0
    left_len, right_len = traverse(node.left), traverse(node.right)
    left = (left_len+1) if node.left and node.left.val==node.val else 0
    right = (right_len+1) if node.right and node.right.val==node.val else 0
    self.res = max(self.res, left + right)
    return max(left, right)
    traverse(root)
    return self.res

700. Search in a Binary Search Tree

在二叉搜索树中搜索节点。
查看原题

1
2
3
4
5
6
7
8
Given the tree:
4
/ \
2 7
/ \
1 3

And the value to search: 2
  • 方法一:recursively.

    1
    2
    3
    4
    5
    6
    def searchBST(self, root: 'TreeNode', val: 'int') -> 'TreeNode':
    if root:
    if val == root.val:
    return root
    return self.searchBST(
    (root.left, root.right)[root.val < val], val)
  • 方法二:iteratively.

    1
    2
    3
    4
    5
    def searchBST(self, root: 'TreeNode', val: 'int') -> 'TreeNode':
    node = root
    while node and node.val != val:
    node = (node.left, node.right)[node.val < val]
    return node

872. Leaf-Similar Trees

叶子相近的树,只从左到右遍历叶子节点的顺序相同的两棵树。
查看原题

  • 方法一:前序遍历+生成器。空间复杂度过高,beats 1%。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def leafSimilar(self, root1: 'TreeNode', root2: 'TreeNode') -> 'bool':

    def leaves(root):
    stack = root and [root]
    while stack:
    node = stack.pop()
    if node:
    if not node.right and not node.left:
    yield node.val
    stack.append(node.right)
    stack.append(node.left)

    leaves1 = leaves(root1)
    leaves2 = leaves(root2)
    return all(
    a==b for a, b in itertools.zip_longest(leaves1, leaves2))
  • 方法二:dfs.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def leafSimilar(self, root1: 'TreeNode', root2: 'TreeNode') -> 'bool':

    def dfs(node):
    if node:
    if not node.left and not node.right:
    yield node.val
    yield from dfs(node.left)
    yield from dfs(node.right)

    return all(
    a==b for a, b in itertools.zip_longest(dfs(root1), dfs(root2)))

897. Increasing Order Search Tree

根据中序遍历建立一个只有右子树的二叉树。要求在原树上修改。
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Example 1:
Input: [5,3,6,2,4,null,8,1,null,null,null,7,9]

5
/ \
3 6
/ \ \
2 4 8
/ / \
1 7 9

Output: [1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]

1
\
2
\
3
\
4
  • 方法一:iteratively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def increasingBST(self, root: TreeNode) -> TreeNode:
    ans = head = TreeNode(0)
    stack = []
    while stack or root:
    while root:
    stack.append(root)
    root = root.left
    root = stack.pop()
    head.right = TreeNode(root.val)
    root, head = root.right, head.right
    return ans.right
  • 方法二:生成器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def increasingBST(self, root: 'TreeNode') -> 'TreeNode':

    def inorder(node):
    if node:
    yield from inorder(node.left)
    yield node.val
    yield from inorder(node.right)

    ans = head = TreeNode(0)
    for v in inorder(root):
    head.right = TreeNode(v)
    head = head.right
    return ans.right
  • 方法三:题中有个要求在原树上修改,所以以上两种方法其实不符合要求,这里使用递归实现。

    1
    2
    3
    4
    5
    6
    def increasingBST(self, root: 'TreeNode', tail=None) -> 'TreeNode':
    if not root: return tail
    res = self.increasingBST(root.left, root)
    root.left = None
    root.right = self.increasingBST(root.right, tail)
    return res

993. Cousins in Binary Tree

表弟节点指两个节点在同一深度,并且父节点不同。判断两个节点是否是表弟节点。树中节点值唯一。
查看原题

  • 用dict记录。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def isCousins(self, root: 'TreeNode', x: 'int', y: 'int') -> 'bool':
    parent, depth = {}, {}

    def dfs(node, par=None):
    if node:
    parent[node.val] = par
    depth[node.val] = depth[par] + 1 if par else 0
    dfs(node.left, node.val)
    dfs(node.right, node.val)

    dfs(root)
    return depth[x] == depth[y] and parent[x] != parent[y]

230. Kth Smallest Element in a BST

二叉搜索树的第K小节点值。
查看原题

1
2
3
4
5
6
7
Input: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
Output: 1
  • 方法一:生成器前序遍历。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def kthSmallest(self, root: TreeNode, k: int) -> int:

    def inorder(node):
    if node:
    yield from inorder(node.left)
    yield node.val
    yield from inorder(node.right)

    for n in inorder(root):
    if k == 1:
    return n
    else:
    k -= 1
  • 方法二:迭代。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def kthSmallest(self, root: TreeNode, k: int) -> int:
    stack = []
    while root or stack:
    while root:
    stack.append(root)
    root = root.left
    root = stack.pop()
    k -= 1
    if k == 0:
    return root.val
    root = root.right

98. Validate Binary Search Tree

验证一个树是否是二叉搜索树。
查看原题

1
2
3
4
5
6
7
8
    5
/ \
1 4
/ \
3 6
Output: false
Explanation: The input is: [5,1,4,null,null,3,6]. The root node's value
is 5 but its right child's value is 4.
  • 中序遍历即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def isValidBST(self, root: TreeNode) -> bool:
    stack, last = [], float('-inf')
    while stack or root:
    while root:
    stack.append(root)
    root = root.left
    root = stack.pop()
    if root.val <= last:
    return False
    last = root.val
    root = root.right
    return True

109. Convert Sorted List to Binary Search Tree

将有序链表转成平衡二叉搜索树。
查看原题

1
2
3
4
5
6
7
8
9
Given the sorted linked list: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST:

0
/ \
-3 9
/ /
-10 5
  • 方法一:先遍历链表,再二分递归创建树。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def sortedListToBST(self, head: ListNode) -> TreeNode:
    inorder = []
    while head:
    inorder.append(head.val)
    head = head.next
    lo, hi = 0, len(inorder)-1

    def build_tree(lo, hi):
    if lo > hi:
    return None
    mid = (lo + hi) // 2
    root = TreeNode(inorder[mid])
    root.left = build_tree(lo, mid-1)
    root.right = build_tree(mid+1, hi)
    return root

    return build_tree(lo, hi)
  • 方法二:这个方法很棒。先遍历一遍找到链表的长度;然后递归去构建树,共享一个head可变对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def sortedListToBST(self, head: ListNode) -> TreeNode:

    def find_size(head):
    h, count = head, 0
    while h:
    h = h.next
    count += 1
    return count
    lo, hi = 0, find_size(head)

    def form_bst(lo, hi):
    nonlocal head
    if lo > hi:
    return None
    mid = (lo + hi) // 2
    left = form_bst(lo, mid-1)
    root = TreeNode(head.val)
    head = head.next
    root.left = left
    right = form_bst(mid+1, hi)
    root.right = right
    return root

    return form_bst(lo, hi-1)

1008. Construct Binary Search Tree from Preorder Traversal

根据前序遍历重建二叉搜索树。
查看原题

1
2
Input: [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]
  • recursively.

    1
    2
    3
    4
    5
    6
    7
    def bstFromPreorder(self, preorder: List[int]) -> TreeNode:
    if not preorder: return None
    root = TreeNode(preorder[0])
    i = bisect.bisect(preorder, root.val)
    root.left = self.bstFromPreorder(preorder[1:i])
    root.right = self.bstFromPreorder(preorder[i:])
    return root

236. Lowest Common Ancestor of a Binary Tree

二叉树两个节点的最小公共祖先。
查看原题

  • 方法一: 递归,是用mid表示当前节点是否是其中的一个。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    self.ans = None

    def dfs(node):
    if not node:
    return False
    left = dfs(node.left)
    right = dfs(node.right)
    mid = node in (p, q)
    if mid + left + right >= 2:
    self.ans = node
    return mid or left or right
    dfs(root)
    return self.ans
  • 方法二:递归,思想如果是两个节点中的一个,就返回这个节点。

    1
    2
    3
    4
    5
    6
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    if root in (None, p, q):
    return root
    left = self.lowestCommonAncestor(root.left, p, q)
    right = self.lowestCommonAncestor(root.right, p, q)
    return root if left and right else left or right
  • 方法三:参考了257的dfs解法。需要注意的是一定要加list(path),否则由于可变对象的问题,会导致最后结果为[]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
    ans = []
    def dfs(n, path):
    if n:
    path.append(n)
    if n in (p, q):
    ans.append(list(path)) # must use list, or you will get []
    if len(ans) == 2: # optimized
    return
    dfs(n.left, path)
    dfs(n.right, path)
    path.pop()
    dfs(root, [])
    return next(a for a, b in list(zip(*ans))[::-1] if a==b)

654. Maximum Binary Tree

根据数组建立一个树,要求根节点为数组最大的树。
查看原题

  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
    if not nums:
    return None
    v = max(nums)
    root = TreeNode(v)
    i = nums.index(v)
    root.left = self.constructMaximumBinaryTree(nums[:i])
    root.right = self.constructMaximumBinaryTree(nums[i+1:])
    return root

513. Find Bottom Left Tree Value

寻找二叉树最底层的最左节点。
查看原题

  • 方法一:根据分层遍历改编。

    1
    2
    3
    4
    5
    6
    def findBottomLeftValue(self, root: TreeNode) -> int:
    ans, levels = None, root and [root]
    while levels:
    ans = levels[0].val
    levels = [k for n in levels for k in (n.left, n.right) if k]
    return ans
  • 方法二:双端队列,BFS.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def findBottomLeftValue(self, root: TreeNode) -> int:
    q = collections.deque([root])
    while q:
    node = q.pop()
    if node.right:
    q.appendleft(node.right)
    if node.left:
    q.appendleft(node.left)
    return node.val
  • 方法三:循环时改变迭代对象,这种方式个人觉得不好。不过好在是在遍历之前添加到末端。

    1
    2
    3
    4
    5
    def findBottomLeftValue(self, root: TreeNode) -> int:
    queue = [root]
    for node in queue:
    queue += (x for x in (node.right, node.left) if x)
    return node.val

814. Binary Tree Pruning

剪掉树中不包含1的子树。
查看原题

  • recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def pruneTree(self, root: TreeNode) -> TreeNode:

    def dfs(node):
    if not node:
    return True
    left = dfs(node.left)
    right = dfs(node.right)
    if left:
    node.left = None
    if right:
    node.right = None

    return node.val==0 and left and right
    dfs(root)
    return root

199. Binary Tree Right Side View

二叉树从右向左看时,从上到下的节点。
查看原题

  • 方法一:和分层遍历思想相同。

    1
    2
    3
    4
    5
    6
    def rightSideView(self, root: TreeNode) -> List[int]:
    ans, levels = [], root and [root]
    while levels:
    ans.append(levels[-1].val)
    levels = [k for n in levels for k in (n.left, n.right) if k]
    return ans
  • 方法二:dfs. 从右到左深度遍历,用一个深度变量控制是否是第一个最右节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def rightSideView(self, root: TreeNode) -> List[int]:
    ans = []
    def dfs(n, depth):
    if n:
    if depth == len(ans):
    ans.append(n.val)
    dfs(n.right, depth+1)
    dfs(n.left, depth+1)
    dfs(root, 0)
    return ans

662. Maximum Width of Binary Tree

二叉树的最大宽度。
查看原题

  • 方法一:常规队列写法。需要注意的是,每层遍历要用最右边的减去最左边的才是宽度。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def widthOfBinaryTree(self, root: TreeNode) -> int:
    queue = [(root, 0, 0)]
    ans = cur_depth = left = 0
    for node, depth, pos in queue:
    if node:
    queue.append((node.left, depth+1, pos*2))
    queue.append((node.right, depth+1, pos*2+1))
    if cur_depth != depth:
    cur_depth = depth
    left = pos
    ans = max(pos-left+1, ans)
    return ans
  • 方法二:按照分层顺序将所有节点编号,从1开始,enumerate其实就是计算2*pos, 2*pos+1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def widthOfBinaryTree(self, root: TreeNode) -> int:
    levels = [(1, root)]
    width = 0
    while levels:
    width = max(levels[-1][0] - levels[0][0] + 1, width)
    levels = [k
    for pos, n in levels
    for k in enumerate((n.left, n.right), 2 * pos)
    if k[1]]
    return width

222. Count Complete Tree Nodes

统计完整树的节点个数。
查看原题

  • 二分法。比较左子树的深度和右子树的深度,如果相同则表明左子树为满树,右子树为完整树。如果不同则表明左子树为完整树,右子树为满树。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def countNodes(self, root: TreeNode) -> int:
    if not root:
    return 0
    left, right = self.depth(root.left), self.depth(root.right)
    if left == right:
    return 2 ** left + self.countNodes(root.right)
    else:
    return 2 ** right + self.countNodes(root.left)

    def depth(self, node):
    if not node:
    return 0
    return 1 + self.depth(node.left)

1022. Sum of Root To Leaf Binary Numbers

计算所有根到叶子节点路径二进制数表示的的和。
查看原题

1
2
3
Input: [1,0,1,0,1,0,1]
Output: 22
Explanation: (100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22
  • 思路和 257.Binary Tree Paths一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def sumRootToLeaf(self, root: TreeNode) -> int:
    self.ans = 0
    def dfs(n, path):
    if n:
    path.append(str(n.val))
    if not n.left and not n.right:
    self.ans += int(''.join(path), 2)
    dfs(n.left, path)
    dfs(n.right, path)
    path.pop()

    dfs(root, [])
    return self.ans % (10**9 + 7)

1026. Maximum Difference Between Node and Ancestor

祖先和其子节点的最大差绝对值。
查看原题

  • 方法一:周赛时写的dfs. 380ms. 瓶颈在于每次都求一次最大值和最小值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def maxAncestorDiff(self, root: TreeNode) -> int:
    self.ans = float('-inf')

    def dfs(n, p):
    if n:
    if p:
    max_diff = max(abs(max(p)-n.val), abs(min(p)-n.val))
    self.ans = max(self.ans, max_diff)
    p.append(n.val)
    dfs(n.left, p)
    dfs(n.right, p)
    p.pop()

    dfs(root, [])
    return self.ans
  • 方法二:改良了一下,使用p记录一个当前的最大值和最小值。52ms.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def maxAncestorDiff(self, root: TreeNode) -> int:
    self.ans = float('-inf')

    def dfs(n, p):
    if n:
    if p:
    mx, mn = p[-1]
    self.ans = max(self.ans, max(mx-n.val, n.val-mn))
    p.append((max(mx, n.val), min(mn, n.val)))
    else:
    p.append((n.val, n.val))
    dfs(n.left, p)
    dfs(n.right, p)
    p.pop()
    dfs(root, [])
    return self.ans

1038. Binary Search Tree to Greater Sum Tree

二叉搜索树转成一颗规则的树,从右根左的顺序累加节点值。
查看原题

  • 方法一:使用栈。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def bstToGst(self, root: TreeNode) -> TreeNode:
    head = root
    stack, total = [], 0
    while stack or root:
    while root:
    stack.append(root)
    root = root.right
    root = stack.pop()
    total += root.val
    root.val = total
    root = root.left
    return head
  • 方法二:Lee神的递归方式。

    1
    2
    3
    4
    5
    6
    7
    class Solution:
    val = 0
    def bstToGst(self, root: TreeNode) -> TreeNode:
    if root.right: self.bstToGst(root.right)
    root.val = self.val = self.val + root.val
    if root.left: self.bstToGst(root.left)
    return root

1080. Insufficient Nodes in Root to Leaf Paths

计算所有的根到叶子节点的路径,如果路径和小于给定值,则剪掉这个树枝。
查看原题

  • recursively.

    1
    2
    3
    4
    5
    6
    7
    8
    def sufficientSubset(self, root: TreeNode, limit: int) -> TreeNode:
    if not root:
    return None
    if not root.left and not root.right:
    return root if root.val >= limit else None
    root.left = self.sufficientSubset(root.left, limit-root.val)
    root.right = self.sufficientSubset(root.right, limit-root.val)
    return root if root.left or root.right else None

1161. Maximum Level Sum of a Binary Tree

求最节点和最大层的层数。
查看原题

  • 分层遍历

    1
    2
    3
    4
    5
    6
    7
    def maxLevelSum(self, root: TreeNode) -> int:
    lvsum = []
    level = [root]
    while level:
    lvsum.append(sum(n.val for n in level))
    level = [k for n in level for k in (n.left, n.right) if k]
    return lvsum.index(max(lvsum)) + 1

1104. Path In Zigzag Labelled Binary Tree

之字形树的目标节点路径。
查看原题

  • 方法一:迭代,此题纯粹是数学题,这里先假设非之字形的树,找到规律,然后知道每层的节点数再相减。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def pathInZigZagTree(self, label: int) -> List[int]:

    ans = []
    n = 0
    while 2 ** n <= label:
    n += 1

    while n > 0 and label >= 1:
    ans.append(label)
    org_lable = label // 2
    label = 2**(n-1)-1-org_lable+2**(n-2)
    n -= 1
    return ans[::-1]
  • 方法二:Lee神的递归。原理一样,层数n是通过查2的幂求的。

    1
    2
    def pathInZigZagTree(self, x):
    return self.pathInZigZagTree(3 * 2 ** (len(bin(x)) - 4) - 1 - x / 2) + [x] if x > 1 else [1]

1110. Delete Nodes And Return Forest

给定一个树,删除指定的一些节点,然后删除的节点的左右子树成为单独的根节点。返回所有的树。
查看原题

1
2
Input: root = [1,2,3,4,5,6,7], to_delete = [3,5]
Output: [[1,2,null,4],[6],[7]]
  • 递归。做着题的时候有个误区:在当前节点被删除后,找到其在父节点对应的位置,然后置为空。实际上应该讲根节点删除的状态保留,在下一层处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def delNodes(self, root: TreeNode, to_delete: List[int]) -> List[TreeNode]:
    ans = []
    to_del = set(to_delete)

    def helper(root, is_root):

    if not root:
    return None
    is_del = root.val in to_del
    root.left = helper(root.left, is_del)
    root.right = helper(root.right, is_del)
    if not is_del and is_root:
    ans.append(root)
    return None if is_del else root

    helper(root, True)
    return ans

在单调中寻找,在起伏中失效

关于二分法

单调区间中寻找特定元素的高效算法。

使用核心

  • 区间单调

    • 在单调模型上求目标解,非单调模型不可使用
  • 时间效率O(logn)

  • 核心代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def binarysearch(array, target):
    head = 0
    tail = len(array) - 1
    while head < tail:
    mid = (head + tail) >> 1
    if array[mid] < target:
    head = mid + 1
    else:
    tail = mid
    return head

技巧

  • 题意反推

    很多需要用二分法的题目,会在数据范围上暴露信息。

    比如(0 < M <= 100000000)
    这种数据范围一般会和时间复杂度为O(logn)的算法有关系,
    快排堆排线段树等等。

LeetCode真题

用二分法在有序数组中查找元素。
查看原题

1
2
3
Input: nums = [-1,0,3,5,9,12], target = 9
Output: 4
Explanation: 9 exists in nums and its index is 4
  • 方法一:实现原理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def binary_search(nums, target):
    l, r = 0, len(nums)-1
    while l <= r:
    mid = (l+r) // 2
    if nums[mid] > target:
    r = mid - 1
    elif nums[mid] < target:
    l = mid + 1
    else:
    return mid
    return -1
  • 方法二:使用标准库。

    1
    2
    3
    4
    def search(self, nums, target):
    from bisect import bisect_left
    index = bisect_left(nums, target)
    return index if index < len(nums) and nums[index] == target else -1

35. Search Insert Position

给定一个target,插入到一个有序数组中,假定数组中无重复元素。
查看原题

1
2
Input: [1,3,5,6], 5
Output: 2
  • 方法一:实现原理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def binary_insert(nums, target):
    l, r = 0, len(nums)-1
    while l <= r:
    mid = (l+r) // 2
    if nums[mid] > target:
    r = mid - 1
    elif nums[mid] < target:
    l = mid + 1
    else:
    return mid
    return l
  • 方法二:使用标准库。

    1
    2
    3
    def searchInsert(self, nums, target):
    from bisect import bisect_left
    return bisect_left(nums, target)

153. Find Minimum in Rotated Sorted Array

通过一个排序数组旋转后的结果,找出最小元素。
查看原题

1
2
Input: [3,4,5,1,2] 
Output: 1
  • 思路:通过二分法不断缩小范围,由于mid是整除,最后l==mid,并且nums[mid] > nums[r]的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def find_min(nums):
    l, r = 0, len(nums)-1
    if nums[l] < nums[r]:
    return nums[l]
    while l <= r:
    mid = (l+r) // 2
    if nums[mid] > nums[l]:
    l = mid
    elif nums[mid] < nums[r]:
    r = mid
    else:
    return nums[r]

34. Find First and Last Position of Element in Sorted Array

有序数组中查找数组,返回数字的索引范围。
查看原题

1
2
3
4
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def searchRange(self, nums, target):

    left_idx = self.search_edge(nums, target, True)
    if left_idx == len(nums) or nums[left_idx] != target:
    return [-1, -1]
    return [left_idx, self.search_edge(nums, target, False)-1]

    def search_edge(self, nums, target, left):
    l, r = 0, len(nums)
    while l < r:
    mid = (l+r) // 2
    if nums[mid] > target or (left and nums[mid]==target):
    r = mid
    else:
    l = mid + 1
    return l

278. First Bad Version

找出提交版本中的bad version。
查看原题

1
2
3
4
5
6
7
Given n = 5, and version = 4 is the first bad version.

call isBadVersion(3) -> false
call isBadVersion(5) -> true
call isBadVersion(4) -> true

Then 4 is the first bad version.
  • 解法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def firstBadVersion(self, n):
    l, r = 1, n
    while l <= r:
    mid = (l+r) // 2
    if isBadVersion(mid):
    r = mid - 1
    else:
    l = mid + 1
    return l

374. Guess Number Higher or Lower

猜数游戏1~n,每猜一次会告诉你答案是更小还是更大。
查看原题

1
2
3
4
5
6
7
8
def guess(num):
return
-1 : My number is lower
1 : My number is higher
0 : Congrats! You got it!

Input: n = 10, pick = 6
Output: 6
  • 方法一:实现原理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def guessNumber(self, n):
    l, r = 1, n
    while l <= r:
    mid = (l+r) // 2
    if guess(mid) == -1:
    r = mid - 1
    elif guess(mid) == 1:
    l = mid + 1
    else:
    return mid
  • 方法二:使用标准库。核心思想为将guess返回的结果转为一个数组,然后使用二分法查找。

    1
    2
    3
    4
    5
    6
    7
    def guessNumber(self, n):
    from bisect import bisect, bisect_left
    class C:
    def __getitem__(self, x):
    return -guess(x)
    # return bisect(C(), -1, 1, n)
    return bisect_left(C(), 0, 1, n)

    解析:以n=10, pick=6为例。实际上C class相当于:

    1
    2
    3
    4
    ary = map(lambda x: -guess(x), range(1, n+1))
    ary.insert(0, None)
    # ary = [None, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1]
    return bisect(ary, -1, 1, n)

    而索引又是从1开始,所以这里在前面添加了一个None,实际上将题转为了查找ary的0,问题便迎刃而解。
    值得注意的是,如果使用了map,会导致空间,时间复杂度增加,而使用class的方法,并没有求出整个的list,
    所以效率更高。

744. Find Smallest Letter Greater Than Target

找出比目标大的最小字母,没有的返回首字母
查看原题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Input:
letters = ["c", "f", "j"]
target = "d"
Output: "f"

Input:
letters = ["c", "f", "j"]
target = "g"
Output: "j"

Input:
letters = ["c", "f", "j"]
target = "j"
Output: "c"
  • 方法一:实现原理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def nextGreatestLetter(self, letters: 'List[str]', target: 'str') -> 'str':
    lo, hi = 0, len(letters)-1
    while lo <= hi:
    mid = (lo + hi) // 2
    if letters[mid] > target:
    hi = mid -1
    elif letters[mid] <= target:
    lo = mid + 1
    return letters[lo % len(letters)]
  • 方法二:使用库。

    1
    2
    3
    def nextGreatestLetter(self, letters: 'List[str]', target: 'str') -> 'str':
    index = bisect.bisect(letters, target)
    return letters[index % len(letters)]

852. Peak Index in a Mountain Array

找到数组中的峰值。假设峰值一定存在。
查看原题

1
2
Input: [0,2,1,0]
Output: 1
  • 方法一:线性枚举O(n)。

    1
    2
    3
    4
    def peakIndexInMountainArray(self, A: 'List[int]') -> 'int':
    for i in range(1, len(A)-1):
    if A[i] > A[i+1]:
    return i
  • 方法二:max函数

    1
    2
    def peakIndexInMountainArray(self, A: 'List[int]') -> 'int':
    return A.index(max(A))
  • 方法三:二分法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def peakIndexInMountainArray(self, A: 'List[int]') -> 'int':
    lo, hi = 0, len(A)-1
    while lo < hi:
    mid = (lo + hi) // 2
    if A[mid] > A[mid+1]:
    hi = mid
    else:
    lo = mid + 1
    return lo
  • 方法四:黄金分割法,应用在单峰函数求极值,速度比二分法要快。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def peakIndexInMountainArray(self, A: 'List[int]') -> 'int':

    def gold1(i, j):
    return i + int(round((j-i) * 0.382))
    def gold2(i, j):
    return i + int(round((j-i) * 0.618))

    l, r = 0, len(A) - 1
    x1, x2 = gold1(l, r), gold2(l, r)
    while x1 < x2:
    if A[x1] < A[x2]:
    l = x1
    x1 = x2
    x2 = gold1(x1, r)
    else:
    r = x2
    x2 = x1
    x1 = gold2(l, x2)
    return x1

1014. Capacity To Ship Packages Within D Days

n天内轮船运送的最小容量
查看原题

1
2
3
4
5
6
7
8
9
10
11
Input: weights = [1,2,3,4,5,6,7,8,9,10], D = 5
Output: 15
Explanation:
A ship capacity of 15 is the minimum to ship all the packages in 5 days like this:
1st day: 1, 2, 3, 4, 5
2nd day: 6, 7
3rd day: 8
4th day: 9
5th day: 10

Note that the cargo must be shipped in the order given, so using a ship of capacity 14 and splitting the packages into parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed.
  • 二分结果快速出解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def shipWithinDays(self, weights: List[int], D: int) -> int:
    lo, hi = max(weights), sum(weights)
    while lo <= hi:
    mid, days, cur = (lo + hi) // 2, 1, 0
    for w in weights:
    if cur+w > mid:
    days += 1
    cur = 0
    cur += w
    if days > D:
    lo = mid + 1
    else:
    hi = mid - 1
    # print(lo, mid, hi)
    return lo

875. Koko Eating Bananas

这道题思路和1014一样。不同的是,如果当前堆的香蕉小于吃的速度,那么也不能吃下一堆。
查看原题

1
2
Input: piles = [3,6,7,11], H = 8
Output: 4
  • 二分结果快速出解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def minEatingSpeed(self, piles: List[int], H: int) -> int:
    lo, hi = 1, max(piles)
    while lo <= hi:
    mid = (lo + hi ) >> 1
    # needs = sum(math.ceil(p/mid) for p in piles) # slower
    needs = sum((p-1)//mid+1 for p in piles)
    if needs > H:
    lo = mid + 1
    else:
    hi = mid - 1
    return lo

1145. Binary Tree Coloring Game

二叉树染色游戏。两个人轮流给二叉树染色,每次只能染相邻位的节点,给定第一个人染色的位置,问第二个人是否能够必胜。
查看原题

1
2
Input: piles = [3,6,7,11], H = 8
Output: 4
  • 关键的一点需要想明白,从第一个人染色的地方,有三个分支,如果有一个分支可以大于整个节点的一半,那么第二个人选择这个分支,就能赢得比赛

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def btreeGameWinningMove(self, root: TreeNode, n: int, x: int) -> bool:
    count = [0, 0]

    def dfs(node):
    if not node:
    return 0
    left = dfs(node.left)
    right = dfs(node.right)
    if node.val == x:
    count[0] = left
    count[1] = right
    return left + right + 1

    dfs(root)
    return max(max(count), n - sum(count) - 1) > n // 2