- 41 - First Missing Positive
- 48 - Rotate Image
- 53 - Maximum Subarray
- 55 - Jump Game
- 62 - Unique Paths
- 64 - Minimum Path Sum
- 70 - Climbing Stairs
- 91 - Decode Ways
- 509 - Fibonacci Number
- 75 - Sort Colors
- 78 - Subsets
- 79 - Word Search
- 45 - Jump Game II
- 1306 - Jump Game III
- 84 - Largest Rectangle in Histogram
- 85 - Maximal Rectangle
- 121 - Best Time to Buy and Sell Stock
- 122 - Best Time to Buy and Sell Stock II
- 123 - Best Time to Buy and Sell Stock III
- 188 - Best Time to Buy and Sell Stock IV
- 309 - Best Time to Buy and Sell Stock with Cooldown
- 104 - Maximum Depth of Binary Tree
- 21 - Merge Two Sorted Lists
- 101 - Symmetric Tree
- 198 - House Robber
- 300 - Longest Increasing Subsequence
- 322 - Coin Change
- 152 - Maximum Product Subarray
- 96 - Unique Binary Search Trees
- 221 - Maximal Square
- 279 - Perfect Squares
- 647 - Palindromic Substrings
- 5 - Longest Palindromic Substring
- 136 - Single Number
- 202 - Happy Number
- 338 - Counting Bits
- 494 - Target Sum
- 283 - Move Zeros
- 416 - Partition Equal Subset Sum
- 698 - Partition to K Equal Sum Subsets
- 215 - Kth Largest Element in an Array
- 49 - Group Anagrams
- 876 - Middle of the Linked List
- 844 - Backspace String Compare
- 155 - Min Stack
- 543 - Diameter of Binary Tree
- 1046 - Last Stone Weight
- 525 - Contiguous Array
- Perform String Shifts
- 238 - Product of Array Except Self
- 678 - Valid Parenthesis String
- 200 - Number of Islands
- 46 - Permutations
- 47 - Permutations II
- 1008 - Construct Binary Search Tree from Preorder Traversal
- 105 - Construct Binary Tree from Preorder and Inorder Traversal
- Leftmost Column with at Least a One
- 560 Subarray Sum Equals K
- 201 - Bitwise AND of Numbers Range
- 146 - LRU Cache
- 460 - LFU Cache
- 437 - Path Sum III
- 1143 - Longest Common Subsequence
- 887 - Super Egg Drop
- 28 - Implement strStr() (KMP Algorithm)
- 169 - Majority Element
- 448 - Find All Numbers Disappeard in an Array
- 124 - Binary Tree Maximum Path Sum
- 278 - First Bad Version
- 106 - Construct Binary Tree from Inorder and Postorder Traversal
- 128 - Longest Consecutive Sequence
- 98 - Validate Binary Search Tree
- 1009 - Complement of Base 10 Integer
- 337 - House Robber III
- Cousins in Binary Tree
- 1232 - Check If It Is a Straight Line
- 733 - Flood Fill
- 997 - Find the Town Judge
- 540 - Single Element in a Sorted Array
- 402 - Remove K Digits
- 208 - Implement Trie (Prefix Tree)
- 918 - Maximum Sum Circular Subarray
- 328 - Odd Even Linked List
- 438 - Find All Anagrams in a String
- 567 - Permutation in String
- 901 - Onine Stock Span
- 230 - Kth Smallest Element in a BST
- 1277 - Count Square Submatrices with All Ones
- 76 - Minimum Window Substring
- 451 - Sort Characters By Frequency
- 986 - Interval List Intersections
- 1035 - Uncrossed Lines
- 886 - Possible Bipartition
- 207 - Course Schedule
- 210 - Course Schedule II
- 973 - K Closest Points to Origin
- 72 - Edit Distance
- 226 - Invert Binary Tree
- 746 - Min Cost Climbing Stairs
- 237 - Delete Node in a Linked List
- 1029 - Two City Scheduling
- 528 - Random Pick with Weight
- 518 - Coin Change 2
- 231 - Power of Two
- 406 - Queue Reconstruction by Height
- 213 - House Robber II
- 38 - Count and Say
- 66 - Plus One
- 392 - Is Subsequence
- 40 - Combination Sum II
- 39 - Combination Sum
- 377 - Combination Sum IV
- 216 - Combination Sum III
- 47 - Permutations II
- 51 - N-Queens
- 52 - N-Queens II
- 77 - Combinations
- 90 - Subsets II
- 100 - Same Tree
- 112 - Path Sum
- 113 - Path Sum II
- 118 - Pascal’s Triangle
- 54 - Spiral Matrix
- 58 - Length of Last Word
- 59 - Spiral Matrix II
- 61 - Rotate List
- 63 - Unique Paths II
- 74 - Search a 2D Matrix
- 162 - Find Peak Element
- 83 - Remove Duplicates from Sorted List
- 95 - Unique Binary Search Trees II
- 222 - Count Complete Tree Nodes
- 130 - Surrounded Regions
- 129 - Sum Root to Leaf Numbers
- 988 - Smallest String Starting From Leaf
- 303 - Range Sum Query - Immutable
- 264 - Ugly Number II
- 263 - Ugly Number
- 332 - Reconstruct Itinerary
- 212 - Word Search II
- 441 - Arranging Coins
- 107 - Binary Tree Level Order Traversal II
- 463 - Island Perimeter
- 695 - Max Area of Island
- 15 - 3SUM
- 10 - Regular Expression Matching
- 37 - Sudoku Solver
- 36 - Valid Sudoku
- 190 - Reverse Bits
- 461 - Hamming Distance
- 32 - Longest Valid Parentheses
- 71 - Simplify Path
- 73 - Set Matrix Zeroes
- 80 - Remove Duplicates from Sorted Array II
- 81 - Search in Rotated Sorted Array II
- 191 - Number of 1 Bits
- 82 - Remove Duplicates from Sorted List II
- 240 - Search a 2D Matrix II
- 1103 - Distribute Candies to People
- 148 - Sort List
- 142 - Reorder List
- 967 - Numbers With Same Consecutive Differences
- 97 - Interleaving String
- 108 - Convert Sorted Array to Binary Search Tree
- 109 - Convert Sorted List to Binary Search Tree
- 110 - Balanced Binary Tree
- 111 - Minimum Depth of Binary Tree
- 824 - Goat Latin
- 114 - Flatten Binary Tree to Linked List
- 368 - Largest Divisible Subset
- 784 - Letter Case Permutation
- 1079 - Letter Tile Possibilities
- 516 - Longest Palindromic Subsequence
- 1032 - Stream of Characters
- 199 - Binary Tree Right Side View
- 1219 - Path with Maximum Gold
- 404 - Sum of Left Leaves
- 131 - Palindrome Partitioning
- 132 - Palindrome Partitioning II
- 983 - Minimum Cost For Tickets
- 412 - Fizz Buzz
- 1286 - Iterator for Combination
- 93 - Restore IP Addresses
- 436 - Find Right Interval
- 980 - Unique Paths III
- 450 - Delete Node in a BST
- 526 - Beautiful Arrangement
- 354 - Russian Doll Envelopes
- 403 - Frog Jump
- 376 - Wiggle Subsequence
- 86 - Partition List
- 378 - Kth Smallest Element in a Sorted Matrix
- 115 - Distinct Subsequences
- 1392 - Longest Happy Prefix
- 395 - Longest Substring with At Least K Repeating Characters
- 127 - Word Ladder
- 126 - Word Ladder II
- 257 - Binary Tree Paths
- 691 - Stickers to Spell Word
- 797 - All Paths From Source to Target
- 464 - Can I Win
- 116 - Populating Next Right Pointers in Each Node
- 295 - Find Median from Data Stream
- 996 - Number of Squareful Arrays
- 842 - Split Array into Fibonacci Sequence
- 486 - Predict the Winner
- 556 - Next Greater Element III
- 381 - Insert Delete GetRandom O(1) - Duplicates allowed
- 134 - Gas Station
- 135 - Candy
- 140 - Word Break II
- 139 - Word Break
- 443 - String Compression
- 30 - Substring with Concatenation of All Words
- 1010 - Pairs of Songs With Total Durations Divisible by 60
- 235 - Lowest Common Ancestor of a Binary Search Tree
- 701 - Insert into a Binary Search Tree
- 323 - Number of Connected Components in an Undirected Graph
- 863 - All Nodes Distance K in Binary Tree
- 515 - Find Largest Value in Each Tree Row
- 42 - Trapping Rain Water
- 236 - Lowest Common Ancestor of a Binary Tree
41 - First Missing Positive
Problem
Given an unsorted integer array, find the smallest missing positive integer.
Example 1:
Input: [1,2,0]
Output: 3
Example 2:
Input: [3,4,-1,1]
Output: 2
Example 3:
Input: [7,8,9,11,12]
Output: 1
Note:
Your algorithm should run in O(n) time and uses constant extra space.
Notes
Run in O(n) time and uses constant extra space
-
Say the length of the array is l, the number must be in 1…l+1 (also l possible numbers)
For example
[1, 2, 3, 4], the first missing positive is 5.
[7, 8, 9, 10], the first missing positive is 1
It means you can use the array as a constant space. The result must be (one of the indexes + 1).
-
We put the number in the right place. When it is 10, we swap it with A[9].
After all the numbers are in the right place, the first one, whose index + 1 != number, it is the missing one
How to put the numer in the right place
Use the `while` to swap the numbers. Only `if` can not do the same job.
Consider nums = [3, 4, -1, 1].
Only with if:
First Loop: swap 3 and -1
nums = [-1, 4, 3, 1]
Second Loop: swap 4 and 1
nums = [-1, 1, 3, 4]
And the process stops. Because 4 is already in the right place. You miss to put the 1 in the right place.
So you have to do it recursively, with `while`.
Solution
class Solution(object):
def firstMissingPositive(self, nums):
l = len(nums)
for i in range(l):
# Note!: here has to be using while
while (nums[i] > 0 and nums[i] <= l and nums[nums[i] - 1] != nums[i]):
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
for i, n in enumerate(nums):
if (n != i+1):
return i + 1
return l + 1
48 - Rotate Image
Problem
You are given an n x n 2D matrix representing an image.
Rotate the image by 90 degrees (clockwise).
Note:
You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
Example 1:
Given input matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
rotate the input matrix in-place such that it becomes:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
Example 2:
Given input matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
rotate the input matrix in-place such that it becomes:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
Notes
Naive solution, to do it one by one.
Important:
You go from the outside into the middle. So the main loop is half of the dimension.
The inner loop should also shrink its size everytime. Begins at i and ends and n-2-i, not n-1-i.
Because you don’t want to swap the last one. The last one n-1-i has already been swapped with the i.
Another solution: how to rotate a matrix faster
Swap the diagnoal elements and reverse each line in the matrix.
1 | 2 | 3 | swap | 1 | 4 | 7 | reverse | 7 | 4 | 1 |
---|---|---|---|---|---|---|---|---|---|---|
4 | 5 | 6 | —> | 2 | 5 | 8 | ——> | 8 | 5 | 2 |
7 | 8 | 9 | 3 | 6 | 9 | 9 | 6 | 3 |
Solution
Solution 1: Straightforward
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
for i in range(n//2):
# Shrink the dimension
# Do not include the last element
for j in range(i, n-i-1):
tmp = matrix[i][j]
matrix[i][j] = matrix[n-1-j][i]
matrix[n-1-j][i] = matrix[n-1-i][n-1-j]
matrix[n-1-i][n-1-j] = matrix[j][n-1-i]
matrix[j][n-1-i] = tmp
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Solution().rotate(matrix)
[print(*line) for line in matrix]
Solution 2:
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
for i in range(n):
for j in range(i, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
for i in range(n):
matrix[i].reverse()
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Solution().rotate(matrix)
[print(*line) for line in matrix]
53 - Maximum Subarray
Problem
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
Notes
Dynamic programming problem.
Use nums[i] always store the maximum sum.
maxSum(i) = maxSum(i-1) + nums[i] only if maxSum(i-1) > 0
Solution
Solution 1: use a extra dp array
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
curSum = maxSum = nums[0]
for num in nums[1:]:
curSum = max(num, curSum+num)
maxSum = max(curSum, maxSum)
return maxSum
print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))
Solution 2: no extra space, in place modify
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 0:
return 0
ret = nums[0]
for i in range(1, len(nums)):
if nums[i - 1] > 0:
nums[i] += nums[i - 1]
ret = max(ret, nums[i])
return ret
print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))
55 - Jump Game
Problem
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
jump length is 0, which makes it impossible to reach the last index.
Notes
Greedy algorithm. There are 2 approaches, from head or from tail.
Start from head
always remember the furthest reachable index.
reach = max(i + nums[i], reach) if i <= reach
Start from tail
always remember the last position it can reach.
lastPos = i if i + nums[i] >= lastPos
Solition
Solution 1: start from head
class Solution():
def canJump(self, nums):
reach = 0
for i in range(len(nums)):
if i <= reach:
reach = max(i + nums[i], reach)
else:
break
return reach >= len(nums) - 1
print(Solution().canJump([ 2,3,1,1,4 ]))
print(Solution().canJump([ 3,2,1,0,4 ] ))
Solution 2: start from tail
class Solution():
def canJump(self, nums):
lastPos = len(nums) - 1
for i in reversed(range(len(nums))):
if i + nums[i] >= lastPos:
lastPos = i
return lastPos == 0
print(Solution().canJump([ 2,3,1,1,4 ]))
print(Solution().canJump([ 3,2,1,0,4 ] ))
62 - Unique Paths
Problem
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
How many possible unique paths are there?
Note: m and n will be at most 100.
Example 1:
Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right
Example 2:
Input: m = 7, n = 3
Output: 28
Notes
It is a DP problem.
-
There are only two possibilities to arrive at the finish point
- arrive at that point from above
- arrive at that point from left
-
So the ways to arrive at current point is equal to the ways from above plus the ways from left. dp[i][j] = dp[i][j-1] + dp[i - 1][j]
-
Dynamic programming. Maintain a two dimensional matrix.
-
Optimize it to one dimension.
-
Complexity O(m * n)
UniquePath with 2-D DP
dp[i][j] = dp[i-1][j] + dp[i][j-1]
After you have the edge, you go levelly to the bottom.
UniquePath with 1-D DP
dp[j] = dp[j - 1] + dp[j]
Solution
Solution 1: 2-D DP
class Solution():
def uniquePath(self, m, n):
dp = [[1 for j in range(n)] for i in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
return dp[-1][-1] if m and n else 0
Solution 2: 1-D DP
class Solution():
def uniquePath(self, m, n):
dp = [1] * n
for i in range(1, m):
for j in range(1, n):
dp[j] = dp[j - 1] + dp[j]
return dp[-1] if m and n else 0
64 - Minimum Path Sum
Problem
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Example:
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
Notes
Thinking: It seems to be a greedy algorithm problem.
It is a dp problem.
dp equation:
dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j]
Remember to handle the edge cases.
Solution
Solution 1: 2D DP
- Inplace DP
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
for i in range(m):
for j in range(n):
if i == 0:
if j == 0:
continue
else:
grid[i][j] += grid[i][j-1]
else:
if j == 0:
grid[i][j] += grid[i-1][j]
else:
grid[i][j] += min(grid[i-1][j], grid[i][j-1])
return grid[-1][-1] if m and n else 0
grid = [[1,3,1],[1,5,1],[4,2,1]]
print(Solution().minPathSum(grid))
- Additional DP with auxiliary columns
import sys
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
dp = [[ 0 for i in range(n+1)] for j in range(m+1)]
for i in range(len(dp)):
dp[i][0] = sys.maxsize
for i in range(len(dp[0])):
dp[0][i] = sys.maxsize
dp[1][1] = grid[0][0]
for i in range(1, m+1):
for j in range(1, n+1):
if i == 1 and j == 1:
continue
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i-1][j-1]
return dp[-1][-1] if m and n else 0
grid = [[1,3,1],[1,5,1],[4,2,1]]
print(Solution().minPathSum(grid))
Solution 2: 1D DP
import sys
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
dp = [sys.maxsize for i in range(n + 1)]
dp[1] = grid[0][0]
for i in range(m):
for j in range(n):
if i == 0 and j == 0:
continue
else:
dp[j + 1] = min(dp[j + 1], dp[j]) + grid[i][j]
return dp[-1] if m and n else 0
grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]]
print(Solution().minPathSum(grid))
70 - Climbing Stairs
Problem
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
Example 1:
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:
Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
Notes
The distinct ways to take n stair cases:
- take one step at last, the distinct ways to take n-1 stair cases -> f(n-1) ways
- take two steps at last, the distinct ways to take n-2 stair cases -> f(n-2) ways
So f(n) = f(n-1) + f(n-2)
Solution
class Solution():
def climbStairs(self, n):
if n < 2:
return n
dp = [0] * n
dp[0] = 1
dp[1] = 2
for i in range(2, n):
dp[i] = dp[i-1] + dp[i-2]
return dp[-1]
91 - Decode Ways
Problem
A message containing letters from A-Z is being encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given a non-empty string containing only digits, determine the total number of ways to decode it.
Example 1:
Input: "12"
Output: 2
Explanation: It could be decoded as "AB" (1 2) or "L" (12).
Example 2:
Input: "226"
Output: 3
Explanation: It could be decoded as "BZ" (2 26), "VF" (22 6), or "BBF" (2 2 6).
Notes
DP problem.
- Initialize dp array with dp[0] = 1 as padding, the rest of them are 0.
- Start from the first index of the string.
- If dp[i] is in range 1 to 9, dp[i] = dp[i-1]. The ways of decode will not increase, if it is 0, it remains 0.
- If dp[i] is in range 10 to 26, dp[i] += dp[i-1]. The ways of decode increase by one, if it is 00, it remains 0.
Important:
- Corner cases: “0” -> 0, “1002” -> 0
- Notice the padding
Solution
class Solution():
def numsDecodings(self, s):
if not s:
return 0
n = len(s)
dp = [0] * (n + 1)
dp[0] = 1
for i in range(1, n+1):
if s[i-1: i] != '0':
dp[i] = dp[i-1]
if i != 1 and '10' <= s[i-2:i] <= '26':
dp[i] += dp[i-2]
return dp[-1]
509 - Fibonacci Number
Problem
The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is,
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), for N > 1.
Given N, calculate F(N).
Example 1:
Input: 2
Output: 1
Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1.
Example 2:
Input: 3
Output: 2
Explanation: F(3) = F(2) + F(1) = 1 + 1 = 2.
Example 3:
Input: 4
Output: 3
Explanation: F(4) = F(3) + F(2) = 2 + 1 = 3.
Notes
DP Problem.
Important:
Note how long is the dp array. It shoud be N+1, since we start with the number 0.
Solution
class Solution(object):
def fib(self, N):
"""
:type N: int
:rtype: int
"""
if N < 2:
return N
dp = [0] * (N + 1)
dp[0] = 0
dp[1] = 1
for i in range(2, N + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[-1]
75 - Sort Colors
Problem
Given an array with n objects colored red, white or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note: You are not suppose to use the library's sort function for this problem.
Example:
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Follow up:
A rather straight forward solution is a two-pass algorithm using counting sort.
First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's.
Could you come up with a one-pass algorithm using only constant space?
Notes
First attempt is to use two pointer.
There is a but a corner case: when two point
class Solution:
def subsets(self, nums):
res = [[]]
for i in sorted(nums):
res += [item+[i] for item in res]
return res
print(Solution().subsets([1, 2, 3]))
78 - Subsets
Problem
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
Solution
Solution 1: DFS
class Solution(object):
def subsets(self, nums):
res = []
self.dfs(sorted(nums), 0, [], res)
return res
def dfs(self, nums, index, path, res):
res.append(path)
for i in range(index, len(nums)):
self.dfs(nums, i + 1, path + [nums[i]], res)
Solution 2: Iterative
class Solution:
def subsets(self, nums):
res = [[]]
for i in sorted(nums):
res += [item+[i] for item in res]
return res
Solution 3: backtrack
class Solution(object):
def subsets(self, nums):
output = []
for k in range(0, len(nums)+1):
temp = []
self.backtrack(0, k, nums, temp, output)
return output
def backtrack(self, begin, length, nums, temp, output):
if length == len(temp):
output.append(temp[:])
for i in range(begin, len(nums)):
temp.append(nums[i])
self.backtrack(i+1, length, nums, temp, output)
temp.pop()
print(Solution().subsets([1, 2, 3]))
Solution 4: bitmask
class Solution():
def subsets(self, nums):
n = len(nums)
output = []
for i in range(2**n, 2**(n+1)):
bitmask = bin(i)[3:]
output.append([nums[i] for i in range(n) if bitmask[i] == '1' ])
return output
79 - Word Search
Problem
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
Example:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
Given word = "ABCCED", return true.
Given word = "SEE", return true.
Given word = "ABCB", return false.
Notes
Backtrack problem.
- when found, mark the point to one.
- Use dfs to go down from this point.
- Can’t go down anymore, mark the point back to zero (backtrack step).
Solution
Solution 1: Backtrack, dfs
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
for i in range(len(board)):
for j in range(len(board[0])):
if self.dfs(board, word, 0, i, j):
return True
return False
def dfs(self, board, word, w, i, j):
if w == len(word):
return True
if i < 0 or j < 0 or i >= len(board) or j >= len(board[0]):
return False
if board[i][j] == word[w]:
board[i][j] = '-'
for next_i, next_j in [[i + 1, j], [i - 1, j], [i, j - 1],
[i, j + 1]]:
if self.dfs(board, word, w + 1, next_i, next_j):
return True
board[i][j] = word[w]
return False
45 - Jump Game II
Problem
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Your goal is to reach the last index in the minimum number of jumps.
Example:
Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
Jump 1 step from index 0 to 1, then 3 steps to the last index.
Note:
You can assume that you can always reach the last index.
Notes
Greedy problem.
- [begin, end], go through values in between, have one furthest reach.
- current index reach to end, jump once.
- next interval is [end, reach]
Solution
#+begin_src python class Solution: def jump(self, nums):
jumps = 0 curFurthest = 0 curEnd = 0
for i in range(len(nums) - 1):
curFurthest = max(curFurthest, i + nums[i])
if (i = curEnd): jumps +
1
cur
1306 - Jump Game III
Problem
Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach to any index with value 0.
Notice that you can not jump outside of the array at any time.
Example 1:
Input: arr = [4,2,3,0,3,1,2], start = 5
Output: true
Explanation:
All possible ways to reach at index 3 with value 0 are:
index 5 -> index 4 -> index 1 -> index 3
index 5 -> index 6 -> index 4 -> index 1 -> index 3
Example 2:
Input: arr = [4,2,3,0,3,1,2], start = 0
Output: true
Explanation:
One possible way to reach at index 3 with value 0 is:
index 0 -> index 4 -> index 1 -> index 3
Example 3:
Input: arr = [3,0,2,1,2], start = 2
Output: false
Explanation: There is no way to reach at index 1 with value 0.
Constraints:
1 <= arr.length <= 5 * 10^4
0 <= arr[i] < arr.length
0 <= start < arr.length
Notes
Use dfs to search for 0. Mark the visited place to trigger stop.
Important:
Remember to reset the mark if can not find along the path, so that it can search into another path.
Solution
#+begin_src python class Solution: def canReach(self, arr, start): if start >= len(arr) or start < 0: return False
if arr[start] == 0: return True
if arr[start] == -1: return False
step = arr[start] arr[start] = -1
if self.canReach(arr, start - step) or self.canReach(arr, start + step): return True else:
84 - Largest Rectangle in Histogram
Problem
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Example:
Input: [2,1,5,6,2,3]
Output: 10
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.
Notes
Main idea is to caculate both left edge and right edge for every entry in the array
Two ways of solution.
Brute Force
Generate two arrays left[] and right[] to keep the two edges of every entry.
- one loop to caculate left[].
- one loop to caculate right[].
- one loop to go through all the edges to caculate the square.
Improvement of the brute force
We can:
store the number of the left bars, which are >= current bar, in left[]
store the number of the right bars, which are >= current bar, in right[]
How to avoid duplicating searching.
- left[current] = left[current - 1]
- jump left[current - 1] steps to check the next interval of the array
Stack
Create a stack to store the index of the entry.
- if current entry is smaller than the top, we find the right edge of the top entry. pop it out and caculate the the max square of the top entry
- if current entry is not smaller than the top, push it into stack
- go through the left entries in the stack. The lefts ones are all have the longest bar at the top.
Solution
Solution 1: brute-force
class Solution:
def largestRectangleArea(self, heights):
if not heights:
return 0
n = len(heights)
res = 0
left = [ i for i in range(n) ]
right = [ i for i in range(n) ]
# caculate for the left edge
for i in range(n):
p = i
while p >= 0:
if heights[p] < heights[i]:
break
p -= 1
left[i] = p
# caculate for the right edge
for i in range(n):
p = i
while p < n:
if heights[p] < heights[i]:
break
p += 1
right[i] = p
for i in range(n):
res = max(res, heights[i] * (right[i] - left[i] - 1))
return res
print(Solution().largestRectangleArea([2, 1, 5, 6, 2, 3]))
O(n^2)
It will execeed time limit on leetcode.
10
Solution 2: improved version
class Solution:
def largestRectangleArea(self, heights):
if not heights:
return 0
res = 0
n = len(heights)
left = [1] * n
right = [1] * n
# caculate left[]
for i in range(n):
p = i - 1
while p >= 0:
if heights[p] >= heights[i]:
left[i] += left[p]
# jump backward
p -= left[p]
else:
break
# caculate right[]
for i in range(n - 1, -1, -1):
p = i + 1
while p < n:
if heights[p] >= heights[i]:
right[i] += right[p]
# jump forward
p += right[p]
else:
break
for i in range(n):
res = max(res, heights[i] * (left[i] + right[i] - 1))
return res
print(Solution().largestRectangleArea([2, 1, 5, 6, 2, 3]))
Generale Case: O(n), because it uses the jump Worst Case: O(n^2)
Solution 3: stack
class Solution:
def largestRectangleArea(self, heights):
stack = []
n = len(heights)
res = 0
index = 0
while index < n:
if not stack or heights[stack[-1]] <= heights[index]:
stack.append(index)
index += 1
else:
top = stack.pop()
area = (heights[top] *
((index - stack[-1] - 1) if stack else index))
res = max(res, area)
while stack:
h = stack.pop()
res = max(
r
85 - Maximal Rectangle
Problem
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.
Example:
Input:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
Output: 6
Notes
Two parts:
- generate a heights histogram for every row.
- apple “largest rectangle in histogram” on each row of histogram
Solution
#+begin_src python :results output class Solution: def maximalRectangle(self, matrix): if not matrix or not matrix[0]: return 0
m = len(matrix) n = len(matrix[0])
histograms = [[0] * n for i in range(m)]
res = 0
for i in range(m): for j in range(n): if matrix[i][j] == “1”: histograms[i][j] = histograms[i - 1][j] + 1 if i > 0 else 1
for histogram in histograms: res = max(res, self.largestRectangleHistogram(histogram))
return res
def largestRectangleHistogram(self, histogram):
if not histogram: return 0
stack = [] res = 0 index = 0 n = len(histogram)
while index < n: if not stack or histogram[index] >= histogram[stack[-1]]: stack.append(index) index += 1 else: res = max( res, histogram[stack.pop()] * ((index - stack[-1] - 1) if stack else index))
while stack: height = histogram[stack.pop()] res = max(res, height * ((n - stack[-1] - 1) if stack else n))
return res
maxtrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,
121 - Best Time to Buy and Sell Stock
Problem
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
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.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Notes
Mark the minPrice and the minProfit
Solution
#+begin_src python class Solution: def maxProfit(self, prices): if not prices: return 0
minPrice = prices[0] maxProfit = 0
for price in prices: if price < minPrice: minPrice = price if price - minPrice > maxProfit: maxProfit = price
122 - Best Time to Buy and Sell Stock II
Problem
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
Example 1:
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.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Notes
Solution 1
Find the valley and peak, save the local maxProfit, add it up when new valley is found.
Solution 2
Simplify the solution 1: we can sum up the profit when we go up step by step.
Solution
Solution 1:
class Solution:
def maxProfit(self, prices):
if not prices:
return 0
valley = prices[0]
maxProfit = 0
res = 0
for price in prices:
if price - valley < maxProfit:
valley = price # new valley
res += maxProfit # add the current maxProfit
maxProfit = 0 # reset current maxProfit
else:
maxProfit = price - valley # update the maxProfit when we still go up
# remember to add the last maxProfit.
# 1. When we are on the peak. maxProfit > 0, it should be added
# 2. When we are in the vally, maxProfit = 0, would not affect the value
res += maxProfit
return res
Solution 2:
#+begin_src python class Solution: def maxProfit(self, prices): res = 0 for i in range(1, len(prices)): if (prices[i-1] < prices[i]):
123 - Best Time to Buy and Sell Stock III
Problem
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).
Example 1:
Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Notes
A very good article about how to solve this kind of problem generally. [Reference]
DP problem have three main sub problems:
- states => DP-Table
- states transition => Transition
- base cases => Padding or initializtion
DP is also a form of brute force method. We have to find all the states of the problem. And we try to use the sub-states to apply the state transitions to simplify the calculation.
How many states do we have in all:
- number of days, n
- numebr of transactions, k = 2
- have the stock or not, 0 or 1
state[n][k][0 or 1] means:
the state at the n-th day, already k times transactions, have or have not the stock in hand.
State Transition:
-
state[n][k][0] = max(state[n-1][k][0], state[n-1][k][1] + prices)
state: I have no stock in hand
It can transit from two states:
-
I do not have stock yesterday => state[n-1][k][0]
-
I do have stock yesterday, and I sell it => state[n-1][k][1] + price
-
-
state[n][k][1] = max(state[n-1][k][1], state[n-1][k-1][0] - prices)
state: I have stock in hand
It can transit from two states:
-
I do have stock yesterday => state[n-1][k][1]
-
I don’t have stock yesterday, and I buy it => state[n-1][k-1][1] - price
-
Solution
class Solution:
def maxProfit(self, prices):
max_k = 2
dp = [ [ [0] * 2 for k in range(max_k + 1) ] for i in range(len(prices))]
for i in range(len(prices)):
for k in range(1, max_k + 1):
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -prices[i]
else:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
return dp[-1][max_k][0]
Simple Version: reduce the DP-Table
We only have to maintain two 1-dimentional states.
import sys
class Solution:
def maxProfit(self, prices):
buy = [-sys.maxsize] * 3
sell = [0] * 3
for price in prices:
buy[1] = max(buy[1], sell[0]-price)
buy[2] = max(buy[2], sell[1]-price)
sell[1] = max(sell[1], buy[1]+price)
sell[2] = max(sell[2], buy[2]+price)
print(buy)
print(sell)
return sell[2]
print(Solution().maxProfit([3,3,5,0,0,3,1,4]))
#+begin_example [-9223372036854775807, -3, -3] [0, 0, 0] [-9223372036854775807, -3, -3] [0, 0, 0] [-9223372036854775807, -3, -3] [0, 2, 2] [-9223372036854775807, 0, 2] [0, 2, 2] [-9223372036854775807, 0, 2] [0, 2, 2] [-
188 - Best Time to Buy and Sell Stock IV
Problem
Say you have an array for which the i-th element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most k transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Example 1:
Input: [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
Example 2:
Input: [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Notes
See problem 121, 122 and 123.
Note the k. If it is too large(>= n/2), treat it as if with infinitive transactions. We don’t want to loop through a too large k.
Solution
#+begin_src python class Solution: def maxProfit(self, k, prices): if not prices: return 0
n = len(prices) if k >= n//2: # treat it as infinitive transactions res = 0 for i in range(1, n): if prices[i] > prices[i-1]: res += prices[i] - prices[i-1] return res
buy = [-sys.maxsize] * (k+1) sell = [0] * (k+1)
for price in prices: for i in range(1, k+1): buy[i] = max(buy[i], sell[i-1]-price) sell[i] = max(sel
309 - Best Time to Buy and Sell Stock with Cooldown
Problem
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
Notes
Solution
#+begin_src python import sys class Solution: def maxProfit(self, prices): dp_buy = -sys.maxsize dp_sell = 0 dp_pre_0 = 0
for price in prices: tmp = dp_sell dp_sell = max(dp_sell, dp_buy + price) dp_buy = max(dp_buy, dp_pre_0 - price) dp_pre_0 = tmp
104 - Maximum Depth of Binary Tree
Problem
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
Note: A leaf is a node with no children.
Example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
Notes
Recursion is your friend!
Solution
#+begin_src python class Solution: def maxDepth(self, root): return 1 + max(self.maxDepth(root.right), self.maxDepth(
21 - Merge Two Sorted Lists
Problem
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
Notes
Recursion is your friend!
Solution
Solution 1: Recursive
class Solution:
def mergeTwoLists(self, l1, l2):
if not l1:
return l2
if not l2:
return l1
if l1.val > l2.val:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
else:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
101 - Symmetric Tree
Problem
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree [1,2,2,3,4,4,3] is symmetric:
1
/ \
2 2
/ \ / \
3 4 4 3
But the following [1,2,2,null,3,null,3] is not:
1
/ \
2 2
\ \
3 3
Notes
Recursion !
Solution
Solution 1: recursive
#+begin_src python class Solution: def isSymmetric(self, root): if not root: return True return self.isMirrored(root.left, root.right)
def isMirrored(self, left, right):
if not left and not right:
return True
elif not left:
return False
elif not right:
return False
else:
if left.val == right.val:
return self.isMirrored(left.left, right.right) and \\
self.isMirrored(left.right, right.left)
else:
return False
#+end_sr
198 - House Robber
Problem
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
Example 2:
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.
Notes
States: 1. house, 2. rob or not rob -> dp[i][0 or 1]
Transition: dp[i][0] = max(dp[i-1][0], dp[i-1][1]), dp[i][1] = dp[i-1][0] + nums[i]
Base Case: dp[0][0] = 0, dp[0][1] = 0
Solution
class Solution:
def rob(self, nums):
robbed, notRobbed = 0, 0
for i in nums:
robbed, notRobbed = notRobbed + i, max(robbed, notRobbed)
300 - Longest Increasing Subsequence
Problem
Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Note:
There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
Solution
DP problem.
States: index, dp[index] = longest increasing subsequence at this position
Transition: if nums[j] < nums[i]: dp[i] = max(dp[i], dp[j] + 1)
Base Case: dp[0] = 1
Solution 1: DP with O(n^2)
class Solution:
def lengthOfLIS(self, nums):
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) if nums else 0
Solution 2: DP with binary search for LIS
class Solution:
def lengthOfLIS(self, nums):
if not nums:
return 0
dp = [nums[0]]
for i in range(1, len(nums)):
if nums[i] > dp[-1]:
dp.append(nums[i])
else:
j = self.binarySearch(dp, nums[i])
dp[j] = nums[i]
return len(dp)
def binarySearch(self, nums, target):
l = 0
r = 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
Time: O(nlogn)
Space: O(n)
322 - Coin Change
Problem
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
Example 1:
Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = [2], amount = 3
Output: -1
Note:
You may assume that you have an infinite number of each kind of coin.
Notes
DP problem
States: amount
Transition: dp[i] = min(dp[i], dp[i-coins[j]]+1)
Solution
class Solution:
def coinChange(self, coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i in range(1, amount + 1):
for j in range(len(coins)):
if i - coins[j] >= 0:
dp[i] = min(dp[i], dp[i - coins[j]] + 1)
return dp[-1] if dp[-1] != float('inf') else -1
152 - Maximum Product Subarray
Problem
Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example 1:
Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Example 2:
Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
Notes
DP problem:
-
States: We have to know two previous states to deduct the current max product:
- index
- max for positive or min for negative
So the DP Table is dp[i(index)][0 or 1(max or min)]
-
Transition:
-
if nums[i] >= 0: dp[i][0] = max(nums[i], dp[i-1][0] * nums[i]) dp[i][1] = dp[i-1][1] * nums[i]
-
if nums[i] < 0:
dp[i][0] = dp[i-1][1] * nums[i]) dp[i][1] = min(nums[i], dp[i-1][0] * nums[i]
-
-
Base case:
if there is only one element:
dp[0][0] = dp[0][1] = nums[0]
Solution
Solution 1: DP
Original Version:
class Solution:
def maxProduct(self, nums):
if not nums:
return 0
dp = [[0] * 2 for i in range(len(nums))]
res = dp[0][0] = dp[0][1] = nums[0]
for i in range(1, len(nums)):
if nums[i] >= 0:
dp[i][0] = max(nums[i], dp[i - 1][0] * nums[i])
dp[i][1] = dp[i - 1][1] * nums[i]
else:
dp[i][0] = dp[i - 1][1] * nums[i]
dp[i][1] = min(nums[i], nums[i] * dp[i - 1][0])
res = max(res, dp[i][0])
return res
Simplify Version 1:
class Solution:
def maxProduct(self, nums):
if not nums:
return 0
pos = neg = res = nums[0]
for i in nums[1:]:
if i>=0:
pos = max(i, pos*i)
neg = neg*i
else:
tmp = pos # Important: remember the value, because we are gonna alter it.
pos = neg*i
neg = min(tmp*i, i) # Use the original value
# We also can write as this, but we should notice it only works in like python or ruby
# pos, neg = neg*i, min(pos*i, i), simplify to version 3
res = max(pos, res)
return res
Simplify Version 2:
class Solution:
def maxProduct(self, nums):
if not nums:
return 0
pos = neg = res = nums[0]
for i in nums[1:]:
if i>=0:
pos, neg = max(i, pos*i), neg*i
else:
pos, neg = neg*i, min(i, pos*i)
res = max(pos, res)
return res
Solution 2: Prefix sum and Suffix sum
class Solution:
def maxProduct(self, nums):
rnums = nums[::-1]
for i in range(1, len(nums)):
nums[i] *= nums[i-1] or 1
rnums[i] *= rnums[i-1] or 1
return max(nums+rnums)
Time: O(n) Space: O(n)
96 - Unique Binary Search Trees
Problem
Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n?
Example:
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
Notes
Solution 1: DP
In this dp problem, the hard part is to figure out the transition.
-
States: n
-
Transition:
Some intuition:
assume dp[0] = 1 dp[1] = dp[0]*dp[0] dp[2] = dp[0]*dp[1] + dp[0]*dp[1] dp[3] = dp[0]*dp[2] + dp[1]*dp[1] + dp[2]*dp[0] dp[4] = dp[0]*dp[3] + dp[1]*dp[2] + dp[2]*dp[1] + dp[3]*dp[0] dp[5] = dp[0]*dp[4] + dp[1]*dp[3] + dp[2]*dp[2] + dp[3]*dp[1] + dp[4]*dp[0] dp[6] = dp[0]*dp[5] + dp[1]*dp[4] + dp[2]*dp[3] + dp[3]*dp[2] + dp[4]*dp[1] + dp[5]*dp[0]
for i in range(1, n+1): for j in range(0, i): dp[i] += dp[j]*dp[i-1-j]
-
Base case: dp[0] = 1
Solution 2: Catalan Number
Cn = 1/(n+1)(2n n) = (2n)!/(n+1)!n!
The first Catalan numbers for n = 0, 1, 2, 3, ... are
1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012,
742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190,
6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452
Solution
Solution 1: DP
class Solution:
def numTrees(self, n):
if n < 1:
return 0
dp = [0] * (n+1)
dp[0] = 1
for i in range(1, n+1):
for j in range(0, i):
dp[i] += dp[j]*dp[i-1-j]
return dp[n]
Solutiojn 2: Catalan Number
import math
class Solution:
def numTrees(self, n):
return math.factorial(2*n)/(math.factorial(n)*math.factorial(n+1))
221 - Maximal Square
Problem
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
Example:
Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Output: 4
Notes
DP Problem:
-
States:
- position -> [i][j]
- how many continues 1 in vertical direction -> [i][j][0]
- how many continues 1 in horizontal direction -> [i][j][1]
- square value(or the length of the square) -> [i][j][2]
dp[i][j][0 or 1 or 2]
-
Transition:
dp[i + 1][j + 1][0] = dp[i][j + 1][0] + 1 dp[i + 1][j + 1][1] = dp[i + 1][j][1] + 1 dp[i + 1][j + 1][2] = min(dp[i][j][2]+1, dp[i + 1][j + 1][0], dp[i + 1][j + 1][1])
-
Base Case:
One padding row on vertical and horizontal direction, with value 0
Solution
Original Version :
class Solution:
def maximalSquare(self, matrix):
if not matrix:
return 0
dp = [[[0] * 3 for j in range(len(matrix[0]) + 1)] for i in range(len(matrix) + 1)]
res = 0
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == "1":
dp[i + 1][j + 1][0] = dp[i][j + 1][0] + 1
dp[i + 1][j + 1][1] = dp[i + 1][j][1] + 1
dp[i + 1][j + 1][2] = min(dp[i][j][2]+1, dp[i + 1][j + 1][0], dp[i + 1][j + 1][1])
res = max(res, dp[i + 1][j + 1][2])
return res*res
Time: O(mn) Space: O(mn)
Simplified Version 1: optimize the space, 3xn space :
class Solution:
def maximalSquare(self, matrix):
if not matrix:
return 0
dp = [[0] * 3 for j in range(len(matrix[0]) + 1)]
res = 0
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == "1":
dp[j+1][0] = dp[j+1][0] + 1
dp[j+1][1] = dp[j][1] + 1
dp[j+1][2] = min(dp[j+1][2]+1, dp[j+1][0]+1, dp[j][1]+1)
res = max(res, dp[j + 1][2])
else:
dp[j+1][0] = dp[j+1][1] = dp[j+1][2] = 0
return res*res
Space: O(n)
Simplified Version 2: n space :
class Solution:
def maximalSquare(self, matrix):
if not matrix:
return 0
dp = [0 for j in range(len(matrix[0]) + 1)]
res = 0
prev = 0 # previous diagnol element
for i in range(len(matrix)):
for j in range(len(matrix[0])):
temp = dp[j+1]
if matrix[i][j] == "1":
dp[j+1] = min(prev, dp[j+1], dp[j]) + 1
res = max(res, dp[j + 1])
else:
dp[j+1] = 0
prev = temp
return res*res
Simplified Version 3: in place :
class Solution:
def maximalSquare(self, matrix):
if not matrix:
return 0
res = 0
m = len(matrix)
n = len(matrix[0])
for i in range(m):
for j in range(n):
if matrix[i][j] == "1":
if i == 0 or j == 0:
matrix[i][j] = 1
else:
matrix[i][j] = min(matrix[i - 1][j - 1],
matrix[i][j - 1],
matrix[i - 1][j]) + 1
res = max(res, matrix[i][j])
else:
matrix[i][j] = 0
return res * res
Space: O(1)
279 - Perfect Squares
Problem
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.
Example 1:
Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.
Example 2:
Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.
Notes
DP problem
-
States: n
-
Transition:
-
dp[i] = min([_dp[i-j*j]+1 if i-j*j >= 0 else break for j in range(1, sqrt(i)+1)])
-
Base case:
- dp[0] = 0
- dp[i] = i
Solution
import math
class Solution:
dp = [0]
def numSquares(self, n):
_dp = self.dp
if len(_dp) >= n + 1:
return _dp[n]
else:
for i in range(len(_dp), n+1):
_dp += [min([_dp[i-j*j]+1 for j in range(1, int(math.sqrt(i))+1) if i-j*j >= 0])]
return _dp[n]
print(Solution().numSquares(12))
647 - Palindromic Substrings
Problem
Given a string, your task is to count how many palindromic substrings in this string.
The substrings with different start indexes or end indexes are counted as different substrings even they consist of same characters.
Example 1:
Input: "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".
Example 2:
Input: "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
Note:
The input string length won't exceed 1000.
Notes
DP problem
-
States:
left_index, right_index, mark if
s[left_index, right_index+1]
is palindromic -
Transition:
if s[l] == s[r]: # mark it only when both ends are same values dp[l][r] = 1 if r == l # if only one element dp[l][r] = 1 if r+1 == l # if only two elements dp[l][r] = 1 if dp[l+1][r+1] = 1 # if the string in between is palindromic results = results + 1
-
Base case:
initialize dp[i][j] = 0
Solution
class Solution:
def countSubstrings(self, s):
dp = [[0] * len(s) for i in range(len(s))]
res = 0
for r in range(len(s)):
for l in range(r+1):
if s[r] == s[l]:
if r == l or r+1 == l or dp[l+1][r-1] == 1:
dp[l][r] = 1
res += 1
return res
print(Solution().countSubstrings("aba"))
5 - Longest Palindromic Substring
Problem
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
Notes
DP problem
-
States:
left_index, right_index, mark if
s[left_index, right_index+1]
is palindromic -
Transition:
if s[l] == s[r]: # mark it only when both ends are same values dp[l][r] = 1 if r == l # if only one element dp[l][r] = 1 if r+1 == l # if only two elements dp[l][r] = 1 if dp[l+1][r+1] = 1 # if the string in between is palindromic results = s[l:r+1] if r-l+1>len(results) # comparing the length, record the maximum
-
Base case:
initialize dp[i][j] = 0
Solution
Solution 1: DP
class Solution:
def longestPalindrome(self, s: str) -> str:
dp = [[0] * len(s) for i in range(len(s))]
res = ""
for r in range(len(s)):
for l in range(r + 1):
if s[l] == s[r]:
if l == r or l + 1 == r or dp[l + 1][r - 1] == 1:
dp[l][r] = 1
if r - l + 1 > len(res):
res = s[l:r + 1]
return res
Solution 2: Central Expansion
136 - Single Number
Problem
Given a non-empty array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
Example 1:
Input: [2,2,1]
Output: 1
Example 2:
Input: [4,1,2,1,2]
Output: 4
Notes
Solution 1: Hash Table
Solution 2: Bit manipulation
Consider XOR all element together. The left number is the single number.
Solution
Solution 1: Hash Table
class Solution:
def singleNumber(self, nums):
table = {}
for i in nums:
table[i] = table.get(i, 0) + 1
for k in table:
if table[k] == 1:
return k
Solution 2: Bit manipulation for finding the single number
class Solution:
def singleNumber(self, nums):
res = 0
for i in nums:
res ^= i
return res
print(Solution().singleNumber([2, 2, 5, 1, 8, 1, 9, 8, 9]))
Solution 3: Python reduce
from functools import reduce
import operator
nums = [2, 2, 5, 1, 8, 1, 9, 8, 9]
print(reduce(lambda x, y: x ^ y, nums))
print(reduce(operator.xor, nums))
202 - Happy Number
Problem
Write an algorithm to determine if a number is "happy".
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
Example:
Input: 19
Output: true
Explanation:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
Notes
Hash Table
Solution
class Solution:
def isHappy(self, n):
table = {}
while n != 1:
x = n
n = 0
while x != 0:
n += (x % 10)**2
x //= 10
if table.get(n):
return False
else:
table[n] = 1
return True
class Solution:
def isHappy(self, n):
mem = set()
while n not in mem:
mem.add(n)
n = sum(int(i)**2 for i in str(n))
return n == 1
338 - Counting Bits
Problem
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.
Example 1:
Input: 2
Output: [0,1,1]
Example 2:
Input: 5
Output: [0,1,1,2,1,2]
Notes
DP problem
- States: index
- Transition:
dp[0] = 0
dp[1] = dp[1-1] + 1 = 1
dp[2] = dp[2-2] + 1 = 1
dp[3] = dp[3-2] + 1 = 2
dp[4] = dp[4-4] + 1 = 1
dp[5] = dp[5-4] + 1 = 2
dp[6] = dp[6-4] + 1 = 2
dp[7] = dp[7-4] + 1 = 3
dp[8] = dp[8-8] + 1 = 1
dp[9] = dp[9-8] + 1 = 2
dp[i] = dp[i - log_2 (i)] + 1
A trick using bit manipulation:
8 -> 1000
9 -> 1001
10 -> 1010
9 & 8 -> 1001 & 1000 -> 1000 dp[8] + 1 = 2
10 & 9 -> 1010 & 1001 -> 1000 dp[8] + 1 = 2
11 & 10 -> 1011 & 1010 -> 1010 dp[10] + 1 = 3
12 & 11 -> 1100 & 1011 -> 1000 dp[8] + 1 = 2
dp[i] = dp[i & (i-1)] + 1
Solution
Solution 1: DP
class Solution:
def countBits(self, num):
offset = 1
dp = [0] * (num + 1)
for i in range(1, num + 1):
if offset * 2 == i:
offset *= 2
dp[i] = dp[i - offset] + 1
return dp
Solution 2: Bit manipulation on couting bits
class Solution:
def countBits(self, num):
dp = [0] * (num + 1)
for i in range(1, num + 1):
dp[i] = dp[i & (i - 1)] + 1
return dp
Solution 3:
class Solution(object):
def countBits(self, num):
res=[0]
while len(res)<=num:
res+=[i+1 for i in res]
return res[:num+1]
Solution 4:
class Solution:
def countBits(self, num):
dp = [0] * (num + 1)
for i in range(1, num+1):
if not i % 2:
dp[i] = dp[i >> 1]
else:
dp[i] = dp[i >> 1] + 1
return dp[num]
494 - Target Sum
Problem
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
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
There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.
Solution
DP problem. The hardest part of this problem is to find the states.
What states do we have to calculate the current result, which is the number of ways of reaching the target.
-
States:
- index: how many different numbers -> n
- sum: how many different possible sums -> [-sum(nums), +sum(nums)]
The tricky part is how you present the range of sum in an array.
You have to add an offest as large as the sum(nums) to shift the range into the positive side.
So it becomes [0, 2sum(nums)]
For example:
[1, 1, 1, 1, 1], you have a range of [-5, 5].
-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5
Add an offset of sum = 5
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 11 numbers
dp should have a length of 2sum(nums) + 1
-
Transition:
We need all the sum values of the previous number to calculate the current one.
for j in range(2sum+1):
if dp[i-1][j] > 0: # only check the ones, which have been reached
if j+nums[i] < 2sum + 1: # sign + to the current number, we transit from j to j+nums[i]
dp[i][j+nums[i]] += dp[i-1][j]
if j-nums[i] >= 0: # sign + to the current number, we transit from j to j-nums[i]
dp[i][j-nums[i]] += dp[i-1][j]
- Base case:
We start from the offset.
# the first number with symbols
dp[0][offset+nums[0]] +=1
dp[0][offset-nums[0]] +=1
Important: You have to use += 1, not only = 1, because the number can be 0.
For exmaple: we take sum = 5 When the first number is 1 (not 0):
d[0][0+sum + 1] += 1 -> dp[0][6] = 1
d[0][0+sum - 1] += 1 -> dp[0][4] = 1
It means:
there is one ways to reach 1 (6 - offset)
there is one ways to reach -1 (4 - offset)
When the first number is 0:
d[0][0+sum + 1] += 1 -> dp[0][5] = 1
d[0][0+sum - 1] += 1 -> dp[0][5] = 2
It means there are 2 ways to reach 0 (5 - offset)
Solution 1: 2D - DP
class Solution():
def findTargetSumWays(self, nums, S):
l = len(nums)
n = 2 * sum(nums) + 1
offset = sum(nums)
dp = [[0] * n for i in nums]
if not nums:
return 0
# base case
dp[0][offset - nums[0]] += 1
dp[0][offset + nums[0]] += 1
for i in range(1, len(nums)):
for j in range(n):
if dp[i-1][j] > 0:
if j + nums[i] < n:
dp[i][j + nums[i]] += dp[i-1][j]
if j - nums[i] >= 0:
dp[i][j - nums[i]] += dp[i-1][j]
return dp[-1][offset + S]
print(Solution().findTargetSumWays([1, 1, 1, 1, 1], 3))
Time: O(l*n) Space: O(l*n)
Solution 2: 1D - DP
We notice that, to calculate the dp array at the current index, we only have to know one previous row.
So we only have to maintain two dp arrays to record previous values and current values respectly.
class Solution:
def findTargetSumWays(self, nums, S):
if not nums:
return 0
l = len(nums)
n = 2 * sum(nums) + 1
offset = sum(nums)
dp = [[0] * n for i in nums]
dp[0][offset + nums[0]] += 1
dp[0][offset - nums[0]] += 1
for i in range(1, l):
for j in range(n):
if dp[i - 1][j] > 0:
if dp[i - 1][j] < n:
dp[i][j + nums[i]] += dp[i - 1][j]
if dp[i - 1][j] >= 0:
dp[i][j - nums[i]] += dp[i - 1][j]
return dp[-1][offset + S]
print(Solution().findTargetSumWays([1, 1, 1, 1, 1], 3))
283 - Move Zeros
Problem
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
Note:
You must do this in-place without making a copy of the array.
Minimize the total number of operations.
Solution
Two pointers.
One pointer is for the start of 0.
One pointer keeps going forward.
class Solution:
def moveZeros(self, nums):
p1, p2 = 0, 0
for p2 < len(nums):
if nums[p1] != 0:
p1 += 1
elif nums[p2] != 0:
nums[p1], nums[p2] = nums[p2], nums[p1]
p1 += 1
p2 += 1
return nums
416 - Partition Equal Subset Sum
Problem
Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Note:
Each of the array element will not exceed 100.
The array size will not exceed 200.
Example 1:
Input: [1, 5, 11, 5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: [1, 2, 3, 5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.
Solution
DP problem, it is similar to problem 494 Target Sum.
Maintain a dp table: dp[i][sum]. Don’t forget to the offset.
Transition: dp[i][j-nums[i]] += dp[i-1][j] dp[i][j+nums[i]] += dp[i-1][j]
When the transition is only depend on the last row, usually you always can transform the dp table to 1 dimensional.
Solution 1: 1D-DP
class Solution:
def canPartition(slef, nums):
nums_sum = sum(nums)
if nums_sum % 2 != 0:
return False
target = nums_sum//2
n = target + 1
dp = [False] * n
dp[0] = True
for i in range(len(nums)):
for j in reversed(range(nums[i], n)):
dp[j] = dp[j-nums[i]] or dp[j]
if dp[target]:
return True
return dp[target]
Time: O(len(nums)*sum(nums))
Space: O(len(nums)*sum(nums))
Solution 2: Bit manipulation for maintaning the dp table
class Solution:
def canPartition(self, nums):
s = sum(nums)
if s%2 != 0:
return False
bits = 1
for i in nums:
bits |= bits << i
return (bits >> (s//2)) & 1 == 1
Time: O(n)
Space: O(1) maybe, depends on the sum of the array, the bits can be longer
698 - Partition to K Equal Sum Subsets
Problem
Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal.
Example 1:
Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
Output: True
Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.
Note:
1 <= k <= len(nums) <= 16.
0 < nums[i] < 10000.
Solution
215 - Kth Largest Element in an Array
Problem
215. Kth Largest Element in an Array
Medium
3152
222
Add to List
Share
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
Solution
The naive solution would be to maintain an array of length k and update the array in each iteration to keep the first kth largest element in the array
Solution 1: Array of k
class Solution:
def findKthLargest(self, nums, k):
l = []
for i in nums:
if not l:
l.append(i)
else:
len0 = len(l)
for j in range(len(l)):
if i >= l[j]:
l = l[0:j] + [i] + l[j:]
break
if len0 == len(l) and len0 != k:
l.append(i)
l = l[0:k]
return l[-1]
Time: O(nk)
Space: O(k)
Solution 2: Partition to find the kth largest
class Solution:
def findKthLargest(self, nums, k):
pos = self.partition(nums, 0, len(nums) - 1)
if pos + 1 < k:
return self.findKthLargest(nums[pos + 1:], k - pos - 1)
elif pos + 1 > k:
return self.findKthLargest(nums[0:pos], k)
else:
return nums[pos]
def partition(self, nums, l, r):
p = r
while(l < r):
while l < r and nums[l] > nums[p]:
l += 1
while l < r and nums[r] <= nums[p]:
r -= 1
nums[l], nums[r] = nums[r], nums[l]
nums[l], nums[p] = nums[p], nums[l]
return l
Time: O(klogn)
Space: O(1)
49 - Group Anagrams
Problem
Given an array of strings, group anagrams together.
Example:
Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
Note:
All inputs will be in lowercase.
The order of your output does not matter.
Solution
Use the sorted string as key in hash table
import collections
class Solution:
def groupAnagrams(self, strs):
ans = collections.defaultdict(list)
for s in strs:
ans[tuple(sorted(s))].append(s)
return list(ans.values())
Or use the counter
import collections
class Solution:
def groupAnagrams(self, strs):
ans = collections.defaultdict(list)
for s in strs:
a = [0] * 26
for c in s:
a[ord(c) - ord('a')] += 1
ans[tuple(a)].append(s)
return list(ans.values())
876 - Middle of the Linked List
Problem
Given a non-empty, singly linked list with head node head, return a middle node of linked list.
If there are two middle nodes, return the second middle node.
Example 1:
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.
Example 2:
Input: [1,2,3,4,5,6]
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.
Solution
Solution 1: two-pointer
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
p1 = p2 = head
while p2 and p2.next:
p2 = p2.next.next
p1 = p1.next
return p1
Solution 2: counter
class Solution:
def middleNode(self, head):
mid = head
count = 0
while head:
count += 1
if count % 2 == 0:
mid = mid.next
head = head.next
return mid
844 - Backspace String Compare
Problem
Given two strings S and T, return if they are equal when both are typed into empty text editors. # means a backspace character.
Example 1:
Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".
Example 2:
Input: S = "ab##", T = "c#d#"
Output: true
Explanation: Both S and T become "".
Example 3:
Input: S = "a##c", T = "#a#c"
Output: true
Explanation: Both S and T become "c".
Example 4:
Input: S = "a#c", T = "b"
Output: false
Explanation: S becomes "c" while T becomes "b".
Note:
1 <= S.length <= 200
1 <= T.length <= 200
S and T only contain lowercase letters and '#' characters.
Solution
Solution 1: stack
class Solution:
def backspaceCompare(self, S, T):
return self.build(S) == self.build(T)
def build(self, s):
res = []
for c in s:
if c != "#":
res.append(c)
elif res:
res.pop()
return "".join(res)
print(Solution().backspaceCompare("ac#cc#", "ab#c"))
from functools import reduce
class Solution:
def backspaceCompare(self, S, T):
def back(res, c):
if c != "#":
res.append(c)
elif res:
res.pop()
return res
return reduce(back, S, []) == reduce(back, T, [])
print(Solution().backspaceCompare("ac#cc#", "ab#c"))
Solution 2: two pointer, reserve
class Solution:
def backspaceCompare(self, S, T):
pS, pT = len(S) - 1, len(T) - 1
backS, backT = 0, 0
while pS >= 0 or pT >= 0:
while pS >= 0:
if S[pS] == "#":
backS += 1
pS -= 1
elif backS:
backS -= 1
pS -= 1
else:
break
while pT >= 0:
if T[pT] == "#":
backT += 1
pT -= 1
elif backT:
backT -= 1
pT -= 1
else:
break
if not (pS >= 0 and pT >= 0 and S[pS] == T[pT]):
return pS == pT == -1
pS -= 1
pT -= 1
return True
print(Solution().backspaceCompare("###ac#b", "ab#b"))
155 - Min Stack
Problem
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.
Example:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> Returns -3.
minStack.pop();
minStack.top(); --> Returns 0.
minStack.getMin(); --> Returns -2.
Solution
Use a second stack to keep track of the min value. Pay attention to the return value. Remember to return None when there is no result.
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min = []
def push(self, x: int) -> None:
self.stack.append(x)
if not self.getMin() or self.getMin() >= x:
self.min.append(x)
def pop(self) -> None:
if self.stack:
x = self.stack.pop()
if x == self.min[-1]:
self.min.pop()
def top(self) -> int:
if self.stack:
return self.stack[-1]
else:
return None
def getMin(self) -> int:
if self.min:
return self.min[-1]
else:
return None
543 - Diameter of Binary Tree
Problem
Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.
Example:
Given a binary tree
1
/ \
2 3
/ \
4 5
Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].
Note: The length of path between two nodes is represented by the number of edges between them.
Solution
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
if not root:
return 0
elif not root.right and not root.left:
return 0
elif not root.right:
return max(self.diameterOfBinaryTree(root.left),
1 + self.height(root.left))
elif not root.left:
return max(self.diameterOfBinaryTree(root.right),
1 + self.height(root.right))
else:
return max(self.diameterOfBinaryTree(root.right),
self.diameterOfBinaryTree(root.left),
self.height(root.left) + self.height(root.right) + 2)
def height(self, root):
if not root:
return 0
if not root.right and not root.left:
return 0
return 1 + max(self.height(root.left), self.height(root.right))
class Solution:
def diameterOfBinaryTree(self, root):
self.ans = 0
def depth(node):
if not node:
return 0
r = depth(node.right)
l = depth(node.left)
self.ans = max(self.ans, r+l)
return 1 + max(r, l)
depth(root)
return self.ans
1046 - Last Stone Weight
Problem
We have a collection of stones, each stone has a positive integer weight.
Each turn, we choose the two heaviest stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:
If x == y, both stones are totally destroyed;
If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x.
At the end, there is at most 1 stone left. Return the weight of this stone (or 0 if there are no stones left.)
Example 1:
Input: [2,7,4,1,8,1]
Output: 1
Explanation:
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of last stone.
Solution
Priority queue, max heap can be used here.
class Solution:
def lastStoneWeight(self, stones):
res = [ -s for s in stones ]
heapq.heapify(res)
while len(res) > 1 :
y = heapq.heappop(res)
x = heapq.heappop(res)
if y != x:
heapq.heappush(res, y-x)
return res[0]*-1 if res else 0
525 - Contiguous Array
Problem
Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1.
Example 1:
Input: [0,1]
Output: 2
Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1.
Example 2:
Input: [0,1,0]
Output: 2
Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1.
Note: The length of the given binary array will not exceed 50,000.
Solution
We keep a count, which decrease 1 if 0, increase 1 if 1.
If at x and y, they have the same value v of count, then the numbers between x+1 and y have the equal number of 1s and 0s. The max length = y - (x + 1) + 1 = y - x
And after that, if we found at index w, it also has value v of count. The max length should be w - x.
So we have to maintain a hashmap h using the values of count as keys, and the first index of that count as its value.
The maxlen = max(maxlen, h[count])
if current count in the map.
Important
We have an initial value of the map.
h[0] = -1 (If count == 0 at index i, we have all the numbers between h[0] + 1 and i. And h[0] + 1 should at index 0, so h[0] = -1)
Consider the input [0, 1]:
Index 0: h[-1] = 0, maxlen = 0
Index 1: h[0] = -1, maxlen = 1 - (-1) = 2
maxlen = 2
Solution: counting contiguous 1s and 0s with a Hashmap
class Solution:
def findMaxLength(self, nums):
h = {0: -1}
count = ans = 0
for i in range(len(nums)):
count += 1 if nums[0] else -1
ans = max(ans, h.setdefault(count, i))
return ans
Perform String Shifts
Problem
You are given a string s containing lowercase English letters, and a matrix shift, where shift[i] = [direction, amount]:
direction can be 0 (for left shift) or 1 (for right shift).
amount is the amount by which string s is to be shifted.
A left shift by 1 means remove the first character of s and append it to the end.
Similarly, a right shift by 1 means remove the last character of s and add it to the beginning.
Return the final string after all operations.
Example 1:
Input: s = "abc", shift = [[0,1],[1,2]]
Output: "cab"
Explanation:
[0,1] means shift to left by 1. "abc" -> "bca"
[1,2] means shift to right by 2. "bca" -> "cab"
Example 2:
Input: s = "abcdefg", shift = [[1,1],[1,1],[0,2],[1,3]]
Output: "efgabcd"
Explanation:
[1,1] means shift to right by 1. "abcdefg" -> "gabcdef"
[1,1] means shift to right by 1. "gabcdef" -> "fgabcde"
[0,2] means shift to left by 2. "fgabcde" -> "abcdefg"
[1,3] means shift to right by 3. "abcdefg" -> "efgabcd"
Solution
class Solution:
def stringShift(self, s: str, shift: List[List[int]]) -> str:
count = sum([ (-1+2*x)*y for x, y in shift ]) % len(s)
ds = s + s
return ds[len(s)-count: 2*len(s) - count]
238 - Product of Array Except Self
Problem
238. Product of Array Except Self
Medium
4145
358
Add to List
Share
Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].
Example:
Input: [1,2,3,4]
Output: [24,12,8,6]
Constraint: It's guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.
Note: Please solve it without division and in O(n).
Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)
Solution
Solution 1: prefix product and suffix product
class Solution:
def productExceptSelf(self, nums):
n = len(nums)
ans = [0] * n
ans[0] = 1
for i in range(1, n):
ans[i] = ans[i-1] * nums[i-1]
R = 1
for i in reversed(range(n)):
ans[i] = ans[i] * R
R *= nums[i]
return ans
print(Solution().productExceptSelf([1, 2, 3, 4]))
678 - Valid Parenthesis String
Problem
Given a string containing only three types of characters: '(', ')' and '*', write a function to check whether this string is valid. We define the validity of a string by these rules:
Any left parenthesis '(' must have a corresponding right parenthesis ')'.
Any right parenthesis ')' must have a corresponding left parenthesis '('.
Left parenthesis '(' must go before the corresponding right parenthesis ')'.
'*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string.
An empty string is also valid.
Example 1:
Input: "()"
Output: True
Example 2:
Input: "(*)"
Output: True
Example 3:
Input: "(*))"
Output: True
Note:
The string size will be in the range [1, 100].
Solution
Solution 1: cmin and cmax to valida to parenthesis
class Solution:
def checkValidString(self, s: str) -> bool:
counter_max = 0
counter_min = 0
for c in s:
if c == "(":
counter_max += 1
counter_min += 1
elif c == ")":
counter_max -= 1
counter_min = max(counter_min-1, 0)
else:
counter_max += 1
counter_min = max(counter_min-1, 0)
if counter_max < 0:
return False
return counter_min == 0
200 - Number of Islands
Problem
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
Input:
11110
11010
11000
00000
Output: 1
Example 2:
Input:
11000
11000
00100
00011
Output: 3
Solution
DFS problem. Straight forward.
class Solution:
def numIslands(self, grid):
if not grid:
return 0
h = len(grid)
w = len(grid[0])
ans = 0
m = [ [0] * w for x in range(h) ]
for i in range(h):
for j in range(w):
if grid[i][j] == "1" and m[i][j] == 0:
self.dfs(grid, i, j, m)
ans += 1
return ans
def dfs(self, grid, i, j, m):
if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == "0" or m[i][j] == 1:
return None
else:
m[i][j] = 1
self.dfs(grid, i, j+1, m)
self.dfs(grid, i, j-1, m)
self.dfs(grid, i-1, j, m)
self.dfs(grid, i+1, j, m)
return None
46 - Permutations
Problem
Given a collection of distinct integers, return all possible permutations.
Example:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
Solution
Solution 1
class Solution:
def permute(self, nums):
ans = []
self.backtrack([], nums, ans)
return ans
def backtrack(self, path, nums, ans):
if not nums:
ans.append(path[:])
return
for i, v in enumerate(nums):
self.backtrack(path+[v], nums[:i]+nums[i+1:], ans)
Solution 2
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
ans = []
marked = [False] * len(nums)
self.dfs(nums, [], marked, ans)
return ans
def dfs(self, nums, path, marked, ans):
if len(path) == len(nums):
ans.append(list(path))
return
for i, n in enumerate(nums):
if marked[i]:
continue
marked[i] = True
path.append(n)
self.dfs(nums, path, marked, ans)
path.pop()
marked[i] = False
47 - Permutations II
Problem
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
Example:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
Solution
Backtrack problem.
Framework of backtrack problem:
- choose a path
- selection pool
- return condition
ans = []
def backtrack(path, pool):
if meet condition:
ans.add(path)
return
for selection in pool:
path.add(selection)
backtrack(path, new_pool)
path.remove(selection)
Important:
Pay attention, you must add a copy of the path to result, not the path it self. It may get modified afterwards.
class Solution:
def permuteUnique(self, nums):
ans = []
self.backtrack([], nums, ans)
return ans
def backtrack(self, path, pool, ans):
if not pool:
ans.append(path[:])
return
t = {}
for i, v in enumerate(pool):
if v not in t:
t[v] = 1
self.backtrack(path+[v], pool[:i]+pool[i+1:], ans)
print(Solution().permuteUnique([1,1,2]))
1008 - Construct Binary Search Tree from Preorder Traversal
Problem
Return the root node of a binary search tree that matches the given preorder traversal.
(Recall that a binary search tree is a binary tree where for every node, any descendant of node.left has a value < node.val, and any descendant of node.right has a value > node.val. Also recall that a preorder traversal displays the value of the node first, then traverses node.left, then traverses node.right.)
Example 1:
Input: [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]
Solution
We keep track of a low bound for the function.
We always build the right child with the low bound as the current node value.
If next value is smaller then current node value, we construct the left node.
If next value is smaller then current low bound, we construct the right node.
If next value is larger then current low bound, we jump out of the funciton.
Let the recursive jump back to the last level and repeat the same proccess
class Solution:
def bstFromPreorder(self, preorder):
if not preorder:
return None
root = TreeNode(preorder[0])
self.buildTree(root, 1, preorder, sys.maxsize)
return root
def buildTree(self, root, pos, preorder, rmax):
if pos >= len(preorder) or preorder[pos] > rmax:
return None
if preorder[pos] < root.val:
root.left = TreeNode(preorder[pos])
pos = self.buildTree(root.left, pos+1, preorder, root.val - 1)
if pos < len(preorder) and preorder[pos] <= rmax:
root.right = TreeNode(preorder[pos])
pos = self.buildTree(root.right, pos+1, preorder, rmax)
return pos
Concise version:
class Solution:
pos = 0
def bstFromPreorder(self, preorder, bound=float('inf')):
if self.pos >= len(preorder) or preorder[self.pos] > bound:
return None
root = TreeNode(preorder[self.pos])
self.pos += 1
root.left = self.bstFromPreorder(preorder, root.val)
root.right = self.bstFromPreorder(preorder, bound)
return root
105 - Construct Binary Tree from Preorder and Inorder Traversal
Problem
Given preorder and inorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.
For example, given
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:
3
/ \
9 20
/ \
15 7
Solution
- The first node of the preorder is always the root.
- Find the root in the inorder array, split it there. The left part are all the nodes in left branch. the right part in the right branch.
- Repeat it recursively. Note that we should always pop the first node of preorder as current root(solution 1). All keep a global index as shown(solution 2).
Simple Version:
It uses a lot resources
- index() operation is also not O(1).
- inorder[:split] copies the sub array to the next level recursion.
class Solution:
def buildTree(self, preorder, inorder):
if inorder:
node = TreeNode(preorder.pop(0))
split = inorder.index(node.val)
node.left = self.buildTree(preorder, inorder[:split])
ndoe.right = self.buildTree(preorder, inorder[split+1:])
return node
Optimized Version:
- use a map instead of index() operation
- pass the index instead of the sub array it self
class Solution(object):
def __init__(self):
self.map = {}
def buildTree(self, preorder, inorder):
for i, val in enumerate(inorder):
self.map[val] = i
return self.recursive(0, len(preorder) - 1, preorder, inorder)
def recursive(self, start, end, preorder, inorder):
if (start > end):
return None
node = TreeNode(preorder.pop(0))
split = self.map[node.val]
node.left = self.recursive(start, split - 1, preorder, inorder)
node.right = self.recursive(split + 1, end, preorder, inorder)
return node
Leftmost Column with at Least a One
Problem
A binary matrix means that all elements are 0 or 1. For each individual row of the matrix, this row is sorted in non-decreasing order.
Given a row-sorted binary matrix binaryMatrix, return leftmost column index(0-indexed) with at least a 1 in it. If such index doesn't exist, return -1.
You can't access the Binary Matrix directly. You may only access the matrix using a BinaryMatrix interface:
BinaryMatrix.get(x, y) returns the element of the matrix at index (x, y) (0-indexed).
BinaryMatrix.dimensions() returns a list of 2 elements [m, n], which means the matrix is m * n.
Submissions making more than 1000 calls to BinaryMatrix.get will be judged Wrong Answer. Also, any solutions that attempt to circumvent the judge will result in disqualification.
For custom testing purposes you're given the binary matrix mat as input in the following four examples. You will not have access the binary matrix directly.
Example 1:
Input: mat = [[0,0],[1,1]]
Output: 0
Example 4:
Input: mat = [[0,0,0,1],[0,0,1,1],[0,1,1,1]]
Output: 1
Solution
# """
# This is BinaryMatrix's API interface.
# You should not implement it, or speculate about its implementation
# """
#class BinaryMatrix(object):
# def get(self, x: int, y: int) -> int:
# def dimensions(self) -> list[]:
class Solution:
def leftMostColumnWithOne(self, binaryMatrix):
n, m = binaryMatrix.dimensions()
ans = m
for i in range(n):
x = self.findOne(binaryMatrix, i, 0, m - 1)
if x != -1:
ans = min(ans, x)
return ans if ans != m else -1
def findOne(self, m, r, i, j):
if i == j:
return i if m.get(r, i) == 1 else -1
mid = (i + j) // 2
if m.get(r, mid) == 1:
if mid == 0 or m.get(r, mid - 1) == 0:
return mid
else:
return self.findOne(m, r, i, mid - 1)
else:
return self.findOne(m, r, mid+1, j)
560 Subarray Sum Equals K
Problem
Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.
Example 1:
Input:nums = [1,1,1], k = 2
Output: 2
Note:
The length of the array is in range [1, 20,000].
The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].
Solution
class Solution:
def subArraySum(self, nums, k):
h = {0: 1}
s = 0
ans = 0
for i in nums:
s += i
ans += h.get(s-k, 0)
h[s] = h.get(s, 0) + 1
return ans
201 - Bitwise AND of Numbers Range
Problem
Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.
Example 1:
Input: [5,7]
Output: 4
Example 2:
Input: [0,1]
Output: 0
Solution
m = xxx1yyyy n = xxx01zzz
xxx
is the parts that two numbers are the same.
We can definitly find these two numbers in the range
m' = xxx1000 n' = xxx0100
So the result will be xxx0000
The idea is the find position where the the numbers are the same.
class Solution:
def rangeBitwiseAnd(self, m, n):
i = 0
while m != n:
m >>= 1
n >>= 1
i += 1
return n << i
146 - LRU Cache
Problem
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
Solution
-
How to manage to have a O(1) for get()?
Hash Table
-
How to manage to have a O(1) for put()?
We have to somehow maintain its order, when something is added.
Linked list would come to mind at first. But how we can change its order when we call get() on a node and also be able to delete the last one. We would like to know the previous one of a node.
A Double Linked List is a perfect match.
class LRUCache:
def __init__(self, capacity: int):
self.count = 0
self.capacity = capacity
self.hash = {}
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key in self.hash:
node = self.hash[key]
self._remove(node)
self._add(node)
return node.value
else:
return -1
def put(self, key: int, value: int) -> None:
if key not in self.hash:
node = Node(key, value)
self.hash[key] = node
self.count += 1
self._add(node)
else:
node = self.hash[key]
node.value = value
self.get(key)
if self.count > self.capacity:
n = self.tail.prev
self.hash.pop(n.key)
self._remove(n)
self.count -= 1
def _connect(self, p, n):
p.next, n.prev = n, p
def _add(self, n):
tmp = self.head.next
self._connect(self.head, n)
self._connect(n, tmp)
def _remove(self, node):
self._connect(node.prev, node.next)
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.prev = None
self.next = None
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
460 - LFU Cache
Problem
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.
Note that the number of times an item is used is the number of calls to the get and put functions for that item since it was inserted. This number is set to zero when the item is removed.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LFUCache cache = new LFUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.get(3); // returns 3.
cache.put(4, 4); // evicts key 1.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
Solution
- O(1) get => Hash table for all nodes
- for keeping the oder by frequency, we can have another hash table. the key is the frequency. the value is a double linked list
- We need a min_freqeuncy to find the list of min frequency in O(1), and delete the last element of the list
class Node:
def __init__(self, key, value):
self.freq = 1
self.key = key
self.value = value
self.next = None
self.prev = None
class DoubleLinkedList:
def __init__(self):
self._head = Node(0, 0)
self._tail = Node(0, 0)
self._head.next = self._tail
self._tail.prev = self._head
self._size = 0
def append(self, node):
self._connect(node, self._head.next)
self._connect(self._head, node)
self._size += 1
def pop(self, node):
self._connect(node.prev, node.next)
node.next = None
node.prev = None
self._size -= 1
return node
def pop_last(self):
return self.pop(self._tail.prev)
def get_size(self):
return self._size
def _connect(self, p, n):
p.next, n.prev = n, p
class LFUCache:
def __init__(self, capacity):
self.nodes = {}
self.freq = {}
self.min_freq = 1
self.capacity = capacity
def get(self, key):
if key in self.nodes:
node = self.nodes[key]
l = self.freq[node.freq]
l.pop(node)
if l.get_size() == 0 and self.min_freq == node.freq:
self.min_freq += 1
node.freq += 1
if self.freq.get(node.freq):
self.freq[node.freq].append(node)
else:
l = DoubleLinkedList()
l.append(node)
self.freq[node.freq] = l
return node.value
else:
return -1
def put(self, key, value):
if self.capacity == 0:
return
if key in self.nodes:
self.nodes[key].value = value
self.get(key)
else:
node = Node(key, value)
if len(self.nodes) == self.capacity:
l = self.freq[self.min_freq]
n = l.pop_last()
self.nodes.pop(n.key)
self.nodes[key] = node
if 1 in self.freq:
self.freq[1].append(node)
else:
l = DoubleLinkedList()
l.append(node)
self.freq[1] = l
self.min_freq = 1
c = LFUCache(3)
c.put(1, 1)
c.put(2, 2)
print(c.get(1))
print(c.get(2))
print(c.get(1))
c.put(3, 3)
print(c.get(2))
print(c.get(3))
c.put(4, 4)
print(c.get(4))
437 - Path Sum III
Problem
You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
Return 3. The paths that sum to 8 are:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
Solution
Important:
Three parts in recursion:
- the path sum of the left child
- the path sum of the right child
- the path sum only from the root
class Solution:
def pathSum(self, root, sum):
if not root:
return 0
return self.pathSum(root.left, sum) + self.pathSum(
root.right, sum) + self.pathSumFrom(root, sum)
def pathSumFrom(self, root, sum):
if not root:
return 0
if root.val == sum:
return 1 + self.pathSumFrom(root.left, 0) + self.pathSumFrom(
root.right, 0)
else:
return self.pathSumFrom(root.left,
sum - root.val) + self.pathSumFrom(
root.right, sum - root.val)
1143 - Longest Common Subsequence
Problem
Given two strings text1 and text2, return the length of their longest common subsequence.
A subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters.
(eg, "ace" is a subsequence of "abcde" while "aec" is not).
A common subsequence of two strings is a subsequence that is common to both strings.
If there is no common subsequence, return 0.
Example 1:
Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace" and its length is 3.
Example 2:
Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.
Example 3:
Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.
Solution
The hardest part of this problem is to figure out what is the transition.
The current state should be determined by index i, j for each string respectively.
-
States: dp[i][j] means the longest common subsequence for text1 til index i and text2 til index j
-
Transition:
if text1[i]
= text2[j]: dp[i][j] = dp[i-1][j-1] if text1[i] !
text2[j]: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) -
Base Case: we need padding for i = 0 and j = 0
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
dp = [[0] * (len(text1) + 1) for i in range(len(text2) + 1)]
for i in range(len(text2)):
for j in range(len(text1)):
if text2[i] == text1[j]:
dp[i + 1][j + 1] = dp[i][j] + 1
else:
dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])
return dp[-1][-1]
887 - Super Egg Drop
Problem
You are given K eggs, and you have access to a building with N floors from 1 to N.
Each egg is identical in function, and if an egg breaks, you cannot drop it again.
You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.
Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).
Your goal is to know with certainty what the value of F is.
What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?
Example 1:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
Example 2:
Input: K = 2, N = 6
Output: 3
Example 3:
Input: K = 3, N = 14
Output: 4
Solution
Solution 1: dp
A naive approach with dp. Time consuming.
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
memo = dict()
def dp(K, N):
if K == 1:
return N
if N == 0:
return 0
if (K, N) in memo:
return memo[(K, N)]
res = float('INF')
for i in range(1, N+1):
res = min(res, max(dp(K-1, i-1), dp(K, N - i)) + 1)
memo[(K, N)] = res
return res
return dp(K, N)
Time: O(K*N*N)
Space: O(K*N)
Solution 2: dp with binary search
Improve the previous one with a binary search
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
memo = dict()
def dp(K, N):
if K == 1:
return N
if N == 0:
return 0
if (K, N) in memo:
return memo[(K, N)]
res = float('INF')
lo = 1
hi = N
while lo <= hi:
mid = (lo + hi) // 2
broken = dp(K - 1, mid - 1)
not_broken = dp(K, N - mid)
if broken > not_broken:
hi = mid - 1
res = min(res, broken + 1)
else:
lo = mid + 1
res = min(res, not_broken + 1)
memo[(K, N)] = res
return res
return dp(K, N)
print(Solution().superEggDrop(3, 14))
Time: O(K*N*logN)
Space: O(K*N)
Solution 3: dp with moves*eggs
Use a different transition.
Think about the problem as, given K eggs and M moves, what N can you at least reach.
class Solution:
def superEggDrop(self, K, N):
dp = [[0]*(K+1) for _ in range(N+1)]
m = 0
while dp[m][K] < N:
m += 1
for i in range(1, K+1):
dp[m][i] = dp[m-1][i] + dp[m-1][i-1] + 1
return m
print(Solution().superEggDrop(2, 6))
print(Solution().superEggDrop(3, 14))
Time: O(K*N)
Space: O(K*N)
The transition is only related two previous value, we can reduce the dp matrix two O(1).
class Solution:
def superEggDrop(self, K, N):
m = 0
dp = [0] * (K+1)
while dp[K] < N:
m += 1
prev = 0
for i in range(1, K+1):
tmp = dp[i]
dp[i] = dp[i] + prev + 1
prev = tmp
return m
28 - Implement strStr() (KMP Algorithm)
Problem
Implement strStr().
Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.
Example 1:
Input: haystack = "hello", needle = "ll"
Output: 2
Example 2:
Input: haystack = "aaaaa", needle = "bba"
Output: -1
Solution
class Solution:
def __init__(self, pat):
self.pat = pat
self.dp = []
self.KMP(self.pat)
def KMP(self, pat):
M = len(pat)
self.dp = [[0] * 256 for _ in range(M)]
self.dp[0][ord(pat[0])] = 1
X = 0
for j in range(1, M):
for c in range(256):
if ord(pat[j]) == c:
self.dp[j][c] = j + 1
else:
self.dp[j][c] = self.dp[X][c]
X = self.dp[X][ord(pat[j])]
def search(self, txt):
M = len(self.pat)
N = len(txt)
s = 0
for i in range(N):
s = self.dp[s][ord(txt[i])]
if s == M:
return i - M + 1
return -1
sol = Solution("ababc")
print(sol.search("ababdabababc"))
169 - Majority Element
Problem
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always exist in the array.
Example 1:
Input: [3,2,3] Output: 3
Example 2:
Input: [2,2,1,1,1,2,2] Output: 2
Solution
Solution 1: Hash Table
from collections import Counter
class Solution:
def majorityElement(self, nums):
c = Counter()
l = len(nums)
for n in nums:
c[n] += 1
if c[n] > l/2:
return n
print(Solution().majorityElement([1, 1, 2, 2, 2, 2, 4]))
Time: O(n)
Space: O(n)
Solution 2: Boyer-Moore Majority Vote Algorithm
http://www.cs.utexas.edu/~moore/best-ideas/mjrty/index.html
This algorithm works only when the majorty exists.
For array like [1, 1, 2, 2, 2, 1], it return 2. But it is not a majority element.
class Solution:
def majorityElement(self, nums):
counter = 1
ans = nums[0]
for n in nums[1:]:
if n == ans:
counter += 1
else:
counter -= 1
if counter < 0:
counter = 1
ans = n
return ans
print(Solution().majorityElement([1, 1, 2, 2, 2, 2, 4]))
Time: O(n)
Space: O(1)
448 - Find All Numbers Disappeard in an Array
Problem
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
Example:
Input: [4,3,2,7,8,2,3,1]
Output: [5,6]
Solution
Move all the numbers to its position. For example, 4 move to index 3, 3 move to index 2 and so on.
The numbers at index i, which are not equal i + 1, are the ones disappeared.
The operation is in-place.
class Solution:
def findDisappearedNumbers(self, nums):
for i in nums:
j = i
while nums[j-1] != j:
nums[j-1], j = j, nums[j-1]
return [i+1 for i in range(len(nums)) if nums[i] != i+1]
Time: O(N)
Space: O(1)
124 - Binary Tree Maximum Path Sum
Problem
Given a non-empty binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.
Example 1:
Input: [1,2,3]
1
/ \
2 3
Output: 6 Example 2:
Input: [-10,9,20,null,null,15,7]
-10
/ \
9 20
/ \
15 7
Output: 42
Solution
Important:
Remember we should find a path, not a sub tree.
Solution 1: Hash
from collections import defaultdict
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
h = defaultdict(int)
def maxPathSum(self, root: TreeNode) -> int:
if not root:
return -sys.maxsize
return max(self.maxPathSumFrom(root), self.maxPathSum(root.left),
self.maxPathSum(root.right))
def maxPathSumFrom(self, root):
if not root:
return 0
return max(
root.val, root.val + self.maxPathSumDfs(root.right),
root.val + self.maxPathSumDfs(root.left), root.val +
self.maxPathSumDfs(root.right) + self.maxPathSumDfs(root.left))
def maxPathSumDfs(self, root):
if not root:
return 0
if root in self.h:
return self.h[root]
else:
rightSum = self.h.get(root.right, self.maxPathSumDfs(root.right))
leftSum = self.h.get(root.left, self.maxPathSumDfs(root.left))
ans = max(root.val, root.val + rightSum, root.val + leftSum)
self.h[root] = ans
return ans
Solution 2: Global maximum with dfs
The tricky part here is to update the global maximum always with both left branch and right branch, but dfs only returns with the larger branch of the node, because we have to keep the left and right side as a “path” not a “tree
Version 1:
class Solution:
ans = -sys.maxsize
def maxPathSum(self, root):
self.dfs(root)
return self.ans
def dfs(self, root):
if not root:
return 0
left = max(self.dfs(root.left), 0)
right = max(self.dfs(root.right), 0)
self.ans = max(self.ans, root.val + left + right)
return root.val + max(left, right)
Version 2: I find it easier to understand
class Solution:
ans = -sys.maxsize
def maxPathSum(self, root):
self.dfs(root)
return self.ans
def dfs(self, root):
if not root:
return 0
left = self.dfs(root.left)
right = self.dfs(root.right)
val = max(root.val, root.val + left, root.val + right)
self.ans = max(self.ans, val, root.val + left + right)
return val
278 - First Bad Version
Problem
You are a product manager and currently leading a team to develop a new product.
Unfortunately, the latest version of your product fails the quality check.
Since each version is developed based on the previous version, all the versions after a bad version are also bad.
Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.
You are given an API bool isBadVersion(version) which will return whether version is bad.
Implement a function to find the first bad version. You should minimize the number of calls to the API.
Example:
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.
Solution
Binary Search is your friend!
Solution 1: Binary Search to the end
class Solution:
def firstBadVerion(self, n):
l, r, mid = 1, n, (1+n) // 2
while l != mid:
if isBadVersion(mid):
l = mid
else:
r = mid
mid = (l + r) // 2
return l if isBadVersion(l) else r
Solution 2: Binary Search with break
It should be faster then solution 1, because we do not wait for l meets mid, which might take longer.
class Solution:
def firstBadVersion(self, n):
if n == 1:
return 1
l, r = 2, n
while l <= r:
mid = (l + r) // 2
if isBadVersion(mid) and not isBadVersion(mid - 1):
return mid
elif isBadVersion(mid):
r = mid - 1
else:
l = mid + 1
return 1
106 - Construct Binary Tree from Inorder and Postorder Traversal
Problem
Given inorder and postorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.
For example, given
inorder = [9,3,15,20,7]
postorder = [9,15,7,20,3]
Return the following binary tree:
3
/ \
9 20
/ \
15 7
Solution
The problem is almost the same as 105 Construct BT from Preorder and Inorder. Notes can be found there.
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
if inorder:
node = TreeNode(postorder.pop())
split = inorder.index(node.val)
node.right = self.buildTree(inorder[split + 1:], postorder)
node.left = self.buildTree(inorder[:split], postorder)
return node
128 - Longest Consecutive Sequence
Problem
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
Your algorithm should run in O(n) complexity.
Example:
Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
Solution
Solution 1: Hash Map
For each value, we maintain the left bound and right bound of range and also update the bounds for left value and right value.
class Solution:
def longestConsecutive(self, nums):
h = dict()
ans = 0
for num in nums:
if num not in h:
if num - 1 in h and num + 1 in h:
h[h[num + 1][1]][0] = h[num - 1][0]
h[h[num - 1][0]][1] = h[num + 1][1]
h[num] = [h[num - 1][0], h[num + 1][1]]
elif num - 1 in h:
h[h[num - 1][0]][1] += 1
h[num] = h[h[num - 1][0]]
elif num + 1 in h:
h[h[num + 1][1]][0] -= 1
h[num] = h[h[num + 1][1]]
else:
h[num] = [num, num]
ans = max(ans, h[num][1] - h[num][0] + 1)
return ans
Time: O(n)
Space: O(n)
Solution 2: Keep Counting!
Notice the if x - t not in nums
.
class Solution:
def longestConsecutive(self, nums):
ans = 0
nums = set(nums)
for x in nums:
if x - 1 not in nums:
y = x + 1
while y in nums:
y += 1
ans = max(ans, y - x)
return ans
Time: O(n)
Space: O(n)
98 - Validate Binary Search Tree
Problem
Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than the node's key.
- Both the left and right subtrees must also be binary search trees.
Example 1:
2
/ \
1 3
Input: [2,1,3]
Output: true
Example 2:
5
/ \
1 4
/ \
3 6
Input: [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.
Solution
Important:
You don’t only valid the current node with its left and right value. You have to:
- pass the new high bound for left branch, because all the values in the left branch must be less than this high bound, low bound remains
- pass the new low bound for right branch, because all the values in the right branch must be greater than this low bound, high bound remains
class Solution:
def isValidBST(self, root):
return self.isValidBSTBound(root, -sys.maxsize, sys.maxisze)
def isValidBSTBound(self, root, lb, hb):
if not root:
return True
if lb < root.val < hb:
return self.isValidBSTBound(root.left, lb, root.val) and self.isValidBSTBound(root.right, root.val, hb)
else:
return False
1009 - Complement of Base 10 Integer
Problem
Every non-negative integer N has a binary representation. For example, 5 can be represented as "101" in binary, 11 as "1011" in binary, and so on. Note that except for N = 0, there are no leading zeroes in any binary representation.
The complement of a binary representation is the number in binary you get when changing every 1 to a 0 and 0 to a 1. For example, the complement of "101" in binary is "010" in binary.
For a given number N in base-10, return the complement of it's binary representation as a base-10 integer.
Example 1:
Input: 5
Output: 2
Explanation: 5 is "101" in binary, with complement "010" in binary, which is 2 in base-10.
Example 2:
Input: 7
Output: 0
Explanation: 7 is "111" in binary, with complement "000" in binary, which is 0 in base-10.
Example 3:
Input: 10
Output: 5
Explanation: 10 is "1010" in binary, with complement "0101" in binary, which is 5 in base-10.
Solution
Solution 1: XOR
Watch out the corner case when num is 0.
class Solution:
def bitwiseComplement(self, num: int) -> int:
if num == 0:
return 1
c = 1
n = num
while n:
c *= 2
n >>= 1
return num ^ (c - 1)
Solution 2:
x = 2 * x + 1
1 = 1
1*2 + 1 = 3 = 11
3*2 + 1 = 7 = 111
7*2 + 1 = 15 = 1111
class Solution:
def bitwiseComplement(self, num):
x = 1
while x < num: x = 2 * x + 1
return x - num
337 - House Robber III
Problem
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
Example 1:
Input: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
Output: 7
Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:
Input: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
Output: 9
Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
Solution
We can do it recursively with a memo, to reduce the search depth.
On every node, we have 2 statuses, which will determine our next move:
-
Status:
- Existence of current node
- Wether the parent node was robbed?
So we can define a recursive function with two parameters:
def robRec(root, isRobbed)
-
Transition:
-
If parent robbed
Then we can not rob the current node, we just sum up the result of the children
ans = self.robRec(root.left, False) + self.robRec(root.right, Falsek)
-
If not robbed
Then we have a choice: rob the current node or not.
-
if we rob the current node
rob = root.val + self.robRec(root.left, True) + self.robRec(root.right, True)
-
if we do not rob the current node
not_rob = self.robRec(root.left, False) + self.robRec(root.right, True)
-
And we have to choose the greater one
ans = max(rob, not_rob)
-
-
Initial state
root = root isRobbed = False
class Solution:
memo = {}
def rob(self, root):
return self.robRec(root, False)
def robRec(self, root, isRobbed):
if not root:
return 0
if (root, isRobbed) in memo:
return memo[(root, isRobbed)]
if isRobbed:
ans = self.robRec(root.left, False) + self.robRec(root.right, False)
else:
ans = max(root.val + self.robRec(root.left, True) + self.robRec(root.right, True),
self.robRec(root.left, False) + self.robRec(root.right, False))
memo[(root, isRobbed)] = ans
return ans
Cousins in Binary Tree
Problem
In a binary tree, the root node is at depth 0, and children of each depth k node are at depth k+1.
Two nodes of a binary tree are cousins if they have the same depth, but have different parents.
We are given the root of a binary tree with unique values, and the values x and y of two different nodes in the tree.
Return true if and only if the nodes corresponding to the values x and y are cousins.
Example 1:
Input: root = [1,2,3,4], x = 4, y = 3
Output: false
Example 2:
Input: root = [1,2,3,null,4,null,5], x = 5, y = 4
Output: true
Example 3:
Input: root = [1,2,3,null,4], x = 2, y = 3
Output: false
Note:
The number of nodes in the tree will be between 2 and 100.
Each node has a unique integer value from 1 to 100.
Solution
- depth funciton for depth comparison
- A map for remember the parent value of the node
class Solution:
m = {}
def isCousins(self, root, x, y):
if not root:
return False
if self.depth(root, x) == self.depth(root, y):
return self.m[x] != self.m[y]
return False
def depth(self, root, val):
if not root:
return -1
if root.val == val:
return 0
else:
left = self.depth(root.left, val)
right = self.depth(root.right, val)
if left >= 0:
self.m[root.left.val] = root.val
return 1 + left
elif right >= 0:
self.m[root.right.val] = root.val
return 1 + right
return -1
1232 - Check If It Is a Straight Line
Problem
You are given an array coordinates, coordinates[i] = [x, y],
where [x, y] represents the coordinate of a point.
Check if these points make a straight line in the XY plane.
Solution
Important
Notice the divider. It may be zero, when the lines are horizontal.
class Solution:
def checkStraightLine(self, coordinates: List[List[int]]) -> bool:
p1 = coordinates[0]
p2 = coordinates[1]
slope = self.slope(p1, p2)
for i in range(2, len(coordinates)):
if self.slope(coordinates[0], coordinates[i]) != slope:
return False
return True
def slope(self, p1, p2):
if p1[1] == p2[1]:
return sys.maxsize
return (p1[0] - p2[0]) / (p1[1] - p2[1])
733 - Flood Fill
Problem
An image is represented by a 2-D array of integers, each integer representing the pixel value of the image (from 0 to 65535).
Given a coordinate (sr, sc) representing the starting pixel (row and column) of the flood fill, and a pixel value newColor, "flood fill" the image.
To perform a "flood fill", consider the starting pixel, plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, plus any pixels connected 4-directionally to those pixels (also with the same color as the starting pixel), and so on. Replace the color of all of the aforementioned pixels with the newColor.
At the end, return the modified image.
Example 1:
Input:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
Output: [[2,2,2],[2,2,0],[2,0,1]]
Explanation:
From the center of the image (with position (sr, sc) = (1, 1)), all pixels connected
by a path of the same color as the starting pixel are colored with the new color.
Note the bottom corner is not colored 2, because it is not 4-directionally connected
to the starting pixel.
Note:
The length of image and image[0] will be in the range [1, 50].
The given starting pixel will satisfy 0 <= sr < image.length and 0 <= sc < image[0].length.
The value of each color in image[i][j] and newColor will be an integer in [0, 65535].
Solution
Stack.
- Add the starting point to stack
- Stack pop. Fill the point, add its valid neighbours to the stack.
- Repeat 2 until stack is empty
Important:
When the color to fill is the same as the starting point. We don’t need to continue. Otherwise you will be always adding the same points again and again into the stack.
class Solution:
def floodFill(self, image, sr: int, sc: int, newColor: int):
oldColor = image[sr][sc]
stack = []
if newColor != oldColor:
stack.append([sr, sc])
m = len(image)
n = len(image[0])
while stack:
p = stack.pop()
x, y = p
image[x][y] = newColor
for v in [[1, 0], [0, 1]]:
for s in [1, -1]:
r = v[0] * s
c = v[1] * s
if 0 <= x + r < m and 0 <= y + c < n and image[x + r][y + c] == oldColor:
stack.append([x + r, y + c])
return image
997 - Find the Town Judge
Problem
In a town, there are N people labelled from 1 to N.
There is a rumor that one of these people is secretly the town judge.
If the town judge exists, then:
1. The town judge trusts nobody.
2. Everybody (except for the town judge) trusts the town judge.
3. There is exactly one person that satisfies properties 1 and 2.
You are given trust, an array of pairs trust[i] = [a, b] representing that the person labelled a trusts the person labelled b.
If the town judge exists and can be identified, return the label of the town judge. Otherwise, return -1.
Example 1:
Input: N = 2, trust = [[1,2]]
Output: 2
Example 2:
Input: N = 3, trust = [[1,3],[2,3]]
Output: 3
Example 3:
Input: N = 3, trust = [[1,3],[2,3],[3,1]]
Output: -1
Example 4:
Input: N = 3, trust = [[1,2],[2,3]]
Output: -1
Example 5:
Input: N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
Output: 3
Note:
1 <= N <= 1000
trust.length <= 10000
trust[i] are all different
trust[i][0] != trust[i][1]
1 <= trust[i][0], trust[i][1] <= N
Solution
Two tables:
- one for counting how many trusts each person gets
- one for counting how many other people each person trusts
class Solution:
def findJudge(self, N: int, trust: List[List[int]]) -> int:
people = [0] * (N)
h = defaultdict(int)
for t in trust:
people[t[1]-1] += 1
h[t[0]-1] = 1
for i in range(N):
if people[i] >= N - 1 and h[i] == 0:
return i+1
return -1
540 - Single Element in a Sorted Array
Problem
You are given a sorted array consisting of only integers where every element appears exactly twice, except for one element which appears exactly once. Find this single element that appears only once.
Example 1:
Input: [1,1,2,3,3,4,4,8,8] Output: 2
Example 2:
Input: [3,3,7,7,10,11,11] Output: 10
Solution
class Solution:
def singleNonDuplicate(self, nums):
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if mid == 0:
return mid
if (mid - lo + 1) % 2 == 0:
if nums[mid] == nums[mid - 1]:
lo = mid + 1
else:
hi = mid - 1
else:
if nums[mid] == nums[mid - 1]:
hi = mid - 2
else:
lo = mid
return nums[lo]
print(Solution().singleNonDuplicate([1,1,2,3,3,5,5,10,10]))
402 - Remove K Digits
Problem
Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible.
Note: The length of num is less than 10002 and will be ≥ k. The given num does not contain any leading zero.
Example 1:
Input: num = “1432219”, k = 3 Output: “1219” Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.
Example 2:
Input: num = “10200”, k = 1 Output: “200” Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.
Example 3:
Input: num = “10”, k = 2 Output: “0” Explanation: Remove all the digits from the number and it is left with nothing which is 0.
Solution
Stack
Corner cases:
‘1234567’ k = 3: k will not be reduced to 0. So stack[:-k or None] ‘10200’ k = 1: k can be reduced to 0. stack[:-0] will result in []. So stack[:-k or None] ‘1000000’ k = 1: ……lstrip(‘0’) will result 0. So we have to add
or '0'
class Solution:
def removeKdigits(self, num, k):
stack = []
for c in num:
while k and stack and stack[-1] > c:
stack.pop()
k -= 1
stack.append(c)
return ''.join(stack[:-k or None]).lstrip('0') or '0'
208 - Implement Trie (Prefix Tree)
Problem
Implement a trie with insert, search, and startsWith methods.
Example:
Trie trie = new Trie();
trie.insert(“apple”); trie.search(“apple”); / returns true trie.search(“app”); / returns false trie.startsWith(“app”); // returns true trie.insert(“app”); trie.search(“app”); // returns true
Solution
Trie.
Use a “END” Symbol for word ending.
search
method checks for “END”, startsWith
not.
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = Node()
def insert(self, word: str) -> None:
"""
Inserts a word into the trie.
"""
node = self.root
for c in word:
node = node.next[c]
node.isWord = True
def search(self, word: str) -> bool:
"""
Returns if the word is in the trie.
"""
node = self.root
for c in word:
node = node.next.get(c)
if not node:
return False
return node.isWord
def startsWith(self, prefix: str) -> bool:
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
node = self.root
for c in prefix:
node = node.next.get(c)
if not node:
return False
return True
class Node:
def __init__(self):
self.next = defaultdict(Node)
self.isWord = False
s = Trie()
s.insert("apple")
s.insert("banana")
print(s.search("app"))
print(s.search("bananananana"))
print(s.search("banana"))
print(s.startsWith("ban"))
918 - Maximum Sum Circular Subarray
Problem
Given a circular array C of integers represented by A, find the maximum possible sum of a non-empty subarray of C.
Here, a circular array means the end of the array connects to the beginning of the array. (Formally, C[i] = A[i] when 0 <= i < A.length, and C[i+A.length] = C[i] when i >= 0.)
Also, a subarray may only include each element of the fixed buffer A at most once. (Formally, for a subarray C[i], C[i+1], …, C[j], there does not exist i <= k1, k2 <= j with k1 % A.length = k2 % A.length.)
Example 1:
Input: [1,-2,3,-2] Output: 3 Explanation: Subarray [3] has maximum sum 3
Example 2:
Input: [5,-3,5] Output: 10 Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10
Example 3:
Input: [3,-1,2,-1] Output: 4 Explanation: Subarray [2,-1,3] has maximum sum 2 + (-1) + 3 = 4
Example 4:
Input: [3,-2,2,-3] Output: 3 Explanation: Subarray [3] and [3,-2,2] both have maximum sum 3
Example 5:
Input: [-2,-3,-1] Output: -1 Explanation: Subarray [-1] has maximum sum -1
Note:
-30000 <= A[i] <= 30000 1 <= A.length <= 30000
Solution
Use dp we can solve the problem without circular easily. See problem 53. Maximum Subarray.
So we have a maxSum.
There are only two possibility where the sub array locates.
-
The sub array is in the loop of array
—-[—–]—– ————
It’s
maxSum
. -
The sub array connects the next loop.
———-[– —]———-
we can rearrange it to:
[—]——[–]
The the part outside the maximum subarray is “minimum subarray”.
And we can calculate the
minSum
with the same algorithm formaxSum
.
The results is
max(maxSum, s - minSum)
Corner case:
When all elements are negative, the maxSum is also negative. and s - minSum would be 0. max(maxSum, s - minSum) is 0.
But according to the description, we should return maxSum.
class Solution:
def maxSubarraySumCircular(self, A):
curMin = 0
curMax = 0
minSum = sys.maxsize
maxSum = -sys.maxsize
s = 0
for c in A:
s += c
curMax = max(c, curMax + c)
maxSum = max(maxSum, curMax)
curMin = min(c, curMin + c)
minSum = min(minSum, curMin)
return max(maxSum, s - minSum) if maxSum > 0 else maxSum
328 - Odd Even Linked List
Problem
Given a singly linked list, group all odd nodes together followed by the even nodes.
Please note here we are talking about the node number and not the value in the nodes.
You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.
Example 1:
Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL
Example 2:
Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL
Note:
The relative order inside both the even and odd groups should remain as it was in the input.
The first node is considered odd, the second node even and so on ...
Solution
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head or not head.next or not head.next.next:
return head
p1, p2 = head, head.next
even = p2
while p2 and p2.next:
p1.next = p2.next
p1 = p1.next
p2.next = p1.next
p2 = p2.next
p1.next = even
return head
438 - Find All Anagrams in a String
Problem
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.
Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.
The order of output does not matter.
Example 1:
Input:
s: "cbaebabacd" p: "abc"
Output:
[0, 6]
Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
Input:
s: "abab" p: "ab"
Output:
[0, 1, 2]
Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".
Solution
from collections import defaultdict
class Solution:
def findAllAnagrams(self, s, p):
target, window = defaultdict(int), defaultdict(int)
left, right = 0, 0
match = 0
ans = []
for c in p:
target[c] += 1
while right < len(s):
c = s[right]
if c in target:
window[c] += 1
if window[c] == target[c]:
match += 1
right += 1
while right - left + 1 > len(p):
if match == len(target):
ans.append(left)
c = s[left]
left += 1
if c in window:
if window[c] == target[c]:
match -= 1
window[c] -= 1
return ans
print(Solution().findAllAnagrams("cbaeabac", "abc"))
567 - Permutation in String
Problem
Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1.
In other words, one of the first string's permutations is the substring of the second string.
Example 1:
Input: s1 = "ab" s2 = "eidbaooo"
Output: True
Explanation: s2 contains one permutation of s1 ("ba").
Example 2:
Input:s1= "ab" s2 = "eidboaoo"
Output: False
Note:
The input strings only contain lower case letters.
The length of both given strings is in range [1, 10,000].
Accepted
Solution
Sliding window.
from collections import defaultdict
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
target, window = defaultdict(int), defaultdict(int)
left, right = 0, 0
match = 0
for c in s1:
target[c] += 1
while (right < len(s2)):
c = s2[right]
if c in target:
window[c] += 1
if window[c] == target[c]:
match += 1
right += 1
while (right - left + 1 > len(s1)):
if match == len(target):
return True
c = s2[left]
left += 1
if c in window:
if window[c] == target[c]:
match -= 1
window[c] -= 1
return False
901 - Onine Stock Span
Problem
Write a class StockSpanner which collects daily price quotes for some stock, and returns the span of that stock's price for the current day.
The span of the stock's price today is defined as the maximum number of consecutive days (starting from today and going backwards) for which the price of the stock was less than or equal to today's price.
For example, if the price of a stock over the next 7 days were [100, 80, 60, 70, 60, 75, 85], then the stock spans would be [1, 1, 1, 2, 1, 4, 6].
Example 1:
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.
Note:
Calls to StockSpanner.next(int price) will have 1 <= price <= 10^5.
There will be at most 10000 calls to StockSpanner.next per test case.
There will be at most 150000 calls to StockSpanner.next across all test cases.
The total time limit for this problem has been reduced by 75% for C++, and 50% for all other languages.
Solution
Stack problem.
Pop all the smaller one and calculate the result before pushing current one into stack.
class StockSpanner:
def __init__(self):
self.stack=[]
def next(self, price):
res=1
while self.stack and self.stack[-1][0]<=price:
res+=self.stack.pop()[1]
self.stack.append([price,res])
return res
230 - Kth Smallest Element in a BST
Problem
230. Kth Smallest Element in a BST
Medium
2239
57
Add to List
Share
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.
Note:
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.
Example 1:
Input: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
Output: 1
Example 2:
Input: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
Output: 3
Solution
class Solution:
def kthSmallest(self, root):
self.k = k
self.ans = root.val
traverse(root)
return self.ans
def traverse(self, root):
if not root:
return
traverse(root.left)
self.k -= 1
if not self.k:
self.ans = root.val
else:
traverse(root.right)
1277 - Count Square Submatrices with All Ones
Problem
Given a m * n matrix of ones and zeros, return how many square submatrices have all ones.
Example 1:
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.
Example 2:
Input: matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
Output: 7
Explanation:
There are 6 squares of side 1.
There is 1 square of side 2.
Total number of squares = 6 + 1 = 7.
Solution
DP problem. Same as p227.
class Solution:
def countSquares(self, matrix):
if not matrix or not matrix[0]:
return 0
m = len(matrix)
n = len(matrix[0])
ans = 0
for i in range(m):
for j in range(n):
if matrix[i][j] == 1:
if i and j:
matrix[i][j] = min(matrix[i-1][j-1], matrix[i][j-1], matrix[i-1][j]) + 1
ans += matrix[i][j]
return ans
76 - Minimum Window Substring
Problem
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
Note:
If there is no such window in S that covers all characters in T, return the empty string "".
If there is such window, you are guaranteed that there will always be only one unique minimum window in S.
Solution
class Solution:
def minWindow(self, s: str, t: str) -> str:
target, window = defaultdict(int), defaultdict(int)
left, right, match = 0, 0, 0
d = float("inf")
for c in t:
target[c] += 1
while right < len(s):
c = s[right]
if c in target:
window[c] += 1
if window[c] == target[c]:
match += 1
right += 1
while (match == len(target)):
if right - left < d:
ans = s[left:right]
d = right - left
c = s[left]
left += 1
if c in target:
if window[c] == target[c]:
match -= 1
window[c] -= 1
return "" if d == float("inf") else ans
451 - Sort Characters By Frequency
Problem
Given a string, sort it in decreasing order based on the frequency of characters.
Example 1:
Input:
"tree"
Output:
"eert"
Explanation:
'e' appears twice while 'r' and 't' both appear once.
So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
Example 2:
Input:
"cccaaa"
Output:
"cccaaa"
Explanation:
Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer.
Note that "cacaca" is incorrect, as the same characters must be together.
Example 3:
Input:
"Aabb"
Output:
"bbAa"
Explanation:
"bbaA" is also a valid answer, but "Aabb" is incorrect.
Note that 'A' and 'a' are treated as two different characters.
Solution
- Use a map to count the characters.
- Loop the map to the characters into a map using the frequency as key. The value will be a list, containing all the characters with the same frequency.
- Go through the map.
Solution 1: Two maps
from collections import defaultdict
class Solution:
def frequencySort(self, s: str) -> str:
cnt = defaultdict(int)
freq = {}
max_f = 0
ans = ""
for c in s:
cnt[c] += 1
for c, f in cnt.items():
if f not in freq:
freq[f] = []
freq[f].append(c)
max_f = max(max_f, f)
for i in range(max_f, 0, -1):
if i in freq:
for c in freq[i]:
ans += c * i
return ans
Solution 2: One liner
from collections import Counter
class Solution:
def frequencySort(self, s):
return "".join([c * cnt for c, cnt in Counter(s).most_common()])
Similar Problems
347. Top K Frequent Element
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = [1], k = 1
Output: [1]
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
It's guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
You can return the answer in any order.
Solution
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
return [ i for i, cnt in Counter(nums).most_common()[:k]]
692. Top K Frequent Words
Given a non-empty list of words, return the k most frequent elements.
Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.
Example 1:
Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
Output: ["i", "love"]
Explanation: "i" and "love" are the two most frequent words.
Note that "i" comes before "love" due to a lower alphabetical order.
Example 2:
Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
Output: ["the", "is", "sunny", "day"]
Explanation: "the", "is", "sunny" and "day" are the four most frequent words,
with the number of occurrence being 4, 3, 2 and 1 respectively.
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Input words contain only lowercase letters.
Follow up:
Try to solve it in O(n log k) time and O(n) extra space.
Solution
class Solution:
def topKFrequent(self, words: List[str], k: int) -> List[str]:
counter = Counter(words)
return heapq.nsmallest(k,
counter,
key=lambda word: (-counter[word], word))
986 - Interval List Intersections
Problem
Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order.
Return the intersection of these two interval lists.
(Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3].)
Example 1:
Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]]
Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists.
Solution
class Solution:
def intervalListIntersection(self, A, B):
ans = []
i = j = 0
while i < len(A) and j < len(B):
left = max(A[i][0], B[j][0])
right = min(A[i][1], B[j][1])
if left <= right:
ans.append([left, right])
if A[i][1] < B[j][1]:
i += 1
else:
j += 1
return ans
1035 - Uncrossed Lines
Problem
We write the integers of A and B (in the order they are given) on two separate horizontal lines.
Now, we may draw connecting lines: a straight line connecting two numbers A[i] and B[j] such that:
A[i] == B[j];
The line we draw does not intersect any other connecting (non-horizontal) line.
Note that a connecting lines cannot intersect even at the endpoints: each number can only belong to one connecting line.
Return the maximum number of connecting lines we can draw in this way.
Example 1:
Input: A = [1,4,2], B = [1,2,4]
Output: 2
Explanation: We can draw 2 uncrossed lines as in the diagram.
We cannot draw 3 uncrossed lines, because the line from A[1]=4 to B[2]=4 will intersect the line from A[2]=2 to B[1]=2.
Example 2:
Input: A = [2,5,1,2,5], B = [10,5,2,1,5,2]
Output: 3
Example 3:
Input: A = [1,3,7,1,7,5], B = [1,9,2,5,1]
Output: 2
Note:
1 <= A.length <= 500
1 <= B.length <= 500
1 <= A[i], B[i] <= 2000
Solution
class Solution:
def maxUncrossedLines(self, A, B):
dp = [[0] * (len(A)+1) for _ in range(len(B)+1)]
for i in range(len(B)):
for j in range(len(A)):
if B[i] == A[j]:
dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1], dp[i][j] + 1)
else:
dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1], dp[i][j])
return dp[-1][-1]
886 - Possible Bipartition
Problem
Given a set of N people (numbered 1, 2, ..., N), we would like to split everyone into two groups of any size.
Each person may dislike some other people, and they should not go into the same group.
Formally, if dislikes[i] = [a, b], it means it is not allowed to put the people numbered a and b into the same group.
Return true if and only if it is possible to split everyone into two groups in this way.
Example 1:
Input: N = 4, dislikes = [[1,2],[1,3],[2,4]]
Output: true
Explanation: group1 [1,4], group2 [2,3]
Example 2:
Input: N = 3, dislikes = [[1,2],[1,3],[2,3]]
Output: false
Example 3:
Input: N = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]]
Output: false
Note:
1. 1 <= N <= 2000
2. 0 <= dislikes.length <= 10000
3. 1 <= dislikes[i][j] <= N
4. dislikes[i][0] < dislikes[i][1]
5. There does not exist i != j for which dislikes[i] == dislikes[j].
Solution
Graph.
Every dislike is an edge in graph.
We color all vertice in two colors. Every adjacent vertice should have different colors.
Use DFS to color all vetices. If you can color all vertices without contradiction, then the graph has a bipartition.
class Solution:
def possibleBipartition(self, N: int, dislikes):
self.graph = [[] for _ in range(N+1)]
self.marked = [0] * (N+1)
for pair in dislikes:
v, w = pair
self.graph[v].append(w)
self.graph[w].append(v)
for v in range(1, N+1):
if not self.marked[v] and not self.dfs(v, 1):
return False
return True
def dfs(self, v, color):
self.marked[v] = color
for w in self.graph[v]:
if not self.marked[w] and not self.dfs(w, -color):
return False
elif self.marked[w] == color:
return False
return True
207 - Course Schedule
Problem
There are a total of numCourses courses you have to take, labeled from 0 to numCourses-1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example 1:
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.
Example 2:
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
Solution
Solution 1: DAG with DFS
class Solution:
def canFinish(self, numCourses, prerequisites):
self.hasCycle = False
self.adj = [[] for _ in range(numCourses)]
self.marked = [0] * numCourses
self.stack = [0] * numCourses
for pre in prerequisite:
v, w = pre
adj[v].append(w)
for v in range(numCourses):
if not self.marked[v]:
self.dfs(v)
return not self.hasCycle
def dfs(self, v):
self.marked[v] = 1
self.stack[v] = 1
for w in self.adj[v]:
if self.hasCycle:
return
elif not self.marked[w]:
self.dfs(w)
elif self.stack[w]:
self.hasCycle = True
Solution 2: Topological Sort
If a graph has a topological sort order, there is no cycle.
class Solution:
def canFinish(self, numCourses, prerequisites):
adj = [[] for _ in range(numCourses)]
degree = [0] * numCourses
for pre in prerequisites:
v, w = pre
adj[w].append(v)
degree[v] += 1
q = [v for v in range(numCourses) if not degree[v]]
order = []
while q:
v = q.pop(0)
order.append(v)
for w in adj[v]:
degree[w] -= 1
if not degree[w]:
q.append(w)
return len(order) == numCourses
210 - Course Schedule II
Problem
There are a total of n courses you have to take, labeled from 0 to n-1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.
There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.
Example 1:
Input: 2, [[1,0]]
Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished
course 0. So the correct course order is [0,1] .
Example 2:
Input: 4, [[1,0],[2,0],[3,1],[3,2]]
Output: [0,1,2,3] or [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both
courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0.
So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3] .
Solution
Topological sort.
class Solution:
def canFinish(self, numCourses, prerequisites):
adj = [[] for _ in range(numCourses)]
degree = [0] * numCourses
for pre in prerequisites:
v, w = pre
adj[w].append(v)
degree[v] += 1
q = [v for v in range(numCourses) if not degree[v]]
order = []
while q:
v = q.pop(0)
order.append(v)
for w in adj[v]:
degree[w] -= 1
if not degree[w]:
q.append(w)
return len(order) == numCourses
973 - K Closest Points to Origin
Problem
We have a list of points on the plane. Find the K closest points to the origin (0, 0).
(Here, the distance between two points on a plane is the Euclidean distance.)
You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.)
Example 1:
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]].
Example 2:
Input: points = [[3,3],[5,-1],[-2,4]], K = 2
Output: [[3,3],[-2,4]]
(The answer [[-2,4],[3,3]] would also be accepted.)
Solution
Solution 1: sort
class Solution:
def kClosest(self, points, K: int):
return sorted(points, key=lambda p: p[0]**2 + p[1]**2)[:K]
Solution 2: heap
class Solution:
def kClosest(self, points, K):
return heapq.nsmallest(K, points, lambda (x, y): x * x + y * y)
72 - Edit Distance
Problem
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
Insert a character
Delete a character
Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:
Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')
Solution
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0] * (1+len(word2)) for _ in range(1+len(word1))]
for i in range(1+len(word1)):
dp[i][0] = i
for j in range(1+len(word2)):
dp[0][j] = j
for i in range(1, 1+len(word1)):
for j in range(1, 1+len(word2)):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1
return dp[-1][-1]
226 - Invert Binary Tree
Problem
Invert a binary tree.
Example:
Input:
4
/ \
2 7
/ \ / \
1 3 6 9
Output:
4
/ \
7 2
/ \ / \
9 6 3 1
Solution
class Solution:
def invertTree(self):
if root:
tmp = root.left
root.left = self.invertTree(root.right)
root.right = self.invertTree(tmp)
return root
746 - Min Cost Climbing Stairs
Problem
On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed).
Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, and you can either start from the step with index 0, or the step with index 1.
Example 1:
Input: cost = [10, 15, 20]
Output: 15
Explanation: Cheapest is start on cost[1], pay that cost and go to the top.
Example 2:
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].
Note:
cost will have a length in the range [2, 1000].
Every cost[i] will be an integer in the range [0, 999].
Solution
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
dp = [float('inf')] * len(cost)
dp[0] = dp[1] = 0
for i in range(2, len(cost)):
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
return min(dp[-1]+cost[-1], dp[-2]+cost[-2])
237 - Delete Node in a Linked List
Problem
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.
Given linked list -- head = [4,5,1,9], which looks like following:
Example 1:
Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.
Example 2:
Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list should become 4 -> 5 -> 9 after calling your function.
Solution
class Solution:
def deleteNode(self, node):
node.val = node.next.val
node.next = node.next.next
1029 - Two City Scheduling
Problem
There are 2N people a company is planning to interview. The cost of flying the i-th person to city A is costs[i][0], and the cost of flying the i-th person to city B is costs[i][1].
Return the minimum cost to fly every person to a city such that exactly N people arrive in each city.
Example 1:
Input: [[10,20],[30,200],[400,50],[30,20]]
Output: 110
Explanation:
The first person goes to city A for a cost of 10.
The second person goes to city A for a cost of 30.
The third person goes to city B for a cost of 50.
The fourth person goes to city B for a cost of 20.
The total minimum cost is 10 + 30 + 50 + 20 = 110 to have half the people interviewing in each city.
Note:
1 <= costs.length <= 100
It is guaranteed that costs.length is even.
1 <= costs[i][0], costs[i][1] <= 1000
Solution
Solution 1: DP
class Solution:
def twoCitySchedCost(self, costs: List[List[int]]) -> int:
N = len(costs) // 2
dp = [[[float('inf')] * (N + 1), [float('inf')] * (N + 1)]
for _ in range(2 * N + 1)]
dp[0][0][0] = 0
dp[0][1][0] = 0
for i in range(1, 2 * N + 1):
for k in range(1, N + 1):
if i - k >= 0 and i - k <= N:
dp[i][0][k] = min(dp[i - 1][0][k - 1],
dp[i - 1][1][i - k]) + costs[i - 1][0]
dp[i][1][k] = min(dp[i - 1][1][k - 1],
dp[i - 1][0][i - k]) + costs[i - 1][1]
return min(dp[-1][0][-1], dp[-1][1][-1])
Solution 2: sort
class Solution:
def twoCitySchedCost(self, costs):
sortedCosts = sorted(costs, key=lambda cost: cost[0] - cost[1])
minCost = 0
N = len(costs)
for i in range(N//2):
minCost += sortedCosts[i][0]
minCost += sortedCosts[N//2+i][1]
return minCost
528 - Random Pick with Weight
Problem
Given an array w of positive integers, where w[i] describes the weight of index i, write a function pickIndex which randomly picks an index in proportion to its weight.
Note:
1 <= w.length <= 10000
1 <= w[i] <= 10^5
pickIndex will be called at most 10000 times.
Example 1:
Input:
["Solution","pickIndex"]
[[[1]],[]]
Output: [null,0]
Example 2:
Input:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
Output: [null,0,1,1,1,0]
Explanation of Input Syntax:
The input is two lists: the subroutines called and their arguments. Solution's constructor has one argument, the array w. pickIndex has no arguments. Arguments are always wrapped with a list, even if there aren't any.
Solution
class Solution:
def __init__(self, w):
self.w = list(itertools.accumulate(w))
def pickIndex(self):
return bisect.bisect_left(self.w, random.randint(1, self.w[-1]))
518 - Coin Change 2
Problem
You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.
Example 1:
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
Example 2:
Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.
Example 3:
Input: amount = 10, coins = [10]
Output: 1
Note:
You can assume that
0 <= amount <= 5000
1 <= coin <= 5000
the number of coins is less than 500
the answer is guaranteed to fit into signed 32-bit integer
Solution
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount + 1)
dp[0] = 1
for i in coins:
for j in range(i, amount + 1):
dp[j] += dp[j - i]
return dp[-1]
231 - Power of Two
Problem
Given an integer, write a function to determine if it is a power of two.
Example 1:
Input: 1
Output: true
Explanation: 20 = 1
Example 2:
Input: 16
Output: true
Explanation: 24 = 16
Example 3:
Input: 218
Output: false
Solution
Solution 1: Straight forward
class Solution:
def powerOfTwo(self, n):
while n != 1:
if n % 2:
return False
n //= 2
return True
Solution 2: Bit manipulation
power of two: n = 1000000 n - 1 = 111111 n & (n-1) == 0
Note: n can not be 0
class Solution:
def powerOfTwo(self, n):
return n and not (n & n-1)
406 - Queue Reconstruction by Height
Problem
Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.
Note:
The number of people is less than 1,100.
Example
Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
Solution
class Solution:
def main(self, people):
people.sort(key=lambda p: (-p[0], p[1]))
ans = []
for p in people:
ans.insert(p[1], p)
213 - House Robber II
Problem
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
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.
Example 2:
Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
Solution
Two loops of dp:
- consider the array from 0 to n - 2
- consider the array from 1 to n - 1
- return the max value from this two loops
class Solution:
def rob(self, nums):
if not nums:
return 0
if len(nums) == 1:
return nums[0]
rob = nrob = rob2 = nrob2 = 0
for n in range(1, len(nums)):
rob, nrob = nrob + nums[n], max(rob, nrob)
rob2, nrob2 = nrob2 + nums[n - 1], max(rob2, nrob2)
return max(rob, nrob, rob2, nrob2)
38 - Count and Say
Problem
The count-and-say sequence is the sequence of integers with the first five terms as following:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1 is read off as "one 1" or 11.
11 is read off as "two 1s" or 21.
21 is read off as "one 2, then one 1" or 1211.
Given an integer n where 1 ≤ n ≤ 30, generate the nth term of the count-and-say sequence. You can do so recursively, in other words from the previous member read off the digits, counting the number of digits in groups of the same digit.
Note: Each term of the sequence of integers will be represented as a string.
Example 1:
Input: 1
Output: "1"
Explanation: This is the base case.
Example 2:
Input: 4
Output: "1211"
Explanation: For n = 3 the term was "21" in which we have two groups "2" and "1", "2" can be read as "12" which means frequency = 1 and value = 2, the same way "1" is read as "11", so the answer is the concatenation of "12" and "11" which is "1211".
Solution
class Solution:
def countAndSay(self, n: int) -> str:
s = "1"
for i in range(n-1):
say = s[0]
count = 0
tmp = ""
for c in s:
if c == say:
count += 1
else:
tmp = tmp + str(count) + say
say = c
count = 1
s = tmp + str(count) + say
return s
66 - Plus One
Problem
Given a non-empty array of digits representing a non-negative integer, plus one to the integer.
The digits are stored such that the most significant digit is at the head of the list, and each element in the array contain a single digit.
You may assume the integer does not contain any leading zero, except the number 0 itself.
Example 1:
Input: [1,2,3]
Output: [1,2,4]
Explanation: The array represents the integer 123.
Example 2:
Input: [4,3,2,1]
Output: [4,3,2,2]
Explanation: The array represents the integer 4321.
Solution
class Solution:
def plusOne(self, digits):
add = 1
for i in range(len(digits) - 1, -1, -1):
digits[i], add = (digits[i] + add) % 10, (digits[i] + add)//10
if add:
digits.insert(0, add)
return digits
392 - Is Subsequence
Problem
Given a string s and a string t, check if s is subsequence of t.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ace" is a subsequence of "abcde" while "aec" is not).
Follow up:
If there are lots of incoming S, say S1, S2, ... , Sk where k >= 1B, and you want to check one by one to see if T has its subsequence. In this scenario, how would you change your code?
Credits:
Special thanks to @pbrother for adding this problem and creating all test cases.
Example 1:
Input: s = "abc", t = "ahbgdc"
Output: true
Example 2:
Input: s = "axc", t = "ahbgdc"
Output: false
Solution
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
i = 0
for c in t:
if i < len(s) and c == s[i]:
i += 1
return i == len(s)
40 - Combination Sum II
Problem
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]
Solution
Backtracking problem.
Use DFS.
- make a choice
- call dfs recursive for all rest choices
- revert the choice
Notes:
- sort the candidates and avoid the duplicates
- return early when the target is smaller than 0
class Solution:
def combinationSum(self, candidates, target):
self.ans = []
candidates.sort()
self.dfs(candidates, 0, [], target)
return self.ans
def dfs(self, candidates, index, path, target):
if target < 0:
return
if target == 0:
self.ans.append(list(path))
for i in range(index, len(candidates)):
if i > index and candidates[i] == candidates[i-1]:
continue
path.append(candidates[i])
self.dfs(i+1, candidates, path, target-candidates[i])
path.pop()
39 - Combination Sum
Problem
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target),
find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
[7],
[2,2,3]
]
Example 2:
Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
Solution
Backtracking problem.
See Problem 40.
class Solution:
def combinationSum(self, candidates, target):
self.ans = []
self.dfs(sorted(candidates), 0, [], target)
return self.ans
def dfs(self, candidates, index, path, target):
if target == 0:
self.ans.append(list(path))
for i in range(index, len(candidates)):
v = candidates[i]
if v > target:
return
path.append(v)
self.dfs(candidates, i, path, target-v)
path.pop()
377 - Combination Sum IV
Problem
Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Example:
nums = [1, 2, 3]
target = 4
The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
Note that different sequences are counted as different combinations.
Therefore the output is 7.
Solution
It develops from DFS and then optimize it to DP problem
Consider you always choose a num as the leading number in the combination sequence.
Then for a target, the current combination would be:
for i in nums:
dp[target] += dp[target - i]
Note here that we should have dp[target - i] already calculdated beforehand. So we need to do it from bottom-up. And the base case would be dp[0] = 1, because when i == target, we want dp[target - i] = 1 at first, it means we find the combination.
dp[0] = 1
for t in range(target+1):
for i in nums:
dp[target] += dp[target - i]
Solution 1: DFS
class Solution:
def combinationSum4(self, nums, target):
self.ans = 0
self.dfs(nums, target)
return self.ans
def dfs(self, nums, target):
if target < 0:
return
if target == 0:
self.ans += 1
for i in nums:
self.dfs(nums, target - i)
It exceeds time limit.
Solution 2: DFS with memo
class Solution:
def combinationSum4(self, nums, target):
memo = [0] * (target+1)
self.dfs(nums, target, memo)
return memo[target]
def dfs(self, nums, target, memo):
if target < 0:
return 0
if memo[target]:
return memo[target]
if target == 0:
return 1
ans = 0
for i in nums:
ans += self.dfs(nums, target - i, memo)
memo[target] = ans
return ans
It still exceeds time limit.
Solution 3: DP
class Solution:
def combinationSum4(self, nums, target):
dp = [0] * (target + 1)
dp[0] = 1
for i in range(target+1):
for j in nums:
if i < j:
continue
dp[i] += dp[i-j]
return dp[-1]
216 - Combination Sum III
Problem
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
Note:
All numbers will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: k = 3, n = 7
Output: [[1,2,4]]
Example 2:
Input: k = 3, n = 9
Output: [[1,2,6], [1,3,5], [2,3,4]]
Solution
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
ans = []
self.dfs(1, [], ans, k, n)
return ans
def dfs(self, index, path, ans, k, n):
if k == 0 and n == 0:
ans.append(list(path))
return
for i in range(index, 10):
if i > n:
return
path.append(i)
self.dfs(i+1, path, ans, k-1, n-i)
path.pop()
47 - Permutations II
Problem
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
Example:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
Solution
The tricky part is how to avoid duplicates.
First sort the array.
When chose the next element, if it is the same as the previous one and the previous one is now not choosen, we should skip it, because it means the previous one has been already choosen for this position before.
if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
continue
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
marked = [False] * len(nums)
ans = []
self.dfs(sorted(nums), marked, [], ans)
return ans
def dfs(self, nums, marked, path, ans):
if len(path) == len(nums):
ans.append(list(path))
return
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
continue
if marked[i]:
continue
marked[i] = True
path.append(nums[i])
self.dfs(nums, marked, path, ans)
marked[i] = False
path.pop()
51 - N-Queens
Problem
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.
Solution
class Solution:
def solveNQueens(self, n):
ans = []
self.dfs(0, n, [], ans)
return ans
def dfs(self, row, n, path, ans):
if row == n and len(path) == n:
ans.append(self.draw(path, n))
for i in range(n):
if self.attack(path, row, i):
continue
path.append([row, i])
self.dfs(row+1, n, path, ans)
path.pop()
def attack(self, path, row, i):
for pair in path:
if pair[1] == i:
return True
elif abs(row - pair[0]) == abs(i - pair[1]):
return True
return False
def draw(self, path, n):
ans = []
for pair in path:
ans.append("." * pair[1] + "Q" + "." * (n-1-pair[1]))
return ans
52 - N-Queens II
Problem
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return the number of distinct solutions to the n-queens puzzle.
Solution
class Solution:
def totalNQueens(self, n):
self.ans = 0
self.dfs(0, n, [])
return self.ans
def dfs(self, row, n, path):
if row == n and len(path) == n:
self.ans += 1
for i in range(n):
if self.attack(path, row, i):
continue
path.append([row, i])
self.dfs(row+1, n, path)
path.pop()
def attack(self, path, row, i):
for pair in path:
if pair[1] == i:
return True
elif abs(row - pair[0]) == abs(i - pair[1]):
return True
return False
77 - Combinations
Problem
Share
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
Example:
Input: n = 4, k = 2
Output:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
Solution
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
ans = []
self.dfs(1, n, k, [], ans)
return ans
def dfs(self, index, n, k, path, ans):
if k == 0:
ans.append(list(path))
return
for i in range(index, n+1):
path.append(i)
self.dfs(i+1, n, k-1, path, ans)
path.pop()
90 - Subsets II
Problem
Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
Solution
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
ans = []
marked = [False] * len(nums)
self.dfs(sorted(nums), 0, [], ans, marked)
return ans
def dfs(self, nums, index, path, ans, marked):
ans.append(list(path))
for i in range(index, len(nums)):
if i > 0 and nums[i] == nums[i-1] and not marked[i-1]:
continue
marked[i] = True
path.append(nums[i])
self.dfs(nums, i+1, path, ans, marked)
path.pop()
marked[i] = False
100 - Same Tree
Problem
Given two binary trees, write a function to check if they are the same or not.
Two binary trees are considered the same if they are structurally identical and the nodes have the same value.
Example 1:
Input: 1 1
/ \ / \
2 3 2 3
[1,2,3], [1,2,3]
Output: true
Example 2:
Input: 1 1
/ \
2 2
[1,2], [1,null,2]
Output: false
Example 3:
Input: 1 1
/ \ / \
2 1 1 2
[1,2,1], [1,1,2]
Output: false
Solution
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if p and not q:
return False
if not p and q:
return False
if not p and not q:
return True
if p.val == q.val:
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
else:
return False
112 - Path Sum
Problem
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
Solution
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
if not root:
return False
if sum - root.val == 0 and not root.right and not root.left:
return True
else:
return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum-root.val)
113 - Path Sum II
Problem
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
Return:
[
[5,4,11,2],
[5,8,4,5]
]
Solution
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
ans = []
self.dfs(root, sum, [], ans)
return ans
def dfs(self, root, sum, path, ans):
if not root:
return
path.append(root.val)
if root.val == sum and not root.right and not root.left:
ans.append(list(path))
self.dfs(root.left, sum - root.val, path, ans)
self.dfs(root.right, sum - root.val, path, ans)
path.pop()
118 - Pascal’s Triangle
Problem
Given a non-negative integer numRows, generate the first numRows of Pascal's triangle.
Example:
Input: 5
Output:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
Solution
class Solution:
def generate(self, numRows: int) -> List[List[int]]:
ans = []
for i in range(1, numRows+1):
level = [1] * i
if ans:
for j in range(1, i-1):
level[j] = ans[-1][j-1] + ans[-1][j]
ans.append(level)
return ans
54 - Spiral Matrix
Problem
Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.
Example 1:
Input:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:
Input:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
Solution
Clockwise turn with help of step and direction array
step = [[0, 1], [1, 0], [0, -1], [-1, 0]]
di = 0
i, i = i + step[di][0], j + step[di][1]
Clockwise Turn:
di = (di + 1) % 4
i, j = i+step[di][0], j + step[di][1]
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
i = j = 0
m = len(matrix)
n = len(matrix[0])
marked = [[False] * n for _ in matrix]
step = [[0, 1], [1, 0], [0, -1], [-1, 0]]
i = j = di = 0
ans = []
for _ in range(m*n):
ans.append(matrix[i][j])
marked[i][j] = True
ni, nj = i + step[di][0], j + step[di][1]
if 0 <= ni < m and 0 <= nj < n and not marked[ni][nj]:
i, j = ni, nj
else:
di = (di + 1) % 4
i, j = i+step[di][0], j + step[di][1]
return ans
58 - Length of Last Word
Problem
Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word (last word means the last appearing word if we loop from left to right) in the string.
If the last word does not exist, return 0.
Note: A word is defined as a maximal substring consisting of non-space characters only.
Example:
Input: "Hello World"
Output: 5
Solution
class Solution:
def lengthOfLastWord(self, s: str) -> int:
if s.split():
return len(s.split()[-1])
else:
return 0
59 - Spiral Matrix II
Problem
Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order.
Example:
Input: 3
Output:
[
[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]
]
Solution
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0] * n for _ in range(n)]
step = [[0,1], [1, 0], [0, -1], [-1, 0]]
i = j = di = 0
for x in range(1, n * n+1):
matrix[i][j] = x
ni = i + step[di][0]
nj = j + step[di][1]
if 0 <= ni < n and 0 <= nj < n and not matrix[ni][nj]:
i, j = ni, nj
else:
di = (di + 1) % 4
i, j = i + step[di][0], j + step[di][1]
return matrix
61 - Rotate List
Problem
Given a linked list, rotate the list to the right by k places, where k is non-negative.
Example 1:
Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:
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
Solution
Note: k may be larger than the total count of nodes. We sould preprocess k first
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head or not k:
return head
first = second = head
count = 0
while first:
count += 1
first = first.next
k = k % count
first = head
for i in range(k):
first = first.next
while first.next:
first = first.next
second = second.next
first.next = head
head = second.next
second.next = None
return head
63 - Unique Paths II
Problem
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).
Now consider if some obstacles are added to the grids. How many unique paths would there be?
An obstacle and empty space is marked as 1 and 0 respectively in the grid.
Note: m and n will be at most 100.
Example 1:
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
Solution
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid:
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0] * (n+1) for _ in range(m+1)]
dp[0][1] = 1
for i in range(m):
for j in range(n):
if not obstacleGrid[i][j]:
dp[i+1][j+1] = dp[i+1][j] + dp[i][j+1]
return dp[-1][-1]
74 - Search a 2D Matrix
Problem
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
Integers in each row are sorted from left to right.
The first integer of each row is greater than the last integer of the previous row.
Example 1:
Input:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
Output: true
Example 2:
Input:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
Output: false
Solution
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if not matrix or not matrix[0]:
return False
rl, rr = 0, len(matrix) - 1
cl, cr = 0, len(matrix[0]) - 1
while rl <= rr:
mid = (rl + rr) // 2
if target < matrix[mid][0]:
rr = mid - 1
elif target > matrix[mid][-1]:
rl = mid + 1
else:
rl = mid
break
if rl > rr:
return False
while cl <= cr:
mid = (cl + cr) // 2
if target < matrix[rl][mid]:
cr = mid - 1
elif target > matrix[rl][mid]:
cl = mid + 1
else:
return True
return False
162 - Find Peak Element
Problem
A peak element is an element that is greater than its neighbors.
Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that nums[-1] = nums[n] = -∞.
Example 1:
Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.
Example 2:
Input: nums = [1,2,1,3,5,6,4]
Output: 1 or 5
Explanation: Your function can return either index number 1 where the peak element is 2,
or index number 5 where the peak element is 6.
Solution
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
if len(nums) == 1:
return 0
if len(nums) == 2:
return 0 if nums[0] > nums[1] else 1
l = 1
r = len(nums) - 2
while l <= r:
mid = (r + l) // 2
if nums[mid-1] < nums[mid] > nums[mid+1]:
return mid
elif nums[mid] <= nums[mid+1]:
l = mid + 1
else:
r = mid - 1
if nums[0] > nums[1]:
return 0
elif nums[-1] > nums[-2]:
return len(nums) - 1
83 - Remove Duplicates from Sorted List
Problem
Given a sorted linked list, delete all duplicates such that each element appear only once.
Example 1:
Input: 1->1->2
Output: 1->2
Example 2:
Input: 1->1->2->3->3
Output: 1->2->3
Solution
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
first = head
second = first.next
while second:
if first.val == second.val:
first.next = second.next
second = first.next
else:
first, second = second, second.next
return head
95 - Unique Binary Search Trees II
Problem
Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ... n.
Example:
Input: 3
Output:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
Explanation:
The above output corresponds to the 5 unique BST's shown below:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
Solution
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
if n == 0:
return []
return self.helper(1, n+1)
def helper(self, start, end):
if start >= end:
return [None]
res = []
for i in range(start, end):
for left in self.helper(start, i):
for right in self.helper(i+1, end):
node = TreeNode(i)
node.left, node.right = left, right
res.append(node)
return res
222 - Count Complete Tree Nodes
Problem
Given a complete binary tree, count the number of nodes.
Note:
Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.
Example:
Input:
1
/ \
2 3
/ \ /
4 5 6
Output: 6
Solution
class Solution:
def countNodes(self, root: TreeNode) -> int:
if not root:
return 0
rd = self.height(root.right)
ld = self.height(root.left)
if rd == ld:
return pow(2, ld) + self.countNodes(root.right)
else:
return pow(2, rd) + self.countNodes(root.left)
def height(self, root):
if not root:
return 0
return 1 + self.height(root.left)
130 - Surrounded Regions
Problem
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'.
A region is captured by flipping all 'O's into 'X's in that surrounded region.
Example:
X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X
Explanation:
Surrounded regions shouldn’t be on the border, which means that any 'O' on the border of the board are not flipped to 'X'.
Any 'O' that is not on the border and it is not connected to an 'O' on the border will be flipped to 'X'.
Two cells are connected if they are adjacent cells connected horizontally or vertically.
Solution
class Solution:
def solve(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
if not board or not board[0]:
return
marked = [[False] * len(board[0]) for _ in board]
for i in [0, len(board)-1]:
for j in range(len(board[0])):
if board[i][j] == 'O':
self.color(board, i, j, 'O', 'S')
for i in range(1, len(board)-1):
for j in [0, len(board[0])-1]:
if board[i][j] == 'O':
self.color(board, i, j, 'O', 'S')
for i in range(len(board)):
for j in range(len(board[0])):
board[i][j] = 'OX'[board[i][j] !='S']
def color(self, board, i, j, target, symbol):
if i >= len(board) or j >= len(board[0]) or i < 0 or j < 0:
return
if board[i][j] == target:
board[i][j] = symbol
for v, w in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
self.color(board, i+v, j+w, target, symbol)
129 - Sum Root to Leaf Numbers
Problem
Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
Note: A leaf is a node with no children.
Example:
Input: [1,2,3]
1
/ \
2 3
Output: 25
Explanation:
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Therefore, sum = 12 + 13 = 25.
Example 2:
Input: [4,9,0,5,1]
4
/ \
9 0
/ \
5 1
Output: 1026
Explanation:
The root-to-leaf path 4->9->5 represents the number 495.
The root-to-leaf path 4->9->1 represents the number 491.
The root-to-leaf path 4->0 represents the number 40.
Therefore, sum = 495 + 491 + 40 = 1026.
Solution
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sumNumbers(self, root: TreeNode) -> int:
if not root:
return 0
self.ans = 0
self.dfs(root, [])
return self.ans
def dfs(self, root, path):
if not root:
return
if not root.right and not root.left:
path.append(root.val)
self.ans += int("".join([str(i) for i in path]))
path.pop()
return
path.append(root.val)
self.dfs(root.left, path)
self.dfs(root.right, path)
path.pop()
988 - Smallest String Starting From Leaf
Problem
Given the root of a binary tree, each node has a value from 0 to 25 representing the letters 'a' to 'z': a value of 0 represents 'a', a value of 1 represents 'b', and so on.
Find the lexicographically smallest string that starts at a leaf of this tree and ends at the root.
(As a reminder, any shorter prefix of a string is lexicographically smaller: for example, "ab" is lexicographically smaller than "aba". A leaf of a node is a node that has no children.
Example 1:
Input: [0,1,2,3,4,3,4]
Output: "dba"
Example 2:
Input: [25,1,3,1,3,0,2]
Output: "adz"
Solution
class Solution:
def smallestFromLeaf(self, root: TreeNode) -> str:
self.ans = "~"
self.dfs(root, [])
return self.ans
def dfs(self, root, path):
if not root:
return
path.append(chr(root.val+97))
if not root.left and not root.right:
self.ans = min(self.ans, "".join(reversed(path)))
self.dfs(root.left, path)
self.dfs(root.right, path)
path.pop()
303 - Range Sum Query - Immutable
Problem
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
Example:
Given nums = [-2, 0, 3, -5, 2, -1]
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
Note:
You may assume that the array does not change.
There are many calls to sumRange function.
Solution
class NumArray:
def __init__(self, nums: List[int]):
self.dp = list(nums)
self.dp.insert(0, 0)
for i in range(1, len(nums)):
self.dp[i+1] += self.dp[i]
def sumRange(self, i: int, j: int) -> int:
return self.dp[j+1] - self.dp[i]
264 - Ugly Number II
Problem
Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.
Example:
Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note:
1 is typically treated as an ugly number.
n does not exceed 1690.
Solution
class Solution:
ugly = sorted(2**a * 3**b * 5**c for a in range(32) for b in range(32) for c in range(32))
def nthUglyNumber(self, n: int) -> int:
return self.ugly[n-1]
263 - Ugly Number
Problem
Write a program to check whether a given number is an ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5.
Example 1:
Input: 6
Output: true
Explanation: 6 = 2 × 3
Example 2:
Input: 8
Output: true
Explanation: 8 = 2 × 2 × 2
Example 3:
Input: 14
Output: false
Explanation: 14 is not ugly since it includes another prime factor 7.
Note:
1 is typically treated as an ugly number.
Input is within the 32-bit signed integer range: [−231, 231 − 1].
Solution
class Solution:
def isUgly(self, num: int) -> bool:
if num <= 0:
return False
while num != 1:
tmp = num
for n in [2, 3, 5]:
if num % n == 0:
num //= n
if tmp == num:
return False
return True
332 - Reconstruct Itinerary
Problem
Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK.
Note:
If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"].
All airports are represented by three capital letters (IATA code).
You may assume all tickets form at least one valid itinerary.
One must use all the tickets once and only once.
Example 1:
Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
Output: ["JFK", "MUC", "LHR", "SFO", "SJC"]
Example 2:
Input: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]
Explanation: Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"].
But it is larger in lexical order.
Solution
Eulerian Path: a path in graph that visits every edge exactly once.
Eulerian Circuit is an Eulerian Path which starts and ends on the same vertex.
Hierholzer’s algorithm:
Modify the graph dfs to backtrack the vertices if there is no other unvisited edges of that vertice.
while edges[vertice]:
dfs(edges[vertice].pop())
path.append(vertice)
For this problem, we have to notice the lexical order for choices during dfs.
class Solution:
def findItinerary(self, tickets: List[List[str]]) -> List[str]:
edges = defaultdict(list)
for v, w in sorted(tickets)[::-1]:
edges[v] += [w]
path = []
self.dfs("JFK", edges, path)
return path[::-1]
def dfs(self, vertice, edges, path):
while(edges[vertice]):
self.dfs(edges[vertice].pop(), edges, path)
path.append(vertice)
212 - Word Search II
Problem
Given a 2D board and a list of words from the dictionary, find all words in the board.
Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.
Example:
Input:
board = [
['o','a','a','n'],
['e','t','a','e'],
['i','h','k','r'],
['i','f','l','v']
]
words = ["oath","pea","eat","rain"]
Output: ["eat","oath"]
Note:
All inputs are consist of lowercase letters a-z.
The values of words are distinct.
Solution
DFS the matrix.
To break the dfs earlier, a Trie data structure is introduced to check wether the path is a valid combination
class TrieNode:
def __init__(self):
self.children = defaultdict(TrieNode)
self.isWord = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for w in word:
node = node.children[w]
node.isWord = True
class Solution:
def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
trie = Trie()
node = trie.root
for w in words:
trie.insert(w)
ans = []
for i in range(len(board)):
for j in range(len(board[0])):
self.dfs(board, i, j, node, "", ans)
return ans
def dfs(self, board, i, j, node, path, ans):
if node.isWord:
ans.append(path)
node.isWord = False
if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] not in node.children:
return
val = board[i][j]
board[i][j] = "#"
for pair in [[0, 1], [1, 0], [0, -1], [-1, 0]]:
self.dfs(board, i+pair[0], j+pair[1], node.children[val], path+val, ans)
board[i][j] = val
441 - Arranging Coins
Problem
You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins.
Given n, find the total number of full staircase rows that can be formed.
n is a non-negative integer and fits within the range of a 32-bit signed integer.
Example 1:
n = 5
The coins can form the following rows:
¤
¤ ¤
¤ ¤
Because the 3rd row is incomplete, we return 2.
Example 2:
n = 8
The coins can form the following rows:
¤
¤ ¤
¤ ¤ ¤
¤ ¤
Because the 4th row is incomplete, we return 3.
Solution
class Solution:
def arrangeCoins(self, n: int) -> int:
k = 0
while n > 0:
k += 1
n -= k
return k if not n else k - 1
107 - Binary Tree Level Order Traversal II
Problem
Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its bottom-up level order traversal as:
[
[15,7],
[9,20],
[3]
]
Solution
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
queue = [root]
stack = []
while queue:
next_queue = []
tmp = []
while queue:
n = queue.pop(0)
tmp.append(n.val)
if n.left:
next_queue.append(n.left)
if n.right:
next_queue.append(n.right)
stack.append(tmp[:])
queue = next_queue[:]
return stack[::-1]
463 - Island Perimeter
Problem
You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water.
Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells).
The island doesn't have "lakes" (water inside that isn't connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don't exceed 100. Determine the perimeter of the island.
Solution
class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:
if not grid or not grid[0]:
return 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j]:
return self.dfs(grid, i, j)
return 0
def dfs(self, grid, i, j):
if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]):
return 1
if grid[i][j] == -1:
return 0
if grid[i][j]:
grid[i][j] = -1
return self.dfs(grid, i + 1, j) + self.dfs(
grid, i, j + 1) + self.dfs(grid, i - 1, j) + self.dfs(
grid, i, j - 1)
else:
return 1
695 - Max Area of Island
Problem
Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.
Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.)
Example 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
Given the above grid, return 6. Note the answer is not 11, because the island must be connected 4-directionally.
Example 2:
[[0,0,0,0,0,0,0,0]]
Given the above grid, return 0.
Note: The length of each dimension in the given grid does not exceed 50.
Solution
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
if not grid or not grid[0]:
return 0
ans = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] > 0:
ans = max(ans, self.dfs(grid, i, j))
return ans
def dfs(self, grid, i, j):
if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or not grid[i][j]:
return 0
grid[i][j] = 0
count = 1
count += self.dfs(grid, i+1, j)
count += self.dfs(grid, i-1, j)
count += self.dfs(grid, i, j+1)
count += self.dfs(grid, i, j-1)
return count
15 - 3SUM
Problem
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
Solution
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums) < 3:
return []
ans = []
nums.sort()
for i in range(0, len(nums)-2):
if nums[i] > 0:
break
if i > 0 and nums[i-1] == nums[i]:
continue
left, right = i+1, len(nums)-1
while right > left:
s = nums[left] + nums[right] + nums[i]
if s == 0:
ans.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
while right > left and nums[left] == nums[left-1]:
left += 1
while right > left and nums[right] == nums[right+1]:
right -= 1
elif s < 0:
left += 1
else:
right -= 1
return ans
10 - Regular Expression Matching
Problem
Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
Note:
s could be empty and contains only lowercase letters a-z.
p could be empty and contains only lowercase letters a-z, and characters like . or *.
Example 1:
Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:
Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".
Example 4:
Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".
Example 5:
Input:
s = "mississippi"
p = "mis*is*p*."
Output: false
Solution
class Solution:
def isMatch(self, s: str, p: str) -> bool:
dp = [[False] * (len(s)+1) for _ in range(len(p)+1)]
dp[0][0] = True
for i in range(len(p)):
if p[i] == "*" and dp[i-1][0]:
dp[i+1][0] = True
for i in range(len(p)):
for j in range(len(s)):
if p[i] == s[j]:
dp[i+1][j+1] = dp[i][j]
elif p[i] == '.':
dp[i+1][j+1] = dp[i][j]
elif p[i] == '*':
if p[i-1] != s[j] and p[i-1] != '.':
dp[i+1][j+1] = dp[i-1][j+1]
else:
dp[i+1][j+1] = dp[i][j+1] or dp[i+1][j] or dp[i-1][j+1]
return dp[-1][-1]
37 - Sudoku Solver
Problem
Write a program to solve a Sudoku puzzle by filling the empty cells.
A sudoku solution must satisfy all of the following rules:
Each of the digits 1-9 must occur exactly once in each row.
Each of the digits 1-9 must occur exactly once in each column.
Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.
Empty cells are indicated by the character '.'.
Note:
The given board contain only digits 1-9 and the character '.'.
You may assume that the given Sudoku puzzle will have a single unique solution.
The given board size is always 9x9.
Solution
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
self.finish = False
self.solve(board, 0, 0)
def solve(self, board, i, j):
if i >= len(board):
self.finish = True
return
next_i, next_j = i + (j + 1) // 9, (j + 1) % 9
if board[i][j] == '.':
for n in range(1, 10):
if self.is_safe(board, i, j, str(n)):
board[i][j] = str(n)
self.solve(board, next_i, next_j)
if self.finish:
return
board[i][j] = '.'
else:
self.solve(board, next_i, next_j)
def is_safe(self, board, i, j, n):
return self.is_safe_vertical(
board, i, j, n) and self.is_safe_horizontal(
board, i, j, n) and self.is_safe_sub(board, i, j, n)
def is_safe_vertical(self, board, i, j, n):
return n not in [a[j] for a in board]
def is_safe_horizontal(self, board, i, j, n):
return n not in board[i]
def is_safe_sub(self, board, i, j, n):
p1 = i // 3 * 3
p2 = j // 3 * 3
for x in range(p1, p1 + 3):
for y in range(p2, p2 + 3):
if board[x][y] == n:
return False
return True
36 - Valid Sudoku
Problem
Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:
Each row must contain the digits 1-9 without repetition.
Each column must contain the digits 1-9 without repetition.
Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.
Solution
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
for row in board:
nums = [x for x in row if x != '.' ]
if self.notValid(nums):
return False
for i in range(len(board[0])):
col = [row[i] for row in board]
nums = [x for x in col if x != '.' ]
if self.notValid(nums):
return False
for i in [0, 3, 6]:
for j in [0, 3, 6]:
nums = [x for row in board[i:i+3] for x in row[j:j+3] if x != '.']
if self.notValid(nums):
return False
return True
def notValid(self, nums):
return len(set(nums)) != len(nums)
190 - Reverse Bits
Problem
Reverse bits of a given 32 bits unsigned integer.
Example 1:
Input: 00000010100101000001111010011100
Output: 00111001011110000010100101000000
Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 which its binary representation is 00111001011110000010100101000000.
Example 2:
Input: 11111111111111111111111111111101
Output: 10111111111111111111111111111111
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471 which its binary representation is 10111111111111111111111111111111.
Note:
Note that in some languages such as Java, there is no unsigned integer type. In this case, both input and output will be given as signed integer type and should not affect your implementation, as the internal binary representation of the integer is the same whether it is signed or unsigned.
In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 2 above the input represents the signed integer -3 and the output represents the signed integer -1073741825.
Solution
Solution 1: Reverse bit one by one
class Solution:
def reverseBits(self, n: int) -> int:
res = 0
shift = 31
while n:
res += (n & 1) << shift
n = n >> 1
shift -= 1
return res
Solution 2: Reverse bit with masks
- First we swith first 16 bits and last 16 bits
- Repeat this operation in each half part. For example switch the first 8 bits and last 8 bits in the left 16 bits. Do the same in the right one
- Until only switch one bit
class Solution:
def reverseBits(self, n: int) -> int:
n = (n << 16 | n >> 16)
n = ((n & 0xff00ff00) >> 8 | (n & 0x00ff00ff) << 8)
n = ((n & 0xf0f0f0f0) >> 4 | (n & 0x0f0f0f0f) << 4)
n = ((n & 0xcccccccc) >> 2 | (n & 0x33333333) << 2)
n = ((n & 0xaaaaaaaa) >> 1 | (n & 0x55555555) << 1)
return n
461 - Hamming Distance
Problem
The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
Given two integers x and y, calculate the Hamming distance.
Note:
0 ≤ x, y < 231.
Example:
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.
Accepted
Solution
Solution 1:
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
z = x ^ y
s = 0
while z:
s += z & 1
z = z >> 1
return s
Solution 2: t & (t - 1)
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
z = x ^ y
s = 0
while z:
s += 1
z = z & (z - 1)
return s
32 - Longest Valid Parentheses
Problem
Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"
Solution
class Solution:
def longestValidParentheses(self, s: str) -> int:
stack = [-1]
ans = 0
for i in range(len(s)):
if s[i] == '(':
stack.append(i)
else:
if stack:
stack.pop()
if stack:
ans = max(ans, i - stack[-1])
else:
stack.append(i)
return ans
71 - Simplify Path
Problem
Given an absolute path for a file (Unix-style), simplify it. Or in other words, convert it to the canonical path.
In a UNIX-style file system, a period . refers to the current directory. Furthermore, a double period .. moves the directory up a level.
Note that the returned canonical path must always begin with a slash /, and there must be only a single slash / between two directory names. The last directory name (if it exists) must not end with a trailing /. Also, the canonical path must be the shortest string representing the absolute path.
Example 1:
Input: "/home/"
Output: "/home"
Explanation: Note that there is no trailing slash after the last directory name.
Example 2:
Input: "/../"
Output: "/"
Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go.
Example 3:
Input: "/home//foo/"
Output: "/home/foo"
Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one.
Example 4:
Input: "/a/./b/../../c/"
Output: "/c"
Example 5:
Input: "/a/../../b/../c//.//"
Output: "/c"
Example 6:
Input: "/a//b////c/d//././/.."
Output: "/a/b/c"
Solution
class Solution:
def simplifyPath(self, path: str) -> str:
stack = []
for p in path.split('/'):
if p == '..':
if stack:
stack.pop()
elif p and p != '.':
stack.append(p)
return '/' + "/".join(stack)
73 - Set Matrix Zeroes
Problem
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place.
Example 1:
Input:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
Output:
[
[1,0,1],
[0,0,0],
[1,0,1]
]
Example 2:
Input:
[
[0,1,2,0],
[3,4,5,2],
[1,3,1,5]
]
Output:
[
[0,0,0,0],
[0,4,5,0],
[0,3,1,0]
]
Follow up:
A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?
Solution
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
col_zero = False
for i in range(len(matrix)):
if matrix[i][0] == 0:
col_zero = True
for j in range(1, len(matrix[0])):
if matrix[i][j] == 0:
matrix[0][j] = matrix[i][0] = 0
for i in range(1, len(matrix)):
for j in range(1, len(matrix[0])):
if not matrix[0][j] or not matrix[i][0]:
matrix[i][j] = 0
if matrix[0][0] == 0:
for j in range(len(matrix[0])):
matrix[0][j] = 0
if col_zero:
for i in range(len(matrix)):
matrix[i][0] = 0
80 - Remove Duplicates from Sorted Array II
Problem
Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twice and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Example 1:
Given nums = [1,1,1,2,2,3],
Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3 respectively.
It doesn't matter what you leave beyond the returned length.
Example 2:
Given nums = [0,0,1,1,1,1,2,3,3],
Your function should return length = 7, with the first seven elements of nums being modified to 0, 0, 1, 1, 2, 3 and 3 respectively.
It doesn't matter what values are set beyond the returned length.
Solution
Keep a pointer to copy the value from behind.
Very interesting solution: n > nums[i-k], where k = 2
class Solution:
def removeDuplicates(self, nums):
i = 0
k = 2
for n in nums:
if i < k or n > nums[i-k]:
nums[i] = n
i += 1
return i
81 - Search in Rotated Sorted Array II
Problem
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,0,1,2,2,5,6] might become [2,5,6,0,0,1,2]).
You are given a target value to search. If found in the array return true, otherwise return false.
Example 1:
Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true
Example 2:
Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false
Follow up:
This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates.
Would this affect the run-time complexity? How and why?
Solution
Note:
Consider the problem in two part:
- when the left part is in order
- when the right part is in order
Important: How to guarentee the left part is in order when there is duplicates in the array:
We can use a while
to eliminate the duplicates if nums[left] == nums[mid]
class Solution:
def search(self, nums: List[int], target: int) -> bool:
if not nums:
return False
left = 0
right = len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
return True
while left < mid and nums[left] == nums[mid]:
left += 1
if nums[mid] >= nums[left]:
if nums[mid] > target >= nums[left]:
right = mid - 1
else:
left = mid + 1
else:
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
print(left, right)
return target == nums[left]
191 - Number of 1 Bits
Problem
Write a function that takes an unsigned integer and return the number of '1' bits it has (also known as the Hamming weight).
Example 1:
Input: 00000000000000000000000000001011
Output: 3
Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits.
Example 2:
Input: 00000000000000000000000010000000
Output: 1
Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit.
Example 3:
Input: 11111111111111111111111111111101
Output: 31
Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.
Solution
class Solution:
def hammingWeight(self, n: int) -> int:
ans = 0
while n:
ans += n % 2
n //= 2
return ans
82 - Remove Duplicates from Sorted List II
Problem
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
Return the linked list sorted as well.
Example 1:
Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:
Input: 1->1->1->2->3
Output: 2->3
Solution
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
extra = ListNode(None)
extra.next = head
pre = extra
while head and head.next:
if head.val == head.next.val:
while head and head.next and head.val == head.next.val:
head = head.next
head = head.next
pre.next = head
else:
pre = head
head = head.next
return extra.next
240 - Search a 2D Matrix II
Problem
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
Integers in each row are sorted in ascending from left to right.
Integers in each column are sorted in ascending from top to bottom.
Example:
Consider the following matrix:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
Given target = 5, return true.
Given target = 20, return false.
Solution
Solution 1
class Solution:
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
j = 0
for row in matrix:
if row[j] > target:
while row[j] > target:
j -= 1
if j < 0:
return False
elif row[j] < target:
while row[j] < target:
j += 1
if j == len(matrix[0]):
j = 0
break
if row[j] == target:
return True
return False
Solution 2: Like a binary tree
Start from upper right corner.
If matrix[i][j] > target: col - 1
if matrix[i][j] < target: row + 1
class Solution:
def searchMatrix(self, matrix, target):
if not matrix or not matrix[0]:
return False
i, j = 0, len(matrix[0]) - 1
while i < len(matrix) and j >= 0 :
if matrix[i][j] == target:
return True
elif matrix[i][j] > target:
j -= 1
else:
i += 1
return False
1103 - Distribute Candies to People
Problem
We distribute some number of candies, to a row of n = num_people people in the following way:
We then give 1 candy to the first person, 2 candies to the second person, and so on until we give n candies to the last person.
Then, we go back to the start of the row, giving n + 1 candies to the first person, n + 2 candies to the second person, and so on until we give 2 * n candies to the last person.
This process repeats (with us giving one more candy each time, and moving to the start of the row after we reach the end) until we run out of candies. The last person will receive all of our remaining candies (not necessarily one more than the previous gift).
Return an array (of length num_people and sum candies) that represents the final distribution of candies.
Example 1:
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].
Example 2:
Input: candies = 10, num_people = 3
Output: [5,2,3]
Explanation:
On the first turn, ans[0] += 1, and the array is [1,0,0].
On the second turn, ans[1] += 2, and the array is [1,2,0].
On the third turn, ans[2] += 3, and the array is [1,2,3].
On the fourth turn, ans[0] += 4, and the final array is [5,2,3].
Solution
class Solution:
def distributeCandies(self, candies: int, num_people: int) -> List[int]:
i = 1
ans = [0] * num_people
while candies >= 0:
ans[(i-1) % num_people] += min(candies, i)
candies -= i
i += 1
return ans
148 - Sort List
Problem
Sort a linked list in O(n log n) time using constant space complexity.
Example 1:
Input: 4->2->1->3
Output: 1->2->3->4
Example 2:
Input: -1->5->3->4->0
Output: -1->0->3->4->5
Solution
Merge Sort
- Slow-Fast-Pointer to split the list into half. Do not forget slow.next = None
- Sort two part recursively and merge together
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
# split in half
fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
second = slow.next
slow.next = None
first = self.sortList(head)
second = self.sortList(second)
# merge
return self.merge(first, second)
def merge(self, p1, p2):
p = dummy = ListNode(0)
while p1 and p2:
if p1.val < p2.val:
p.next, p1 = p1, p1.next
else:
p.next, p2 = p2, p2.next
p = p.next
p.next = p1 or p2
return dummy.next
142 - Reorder List
Problem
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
You may not modify the values in the list's nodes, only nodes itself may be changed.
Example 1:
Given 1->2->3->4, reorder it to 1->4->2->3.
Example 2:
Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
Solution
class Solution:
def reorderList(self, head: ListNode) -> None:
if not head or not head.next:
return
fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
p1, p2 = slow, slow.next
slow.next = None
while p2:
p2.next, p2, p1 = p1, p2.next, p2
first = head
tail = p1
while tail and first:
first.next, tail.next, first, tail = tail, first.next, first.next, tail.next
967 - Numbers With Same Consecutive Differences
Problem
Return all non-negative integers of length N such that the absolute difference between every two consecutive digits is K.
Note that every number in the answer must not have leading zeros except for the number 0 itself. For example, 01 has one leading zero and is invalid, but 0 is valid.
You may return the answer in any order.
Example 1:
Input: N = 3, K = 7
Output: [181,292,707,818,929]
Explanation: Note that 070 is not a valid number, because it has leading zeroes.
Example 2:
Input: N = 2, K = 1
Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98]
Solution
It is a simple queue problem.
Two corner cases to notice:
- remember to include 0 in the answers if the N == 1
- if K == 0, do not add +K, -K twice. They have the same value.
class Solution:
def numsSameConsecDiff(self, N: int, K: int) -> List[int]:
ans = [i for i in range(1, 10)]
if N == 1:
return [0] + ans
digits = 10 ** (N-1)
while ans[0] / digits < 1:
cur = ans.pop(0)
last_digit = cur % 10
if K == 0:
ans.append(cur * 10 + last_digit)
else:
if last_digit + K < 10:
ans.append(cur * 10 + last_digit + K)
if last_digit - K >= 0:
ans.append(cur * 10 + last_digit - K)
return ans
97 - Interleaving String
Problem
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2.
Example 1:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
Output: true
Example 2:
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
Output: false
Solution
DP problem.
Imagin you have a matrix with s1 as its row and s2 as its column.
You have to find a path from upper left corner to bottom right corner, which consisits the s3.
So when you walk the path, you have two options:
- you come from left. dp[i][j] = dp[i][j-1] and s1[j-1] == s3[i+j-1]
- you come from above. dp[i][j] = dp[i][j-1] and s2[i-1] == s3[i+j-1]
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
if len(s1) + len(s2) != len(s3):
return False
dp = [[False] * (len(s1)+1) for _ in range(len(s2)+1)]
for i in range(len(s2)+1):
for j in range(len(s1)+1):
if not i and not j:
dp[i][j] = True
elif not i:
dp[i][j] = dp[0][j-1] and s1[j-1] == s3[j-1]
elif not j:
dp[i][j] = dp[i-1][0] and s2[i-1] == s3[i-1]
else:
dp[i][j] = (dp[i-1][j] and s2[i-1] == s3[i+j-1]) or (dp[i][j-1] and s1[j-1] == s3[i+j-1])
return dp[-1][-1]
108 - Convert Sorted Array to Binary Search Tree
Problem
Given an array where elements are sorted in ascending order, convert it to a height balanced BST.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
Example:
Given the sorted array: [-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
Solution
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
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
109 - Convert Sorted List to Binary Search Tree
Problem
Given the head of a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
Solution
class Solution:
def sortedListToBST(self, head: ListNode) -> TreeNode:
if not head:
return None
if not head.next:
return TreeNode(head.val)
pre, slow, fast = None, head, head
while fast and fast.next:
pre, slow, fast = slow, slow.next, fast.next.next
pre.next = None
root = TreeNode(slow.val)
root.left = self.sortedListToBST(head)
root.right = self.sortedListToBST(slow.next)
return root
110 - Balanced Binary Tree
Problem
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as:
a binary tree in which the left and right subtrees of every node differ in height by no more than 1.
Example 1:
Given the following tree [3,9,20,null,null,15,7]:
3
/ \
9 20
/ \
15 7
Return true.
Example 2:
Given the following tree [1,2,2,3,3,null,null,4,4]:
1
/ \
2 2
/ \
3 3
/ \
4 4
Return false.
Solution
Solution 1: multiple pass
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
if not root:
return True
hr = self.height(root.right)
hl = self.height(root.left)
return self.isBalanced(root.right) and self.isBalanced(
root.left) and abs(hr - hl) <= 1
def height(self, root):
if not root:
return 0
return 1 + max(self.height(root.right), self.height(root.left))
This method has to calculate the height mutiple times, which costs a lot of time.
Solution 2: one pass
It is a dfs method, which only go over all nodes one time.
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
return self.height(root) != -1
def height(self, root):
if not root:
return 0
lh = self.height(root.right)
if lh == -1:
return -1
rh = self.height(root.left)
if rh == -1:
return -1
if abs(lh - rh) > 1:
return -1
return max(lh, rh) + 1
111 - Minimum Depth of Binary Tree
Problem
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
Note: A leaf is a node with no children.
Example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its minimum depth = 2.
Solution
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root:
return 0
if not root.right:
return self.minDepth(root.left) + 1
elif not root.left:
return self.minDepth(root.right) + 1
else:
return min(self.minDepth(root.right), self.minDepth(root.left)) + 1
class Solution:
def minDepth(self, root):
if not root: return 0
dl = self.minDepth(root.left)
dr = self.minDepth(root.right)
return 1 + (min(dl, dr) or max(dl, dr))
824 - Goat Latin
Problem
A sentence S is given, composed of words separated by spaces. Each word consists of lowercase and uppercase letters only.
We would like to convert the sentence to "Goat Latin" (a made-up language similar to Pig Latin.)
The rules of Goat Latin are as follows:
If a word begins with a vowel (a, e, i, o, or u), append "ma" to the end of the word.
For example, the word 'apple' becomes 'applema'.
If a word begins with a consonant (i.e. not a vowel), remove the first letter and append it to the end, then add "ma".
For example, the word "goat" becomes "oatgma".
Add one letter 'a' to the end of each word per its word index in the sentence, starting with 1.
For example, the first word gets "a" added to the end, the second word gets "aa" added to the end and so on.
Return the final sentence representing the conversion from S to Goat Latin.
Example 1:
Input: "I speak Goat Latin"
Output: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa"
Example 2:
Input: "The quick brown fox jumped over the lazy dog"
Output: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa"
Notes:
S contains only uppercase, lowercase and spaces. Exactly one space between each word.
1 <= S.length <= 150.
Solution
Interesting one-liner
class Solution:
def toGoatLatin(self, S: str) -> str:
return ' '.join([(lambda w: w[1:]+w[0] if w[0] not in 'aeiouAEIOU' else w)(w) + 'ma' + 'a' * i for i, w in enumerate(S.split(' '), 1)])
114 - Flatten Binary Tree to Linked List
Problem
Given a binary tree, flatten it to a linked list in-place.
For example, given the following tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1
\
2
\
3
\
4
\
5
\
6
Solution
- Traverse the tree in reverse preorder, the opposite of root-left-right.
- Save the root, and use it in the upper level.
class Solution:
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
self.pre = None
self.dfs(root)
def dfs(self, root):
if not root:
return
self.dfs(root.right)
self.dfs(root.left)
root.right = self.pre
self.pre = root
root.left = None
368 - Largest Divisible Subset
Problem
Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies:
Si % Sj = 0 or Sj % Si = 0.
If there are multiple solutions, return any subset is fine.
Example 1:
Input: [1,2,3]
Output: [1,2] (of course, [1,3] will also be ok)
Example 2:
Input: [1,2,4,8]
Output: [1,2,4,8]
Solution
Solution 1: DP
class Solution:
def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
if not nums:
return nums
dp = [1] * len(nums)
pre = [i for i in range(len(nums))]
nums.sort()
max_i = 0
for i in range(len(dp)):
for j in range(i):
if nums[i] % nums[j] == 0 and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1
pre[i] = j
if dp[i] > dp[max_i]:
max_i = i
ans = []
i = max_i
while pre[i] != i:
ans.append(nums[i])
i = pre[i]
ans.append(nums[i])
return ans
Solution 2: Hash
- Sort the numbers
- Store the answers for every number while go through the numbers
class Solution:
def largestDivisibleSubset(self, nums):
S = {-1: set()}
H = {}
for n in sorted(nums):
H[n] = max((H[d] for d in S if x % d == 0), key=len) | {n}
return list(max(S.values(), key=len))
784 - Letter Case Permutation
Problem
Given a string S, we can transform every letter individually to be lowercase or uppercase to create another string.
Return a list of all possible strings we could create. You can return the output in any order.
Example 1:
Input: S = "a1b2"
Output: ["a1b2","a1B2","A1b2","A1B2"]
Example 2:
Input: S = "3z4"
Output: ["3z4","3Z4"]
Example 3:
Input: S = "12345"
Output: ["12345"]
Example 4:
Input: S = "0"
Output: ["0"]
Constraints:
S will be a string with length between 1 and 12.
S will consist only of letters or digits.
Solution
class Solution:
def letterCasePermutation(self, S: str) -> List[str]:
ans = []
self.dfs(S, 0, ans, [])
return ans
def dfs(self, S, i, ans, path):
if i == len(S):
ans.append(''.join(path))
return
self.dfs(S, i+1, ans, path + [S[i]])
if S[i].isalpha():
self.dfs(S, i+1, ans, path + [S[i].swapcase()])
1079 - Letter Tile Possibilities
Problem
You have n tiles, where each tile has one letter tiles[i] printed on it.
Return the number of possible non-empty sequences of letters you can make using the letters printed on those tiles.
Example 1:
Input: tiles = "AAB"
Output: 8
Explanation: The possible sequences are "A", "B", "AA", "AB", "BA", "AAB", "ABA", "BAA".
Example 2:
Input: tiles = "AAABBC"
Output: 188
Example 3:
Input: tiles = "V"
Output: 1
Constraints:
1 <= tiles.length <= 7
tiles consists of uppercase English letters.
Solution
class Solution:
def numTilePossibilities(self, tiles: str) -> int:
self.ans = 0
mark = [False] * len(tiles)
self.dfs(sorted(tiles), mark)
return self.ans
def dfs(self, tiles, mark):
for i in range(len(tiles)):
if mark[i] or (i > 0 and tiles[i] == tiles[i-1] and not mark[i-1]):
continue
mark[i] = True
self.ans += 1
self.dfs(tiles, mark)
mark[i] = False
516 - Longest Palindromic Subsequence
Problem
Given a string s, find the longest palindromic subsequence's length in s. You may assume that the maximum length of s is 1000.
Example 1:
Input:
"bbbab"
Output:
4
One possible longest palindromic subsequence is "bbbb".
Example 2:
Input:
"cbbd"
Output:
2
One possible longest palindromic subsequence is "bb".
Constraints:
1 <= s.length <= 1000
s consists only of lowercase English letters.
Solution
DP problem.
dp[i][j] represents the max value for substring from j to i.
Transition:
if s[i] == s[j]: dp[i][j] = dp[i-1][j+1] + 2 else: dp[i][j] = max(dp[i-1][j] + dp[i][j+1])
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
dp = [[0] * len(s) for i in range(len(s))]
for i in range(0, len(s)):
dp[i][i] = 1
for j in range(i-1, -1, -1):
if s[i] == s[j]:
dp[i][j] = dp[i-1][j+1] + 2
else:
dp[i][j] = max(dp[i-1][j], dp[i][j+1])
return dp[-1][0] if s else 0
1032 - Stream of Characters
Problem
Implement the StreamChecker class as follows:
StreamChecker(words): Constructor, init the data structure with the given words.
query(letter): returns true if and only if for some k >= 1, the last k characters queried (in order from oldest to newest, including this letter just queried) spell one of the words in the given list.
Example:
StreamChecker streamChecker = new StreamChecker(["cd","f","kl"]); // init the dictionary.
streamChecker.query('a'); // return false
streamChecker.query('b'); // return false
streamChecker.query('c'); // return false
streamChecker.query('d'); // return true, because 'cd' is in the wordlist
streamChecker.query('e'); // return false
streamChecker.query('f'); // return true, because 'f' is in the wordlist
streamChecker.query('g'); // return false
streamChecker.query('h'); // return false
streamChecker.query('i'); // return false
streamChecker.query('j'); // return false
streamChecker.query('k'); // return false
streamChecker.query('l'); // return true, because 'kl' is in the wordlist
Note:
1 <= words.length <= 2000
1 <= words[i].length <= 2000
Words will only consist of lowercase English letters.
Queries will only consist of lowercase English letters.
The number of queries is at most 40000.
Solution
It’s clearly a trie problem. But how to optimize it?
We must keep trace of the stream, and try to search the every pattern in the trace. But that would be LTE.
In order not to loop through every starting point of the trace, we do it reversely, so we only need to start from the beginning once and stop for the existing word.
For example:
[“dlab”, “xlab”]
Create a reversed trie [“bald”, “balx”]
trace = “”, max length of trace = 4, because, all words in the trie are not longer than 4.
“b”: trace = “b”, search for “b” in the trie, False “a”: trace = “ba”, search for “ba” in the trie, False “l”: trace = “bal”, search for “bal” in the trie, False “d”: trace = “bald”, search for “bald” in the trie, True “x”: trace = “aldx”, search for “aldx” in the trie, False
Steps:
- create the trie with reversed words
- keep the track of the stream with a max possible length
- search the track of the stream in the “reversed” trie.
class StreamChecker:
def __init__(self, words: List[str]):
self.root = TrieNode()
for w in words:
x = self.root
for c in reversed(w):
if c in x.children:
x = x.children[c]
else:
x.children[c] = TrieNode()
x = x.children[c]
x.is_word = True
self.s = ""
self.w = max(map(len, words))
def query(self, letter: str) -> bool:
self.s = (letter+self.s)[:self.w]
x = self.root
res = False
for c in self.s:
if c in x.children:
x = x.children[c]
if x.is_word:
return True
else:
return False
return res
class TrieNode:
def __init__(self):
self.children = {}
self.is_word = False
Simplified Version
import collections
from functools import reduce
class StreamChecker:
def __init__(self, words):
T = lambda: collections.defaultdict(T)
self.trie = T()
for w in words: reduce(dict.__getitem__, w[::-1], self.trie)["#"] = True
self.s = ""
self.w = max(map(len, words))
def query(self, letter):
self.s = (letter + self.s)[:self.w]
cur = self.trie
for c in self.s:
if c in cur:
cur = cur[c]
if c["#"] == True:
return True
else:
break
return False
199 - Binary Tree Right Side View
Problem
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
Example:
Input: [1,2,3,null,5,null,4]
Output: [1, 3, 4]
Explanation:
1 <---
/ \
2 3 <---
\ \
5 4 <---
Solution
Comprare the depth. DFS.
Keep track of the current depth. And the length of current answer is the previous depth.
Traverse right to left
If current_depth > previous depth, then it appears in the right view.
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
ans = []
self.print(root, ans, 0)
return ans
def print(self, root, ans, depth):
if not root:
return
if depth + 1 > len(ans): ans.append(root.val)
self.print(root.right, ans, depth+1)
self.print(root.left, ans, depth+1)
1219 - Path with Maximum Gold
Problem
In a gold mine grid of size m * n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty.
Return the maximum amount of gold you can collect under the conditions:
Every time you are located in a cell you will collect all the gold in that cell.
From your position you can walk one step to the left, right, up or down.
You can't visit the same cell more than once.
Never visit a cell with 0 gold.
You can start and stop collecting gold from any position in the grid that has some gold.
Example 1:
Input: grid = [[0,6,0],[5,8,7],[0,9,0]]
Output: 24
Explanation:
[[0,6,0],
[5,8,7],
[0,9,0]]
Path to get the maximum gold, 9 -> 8 -> 7.
Example 2:
Input: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
Output: 28
Explanation:
[[1,0,7],
[2,0,6],
[3,4,5],
[0,3,0],
[9,0,20]]
Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7.
Solution
class Solution:
def getMaximumGold(self, grid: List[List[int]]) -> int:
self.ans = 0
mark = [[False] * len(grid[0]) for _ in grid]
for i in range(len(grid)):
for j in range(len(grid[0])):
self.dfs(grid, i, j, mark, 0)
return self.ans
def dfs(self, grid, i, j, mark, gold):
if i < 0 or i == len(grid) or j < 0 or j == len(grid[0]) or mark[i][j] or not grid[i][j]:
self.ans = max(self.ans, gold)
return
mark[i][j] = True
for di, dj in [[1,0], [0, 1], [-1, 0], [0, -1]]:
self.dfs(grid, i+di, j+dj, mark, gold+grid[i][j])
mark[i][j] = False
404 - Sum of Left Leaves
Problem
Find the sum of all left leaves in a given binary tree.
Example:
3
/ \
9 20
/ \
15 7
There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.
Solution
class Solution:
def sumOfLeftLeaves(self, root: TreeNode) -> int:
return self.dfs(root, 0, False)
def dfs(self, root, presum, isLeft):
if not root:
return 0
if not root.left and not root.right and isLeft:
return presum + root.val
else:
return self.dfs(root.left, presum, True) + self.dfs(root.right, presum, False)
131 - Palindrome Partitioning
Problem
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
Example:
Input: "aab"
Output:
[
["aa","b"],
["a","a","b"]
]
Solution
class Solution:
def partition(self, s: str) -> List[List[str]]:
ans = []
self.dfs(s, 0, ans, [])
return ans
def dfs(self, s, start, ans, path):
if start == len(s):
ans.append(path.copy())
return
for i in range(start, len(s)):
w = s[start:i + 1]
if w == w[::-1]:
path.append(w)
self.dfs(s, i + 1, ans, path)
path.pop()
132 - Palindrome Partitioning II
Problem
Given a string s, partition s such that every substring of the partition is a palindrome
Return the minimum cuts needed for a palindrome partitioning of s.
Example 1:
Input: s = "aab"
Output: 1
Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
Example 2:
Input: s = "a"
Output: 0
Example 3:
Input: s = "ab"
Output: 1
Constraints:
1 <= s.length <= 2000
s consists of lower-case English letters only.
Solution
If s[i:j+1] is a palindrome, and if we know s[0:i] has a minCut of X, then we know minCut to for s[0:j+1] is not greater than X+1.
class Solution:
def minCut(self, s: str) -> int:
cut = [x for x in range(-1, len(s))]
for i in range(len(s)):
for j in range(i, len(s)):
if s[i:j+1] == s[i:j+1][::-1]:
cut[j+1] = min(cut[j+1], cut[i]+1)
return cut[-1]
983 - Minimum Cost For Tickets
Problem
In a country popular for train travel, you have planned some train travelling one year in advance. The days of the year that you will travel is given as an array days. Each day is an integer from 1 to 365.
Train tickets are sold in 3 different ways:
a 1-day pass is sold for costs[0] dollars;
a 7-day pass is sold for costs[1] dollars;
a 30-day pass is sold for costs[2] dollars.
The passes allow that many days of consecutive travel. For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and 8.
Return the minimum number of dollars you need to travel every day in the given list of days.
Example 1:
Input: days = [1,4,6,7,8,20], costs = [2,7,15]
Output: 11
Explanation:
For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1.
On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9.
On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20.
In total you spent $11 and covered all the days of your travel.
Example 2:
Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15]
Output: 17
Explanation:
For example, here is one way to buy passes that lets you travel your travel plan:
On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30.
On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31.
In total you spent $17 and covered all the days of your travel.
Solution
DP problem. dp[i] represents the min cost for ith day.
We have four options for one day.
-
We don’t travel: dp[i] = dp[i-1]
-
We have to travel:
a. buy 1-day ticket: dp[i] = dp[i-1] + costs[0] b. buy 7-days ticket which can cover ith day: dp[i] = min(dp[i-1], dp[i-2], dp[i-3], … dp[i-7]) + costs[1] c. buy 30-days ticket which can cover ith day: dp[i] = min(dp[i-1], dp[i-2], … dp[i-30]) + costs[2]
Important is to know, the min cost is always increasing.
So min(dp[i-1], dp[i-2]… dp[i-7]) = dp[i-7]
dp[i] = min(dp[i-1] + costs[0], dp[i-7] + costs[0], dp[i-30] + costs[0])
Note that, i-7 and i-30 might smaller than 0 in the loop. Use max(0, i-7) to avoid that.
class Solution:
def mincostTickets(self, days: List[int], costs: List[int]) -> int:
dp = [0] * (max(days)+1)
for i in range(1, len(dp)):
if i not in days:
dp[i] = dp[i-1]
else:
dp[i] = min(dp[i-1]+costs[0], dp[max(0, i-7)] + costs[1], dp[max(0, i-30)] + costs[2])
return dp[-1]
412 - Fizz Buzz
Problem
Write a program that outputs the string representation of numbers from 1 to n.
But for multiples of three it should output “Fizz” instead of the number and for the multiples of five output “Buzz”. For numbers which are multiples of both three and five output “FizzBuzz”.
Example:
n = 15,
Return:
[
"1",
"2",
"Fizz",
"4",
"Buzz",
"Fizz",
"7",
"8",
"Fizz",
"Buzz",
"11",
"Fizz",
"13",
"14",
"FizzBuzz"
]
Solution
class Solution:
def fizzBuzz(self, n: int) -> List[str]:
ans = []
for i in range(1, n+1):
if not i % 3 and not i % 5:
ans.append("FizzBuzz")
elif not i % 5:
ans.append("Buzz")
elif not i % 3:
ans.append("Fizz")
else:
ans.append(str(i))
return ans
1286 - Iterator for Combination
Problem
Design an Iterator class, which has:
A constructor that takes a string characters of sorted distinct lowercase English letters and a number combinationLength as arguments.
A function next() that returns the next combination of length combinationLength in lexicographical order.
A function hasNext() that returns True if and only if there exists a next combination.
Example:
CombinationIterator iterator = new CombinationIterator("abc", 2); // creates the iterator.
iterator.next(); // returns "ab"
iterator.hasNext(); // returns true
iterator.next(); // returns "ac"
iterator.hasNext(); // returns true
iterator.next(); // returns "bc"
iterator.hasNext(); // returns false
Constraints:
1 <= combinationLength <= characters.length <= 15
There will be at most 10^4 function calls per test.
It's guaranteed that all calls of the function next are valid.
Solution
Solution 1: DFS to generate the bit mask
class CombinationIterator:
def __init__(self, characters: str, combinationLength: int):
self.bitmasks = []
self.characters = characters
self.combinationLength = combinationLength
self.dfs(0, 0, [])
print(self.bitmasks)
def next(self) -> str:
ans = ""
mask = self.bitmasks.pop(0)
for i in range(len(mask)):
if mask[i] == "1":
ans += self.characters[i]
return ans
def hasNext(self) -> bool:
return self.bitmasks != []
def dfs(self, count, index, path):
if index == len(self.characters):
if count == self.combinationLength:
self.bitmasks.append(list(path))
return
for i in ["1", "0"]:
if count == self.combinationLength and i == "1":
continue
path.append(i)
if i == "1":
self.dfs(count+1, index+1, path)
else:
self.dfs(count, index+1, path)
path.pop()
Solution 2: Generate the bismask iteratively
class CombinationIterator:
def __init__(self, characters: str, combinationLength: int):
self.characters = characters
self.combinationLength = combinationLength
self.bitmasks = []
for mask in range(1 << len(characters)):
if bin(mask).count('1') == self.combinationLength:
res = ""
for i in range(len(characters)):
res = str(mask%2) + res
mask >>= 1
self.bitmasks.append(res)
self.bitmasks = self.bitmasks[::-1]
def next(self) -> str:
ans = ""
mask = self.bitmasks.pop(0)
for i in range(len(mask)):
if mask[i] == "1":
ans += self.characters[i]
return ans
def hasNext(self) -> bool:
return self.bitmasks != []
93 - Restore IP Addresses
Problem
Given a string s containing only digits. Return all possible valid IP addresses that can be obtained from s. You can return them in any order.
A valid IP address consists of exactly four integers, each integer is between 0 and 255, separated by single points and cannot have leading zeros. For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses and "0.011.255.245", "192.168.1.312" and "192.168@1.1" are invalid IP addresses.
Example 1:
Input: s = "25525511135"
Output: ["255.255.11.135","255.255.111.35"]
Example 2:
Input: s = "0000"
Output: ["0.0.0.0"]
Example 3:
Input: s = "1111"
Output: ["1.1.1.1"]
Example 4:
Input: s = "010010"
Output: ["0.10.0.10","0.100.1.0"]
Example 5:
Input: s = "101023"
Output: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
Constraints:
0 <= s.length <= 3000
s consists of digits only.
Solution
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
if len(s) > 12:
return []
ans = []
self.dfs(s, 0, ans, [])
return ans
def dfs(self, s, start, ans, path):
if start >= len(s):
if len(path) == 4:
ans.append(".".join(path))
return
for i in range(start, min(len(s), start+3)):
number = s[start:i+1]
if len(number) > 1 and number[0] == "0":
continue
if int(number) <= 255:
path.append(number)
self.dfs(s, i+1, ans, path)
path.pop()
436 - Find Right Interval
Problem
Given a set of intervals, for each of the interval i, check if there exists an interval j whose start point is bigger than or equal to the end point of the interval i, which can be called that j is on the "right" of i.
For any interval i, you need to store the minimum interval j's index, which means that the interval j has the minimum start point to build the "right" relationship for interval i. If the interval j doesn't exist, store -1 for the interval i. Finally, you need output the stored value of each interval as an array.
Note:
You may assume the interval's end point is always bigger than its start point.
You may assume none of these intervals have the same start point.
Example 1:
Input: [ [1,2] ]
Output: [-1]
Explanation: There is only one interval in the collection, so it outputs -1.
Example 2:
Input: [ [3,4], [2,3], [1,2] ]
Output: [-1, 0, 1]
Explanation: There is no satisfied "right" interval for [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point;
For [1,2], the interval [2,3] has minimum-"right" start point.
Example 3:
Input: [ [1,4], [2,3], [3,4] ]
Output: [-1, 2, -1]
Explanation: There is no satisfied "right" interval for [1,4] and [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point.
Solution
class Solution:
def findRightInterval(self, intervals: List[List[int]]) -> List[int]:
starts = sorted([[a[0], i] for i, a in enumerate(intervals)]) + [[float('inf'), -1]]
return [starts[bisect.bisect(starts, [x[1]])][1] for x in intervals]
980 - Unique Paths III
Problem
On a 2-dimensional grid, there are 4 types of squares:
1 represents the starting square. There is exactly one starting square.
2 represents the ending square. There is exactly one ending square.
0 represents empty squares we can walk over.
-1 represents obstacles that we cannot walk over.
Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.
Example 1:
Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
Output: 2
Explanation: We have the following two paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
Example 2:
Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
Output: 4
Explanation: We have the following four paths:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
Example 3:
Input: [[0,1],[2,0]]
Output: 0
Explanation:
There is no path that walks over every empty square exactly once.
Note that the starting and ending square can be anywhere in the grid.
Note:
1 <= grid.length * grid[0].length <= 20
Solution
class Solution:
def uniquePathsIII(self, grid: List[List[int]]) -> int:
count = len([x for r in grid for x in r if x == 0]) + 2
start = [[i,j] for i in range(len(grid)) for j in range(len(grid[0])) if grid[i][j] == 1][0]
self.ans = 0
self.dfs(grid, start[0], start[1], 0, count)
return self.ans
def dfs(self, grid, i, j, curCount, count):
if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] in [-1, 100]:
return
if curCount == count-1 and grid[i][j] == 2:
self.ans += 1
return
elif grid[i][j] == 2:
return
x = grid[i][j]
grid[i][j] = 100
for di, dj in [[0, 1], [0, -1], [1, 0], [-1, 0]]:
self.dfs(grid, i+di, j+dj, curCount+1, count)
grid[i][j] = x
450 - Delete Node in a BST
Problem
Design an Iterator class, which has:
A constructor that takes a string characters of sorted distinct lowercase English letters and a number combinationLength as arguments.
A function next() that returns the next combination of length combinationLength in lexicographical order.
A function hasNext() that returns True if and only if there exists a next combination.
Example:
CombinationIterator iterator = new CombinationIterator("abc", 2); // creates the iterator.
iterator.next(); // returns "ab"
iterator.hasNext(); // returns true
iterator.next(); // returns "ac"
iterator.hasNext(); // returns true
iterator.next(); // returns "bc"
iterator.hasNext(); // returns false
Constraints:
1 <= combinationLength <= characters.length <= 15
There will be at most 10^4 function calls per test.
It's guaranteed that all calls of the function next are valid.
Solution
class Solution:
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if not root:
return None
if root.val > key:
root.left = self.deleteNode(root.left, key)
elif root.val < key:
root.right = self.deleteNode(root.right, key)
else:
if not root.left:
root = root.right
elif not root.right:
root = root.left
else:
node = self.getMin(root.right)
node.left = root.left
root = root.right
return root
def getMin(self, root):
if not root:
return None
return self.getMin(root.left) or root
526 - Beautiful Arrangement
Problem
Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is constructed by these N numbers successfully if one of the following is true for the ith position (1 <= i <= N) in this array:
The number at the ith position is divisible by i.
i is divisible by the number at the ith position.
Now given N, how many beautiful arrangements can you construct?
Example 1:
Input: 2
Output: 2
Explanation:
The first beautiful arrangement is [1, 2]:
Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1).
Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2).
The second beautiful arrangement is [2, 1]:
Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1).
Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1.
Note:
N is a positive integer and will not exceed 15.
Solution
class Solution:
def countArrangement(self, N: int) -> int:
self.ans = 0
self.mark = [False for i in range(N+1)]
self.dfs(N, self.mark, [])
return self.ans
def dfs(self, N, mark, path):
if len(path) == N:
self.ans += 1
for n in range(1, N+1):
index = len(path) + 1
if mark[n] or (n % index != 0 and index % n != 0):
continue
mark[n] = True
self.dfs(N, self.mark, path + [n])
mark[n] = False
354 - Russian Doll Envelopes
Problem
You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envelope can fit into another if and only if both the width and height of one envelope is greater than the width and height of the other envelope.
What is the maximum number of envelopes can you Russian doll? (put one inside other)
Note:
Rotation is not allowed.
Example:
Input: [[5,4],[6,4],[6,7],[2,3]]
Output: 3
Explanation: The maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).
Solution
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
if not envelopes:
return 0
heights = [env[1] for env in sorted(envelopes, key = lambda x: [x[0], -x[1]])]
print(heights)
dp = [heights[0]]
for i in range(1, len(heights)):
if heights[i] > dp[-1]:
dp.append(heights[i])
else:
l = self.bs(dp, heights[i])
dp[l] = heights[i]
return len(dp)
def bs(self, dp, target):
r = len(dp) - 1
l = 0
while l <= r:
mid = (l+r) // 2
if dp[mid] > target:
r = mid - 1
elif dp[mid] < target:
l = mid + 1
else:
return mid
return l
403 - Frog Jump
Problem
A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.
Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.
If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction.
Note:
The number of stones is ≥ 2 and is < 1,100.
Each stone's position will be a non-negative integer < 231.
The first stone's position is always 0.
Example 1:
[0,1,3,5,6,8,12,17]
There are a total of 8 stones.
The first stone at the 0th unit, second stone at the 1st unit,
third stone at the 3rd unit, and so on...
The last stone at the 17th unit.
Return true. The frog can jump to the last stone by jumping
1 unit to the 2nd stone, then 2 units to the 3rd stone, then
2 units to the 4th stone, then 3 units to the 6th stone,
4 units to the 7th stone, and 5 units to the 8th stone.
Example 2:
[0,1,2,3,4,8,9,11]
Return false. There is no way to jump to the last stone as
the gap between the 5th and 6th stone is too large.
Solution
DP problem.
states:
dp[i][0]: k-1 steps
dp[i][1]: k steps
dp[i][2]: k+1 steps
Transitions:
for k in [-1, 0, 1]:
dp[i+step][k+1] = max(dp[i+step][k+1], dp[i][j]+k)
Note: It has to use max function in the transition, because the later stones may update the jumps with lower distance.
class Solution:
def canCross(self, stones: List[int]) -> bool:
if len(stones) < 2:
return True
dp = defaultdict(lambda: [0, 0, 0])
dp[1][1] = 1
for i in stones[1:]:
for j in range(3):
if dp[i][j] == 0:
continue
for k in [-1, 0, 1]:
step = dp[i][j] + k
if i+step == stones[-1]:
return True
elif step > 0 and i + step < stones[-1]:
dp[i+step][k+1] = max(dp[i+step][k+1], dp[i][j]+k)
return False
376 - Wiggle Subsequence
Problem
A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.
For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.
Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.
Example 1:
Input: [1,7,4,9,2,5]
Output: 6
Explanation: The entire sequence is a wiggle sequence.
Example 2:
Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
Explanation: There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Example 3:
Input: [1,2,3,4,5,6,7,8,9]
Output: 2
Follow up:
Can you do it in O(n) time?
Solution
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
if len(nums) < 2:
return len(nums)
first_diff = 1
dp = [1] * len(nums)
ret = 1
for first_diff in [1, -1]:
dp = [1] * len(nums)
for i in range(1, len(nums)):
if first_diff * (nums[i] - nums[i-1]) > 0:
dp[i] = dp[i-1] + 1
first_diff *= -1
else:
dp[i] = dp[i-1]
ret = max(ret, max(dp))
return ret
86 - Partition List
Problem
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
Example:
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
Solution
Solution 1: Two Dummy Pointers
Simple version: just create two list, and append them in the end
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
p1 = l1 = ListNode(0)
p2 = l2 = ListNode(0)
while head:
if head.val < x:
p1.next, p1 = head, head
else:
p2.next, p2 = head, head
head.next, head = None, head.next
p1.next = l2.next
return l1.next
Solution 2: In place
Inplace swap with Two pointers:
p1: mark the end of smaller part
p2: go forward to find the smaller one and swap with p1.next
Addtional prev pointer for the swap.
- Use a dummy node to make it possible to swap the smaller one to the head
- Note the case when p1 == prev, no need to swap, just move p1 forward
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
head = ListNode(0, head)
p1, prev, p2 = head, head, head.next
while p2:
if p2.val < x:
if p1 == prev:
prev, p2 = p2, p2.next
else:
p1.next, p2.next, p2, prev.next = p2, p1.next, p2.next, p2.next
p1 = p1.next
else:
prev, p2 = p2, p2.next
return head.next
378 - Kth Smallest Element in a Sorted Matrix
Problem
Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.
Note that it is the kth smallest element in the sorted order, not the kth distinct element.
Example:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
return 13.
Solution
Solution 1: Heap with marker
Maintain a heap, pop the first one, and push the right one and lower one into the heap.
But we need a mark matrix to tell if the one to be pushed was not visited before, since it may have been in the heap when we push the lower one, and now we come to this one from left to right.
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
if not matrix or not matrix[0]:
return None
n = len(matrix)
heap = []
heapq.heapify(heap)
heapq.heappush(heap, (matrix[0][0], [0, 0]))
mark = [[False] * n for _ in range(n)]
while k > 0:
ret = heapq.heappop(heap)
i, j = ret[1]
if i+1 < n and not mark[i+1][j]:
heapq.heappush(heap, (matrix[i+1][j], [i+1, j]))
mark[i+1][j] = True
if j+1 < n and not mark[i][j+1]:
heapq.heappush(heap, (matrix[i][j+1], [i, j+1]))
mark[i][j+1] = True
k -= 1
return ret[0]
Solution 2: Heap without marker
We can eliminate the marker by push the first column into the heap at once. Then we only need to traverse from left to right.
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
n = len(matrix)
heap = [(row[0], index, 0) for index, row in enumerate(matrix)]
heapify(heap)
while k > 0:
ret, i, j = heapq.heappop(heap)
if j+1 < n:
heappush(heap, (matrix[i][j+1], i, j+1))
k -= 1
return ret
Solution 3: Binary Search
class Solution:
def kthSmallest(self, matrix):
lo, hi = len(matrix), len(matrix[0])
while lo < hi:
mid = (lo+hi)//2
if sum(bisect.bisect_right(matrix[row], mid) for row in matrix) < k:
lo = mid+1
else:
hi = mid
115 - Distinct Subsequences
Problem
Given two strings s and t, return the number of distinct subsequences of s which equals t.
A string's subsequence is a new string formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (i.e., "ACE" is a subsequence of "ABCDE" while "AEC" is not).
It's guaranteed the answer fits on a 32-bit signed integer.
Example 1:
Input: s = "rabbbit", t = "rabbit"
Output: 3
Explanation:
As shown below, there are 3 ways you can generate "rabbit" from S.
rabbbit
rabbbit
rabbbit
Example 2:
Input: s = "babgbag", t = "bag"
Output: 5
Explanation:
As shown below, there are 5 ways you can generate "bag" from S.
babgbag
babgbag
babgbag
babgbag
babgbag
Solution
class Solution:
def numDistinct(self, s: str, t: str) -> int:
m, n = len(s), len(t)
dp = [[0] * (m+1) for _ in range(n+1)]
for i in range(m+1):
dp[0][i] = 1
for i in range(1, n+1):
for j in range(1, m+1):
if s[j-1] == t[i-1]:
dp[i][j] = dp[i][j-1] + dp[i-1][j-1]
else:
dp[i][j] = dp[i][j-1]
return dp[n][m]
1392 - Longest Happy Prefix
Problem
A string is called a happy prefix if is a non-empty prefix which is also a suffix (excluding itself).
Given a string s. Return the longest happy prefix of s .
Return an empty string if no such prefix exists.
Example 1:
Input: s = "level"
Output: "l"
Explanation: s contains 4 prefix excluding itself ("l", "le", "lev", "leve"), and suffix ("l", "el", "vel", "evel"). The largest prefix which is also suffix is given by "l".
Example 2:
Input: s = "ababab"
Output: "abab"
Explanation: "abab" is the largest prefix which is also suffix. They can overlap in the original string.
Example 3:
Input: s = "leetcodeleet"
Output: "leet"
Example 4:
Input: s = "a"
Output: ""
Constraints:
1 <= s.length <= 10^5
s contains only lowercase English letters.
Solution
class Solution:
def longestPrefix(self, s: str) -> str:
dp = [0] * len(s)
j = 0
i = 1
while i < len(s):
if s[i] == s[j]:
j += 1
dp[i] = j
elif j > 0:
j = dp[j-1]
i -= 1
i += 1
return s[:dp[-1]]
395 - Longest Substring with At Least K Repeating Characters
Problem
Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.
Example 1:
Input:
s = "aaabb", k = 3
Output:
3
The longest substring is "aaa", as 'a' is repeated 3 times.
Example 2:
Input:
s = "ababbc", k = 2
Output:
5
The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times
Solution
class Solution:
def longestSubstring(self, s: str, k: int) -> int:
c = Counter(s)
for i, v in c.items():
if v < k:
return max(self.longestSubstring(x, k) for x in s.split(i))
return len(s)
127 - Word Ladder
Problem
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
Only one letter can be changed at a time.
Each transformed word must exist in the word list.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
Solution
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
if endWord not in wordList:
return 0
h = Counter(set(wordList))
queue = [beginWord]
ret = 1
while queue:
tmp = []
while queue:
w = queue.pop()
if w == endWord:
return ret
for i in range(len(w)):
for j in range(26):
to = w[:i] + chr(ord('a') + j) + w[i+1:]
if to != w and h[to]:
tmp.append(to)
h[to] = 0
ret += 1
queue = tmp
return 0
126 - Word Ladder II
Problem
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
Only one letter can be changed at a time
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
Return an empty list if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output:
[
["hit","hot","dot","dog","cog"],
["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
Solution
class Solution:
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]):
if endWord not in wordList:
return []
h = Counter(set(wordList))
paths = defaultdict(set)
queue = [beginWord]
h[beginWord] = 0
levels = []
seen = False
while queue and not seen:
tmp = []
while queue:
w = queue.pop()
if w == endWord:
seen = True
for i in range(len(w)):
for j in range(26):
to = w[:i] + chr(ord('a') + j) + w[i+1:]
if to != w and h[to]:
tmp.append(to)
paths[w].add(to)
for t in tmp:
h[t] = 0
levels.append(list(tmp))
queue = tmp
ret = []
self.dfs(beginWord, endWord, paths, [], ret)
return ret
def dfs(self, beginWord, endWord, paths, path, ret):
if beginWord == endWord:
ret.append(path+[beginWord])
return
for x in paths[beginWord]:
self.dfs(x, endWord, paths, path+[beginWord], ret)
257 - Binary Tree Paths
Problem
iven a binary tree, return all root-to-leaf paths.
Note: A leaf is a node with no children.
Example:
Input:
1
/ \
2 3
\
5
Output: ["1->2->5", "1->3"]
Explanation: All root-to-leaf paths are: 1->2->5, 1->3
Solution
class Solution:
def binaryTreePaths(self, root: TreeNode) -> List[str]:
if not root:
return []
if not root.right and not root.left:
return [str(root.val)]
paths = [str(root.val) + "->" + s for s in self.binaryTreePaths(root.right) + self.binaryTreePaths(root.left)]
return paths
691 - Stickers to Spell Word
Problem
We are given N different types of stickers. Each sticker has a lowercase English word on it.
You would like to spell out the given target string by cutting individual letters from your collection of stickers and rearranging them.
You can use each sticker more than once if you want, and you have infinite quantities of each sticker.
What is the minimum number of stickers that you need to spell out the target? If the task is impossible, return -1.
Example 1:
Input:
["with", "example", "science"], "thehat"
Output:
3
Explanation:
We can use 2 "with" stickers, and 1 "example" sticker.
After cutting and rearrange the letters of those stickers, we can form the target "thehat".
Also, this is the minimum number of stickers necessary to form the target string.
Example 2:
Input:
["notice", "possible"], "basicbasic"
Output:
-1
Explanation:
We can't form the target "basicbasic" from cutting letters from the given stickers.
Note:
stickers has length in the range [1, 50].
stickers consists of lowercase English words (without apostrophes).
target has length in the range [1, 15], and consists of lowercase English letters.
In all test cases, all words were chosen randomly from the 1000 most common US English words, and the target was chosen as a concatenation of two random words.
The time limit may be more challenging than usual. It is expected that a 50 sticker test case can be solved within 35ms on average.
Solution
class Solution:
def minStickers(self, stickers: List[str], target: str) -> int:
self.ret = float("inf")
self.stickers = stickers
self.dfs([], target, 0, 0)
return self.ret if self.ret < float("inf") else -1
def dfs(self, pool, target, index, num_s):
if num_s >= self.ret:
return
if index >= len(target):
self.ret = num_s
return
if target[index] in pool:
pool.remove(target[index])
self.dfs(pool, target, index+1, num_s)
pool.append(target[index])
else:
for w in self.stickers:
if target[index] in w:
new_chars = list(w)
new_chars.remove(target[index])
self.dfs(pool + new_chars, target, index+1, num_s+1)
797 - All Paths From Source to Target
Problem
Given a directed acyclic graph (DAG) of n nodes labeled from 0 to n - 1, find all possible paths from node 0 to node n - 1, and return them in any order.
The graph is given as follows: graph[i] is a list of all nodes you can visit from node i (i.e., there is a directed edge from node i to node graph[i][j]).
Solution
class Solution:
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
ret = []
self.dfs(graph, 0, [0], ret)
return ret
def dfs(self, graph, source, path, ret):
if source == len(graph) - 1:
ret.append(list(path))
for w in graph[source]:
self.dfs(graph, w, path+[w], ret)
464 - Can I Win
Problem
In the "100 game" two players take turns adding, to a running total, any integer from 1 to 10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers from 1 to 15 without replacement until they reach a total >= 100.
Given two integers maxChoosableInteger and desiredTotal, return true if the first player to move can force a win, otherwise return false. Assume both players play optimally.
Example 1:
Input: maxChoosableInteger = 10, desiredTotal = 11
Output: false
Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.
Example 2:
Input: maxChoosableInteger = 10, desiredTotal = 0
Output: true
Example 3:
Input: maxChoosableInteger = 10, desiredTotal = 1
Output: true
Constraints:
1 <= maxChoosableInteger <= 20
0 <= desiredTotal <= 300
Solution
DFS
Maintain a hashmap, use unchosen numbers as key,
class Solution:
def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:
h = {}
def canIWinRec(numbers, desiredTotal):
if numbers[-1] >= desiredTotal:
return True
k = tuple(numbers)
if k in h:
return h[k]
for i in range(len(numbers)):
if not canIWinRec(numbers[:i] + numbers[i+1:], desiredTotal - numbers[i]):
h[k] = True
return True
h[k] = False
return False
summed = (maxChoosableInteger + 1) * maxChoosableInteger / 2
if summed < desiredTotal:
return False
if summed == desiredTotal:
return maxChoosableInteger % 2
numbers = list(range(1, maxChoosableInteger+1))
return canIWinRec(numbers, desiredTotal)
116 - Populating Next Right Pointers in Each Node
Problem
You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.
Initially, all next pointers are set to NULL.
Follow up:
You may only use constant extra space.
Recursive approach is fine, you may assume implicit stack space does not count as extra space for this problem.
Solution
Constant Space
class Solution:
def connect(self, root: 'Node') -> 'Node':
node = root
while node:
next_level = node.left
while node and node.left:
node.left.next = node.right
node.right.next = node.next and node.next.left
node = node.next
node = next_level
return root
295 - Find Median from Data Stream
Problem
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
For example,
[2,3,4], the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
void addNum(int num) - Add a integer number from the data stream to the data structure.
double findMedian() - Return the median of all elements so far.
Example:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
Follow up:
If all integer numbers from the stream are between 0 and 100, how would you optimize it?
If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?
Solution
Two heaps, one for smaller half, one for larger half.
Follow ups:
- Keep a array for counting the numbers. Go through the array to find the median. It’s O(1).
- If 99% of all integer is between 0 and 100, keep the array. We need only count the ones over 100 and less than 0. Use the counts and the array to find the median
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.small = []
self.large = []
def addNum(self, num: int) -> None:
heappush(self.small, -heappushpop(self.large, num))
if len(self.large) < len(self.small):
heappush(self.large, -heappop(self.small))
def findMedian(self) -> float:
if not self.large:
return 0
if len(self.large) > len(self.small):
return self.large[0]
return (self.large[0] - self.small[0]) / 2
996 - Number of Squareful Arrays
Problem
Given an array A of non-negative integers, the array is squareful if for every pair of adjacent elements, their sum is a perfect square.
Return the number of permutations of A that are squareful. Two permutations A1 and A2 differ if and only if there is some index i such that A1[i] != A2[i].
Example 1:
Input: [1,17,8]
Output: 2
Explanation:
[1,8,17] and [17,8,1] are the valid permutations.
Example 2:
Input: [2,2,2]
Output: 1
Note:
1 <= A.length <= 12
0 <= A[i] <= 1e9
Solution
Simple DFS
class Solution:
def numSquarefulPerms(self, A: List[int]) -> int:
self.h = defaultdict(bool)
mark = [False] * len(A)
self.ret = 0
self.dfs(sorted(A), [], mark)
return self.ret
def dfs(self, A, path, mark):
if len(path) == len(A):
self.ret += 1
for i, v in enumerate(A):
if (i > 0 and A[i] == A[i-1] and not mark[i-1]) or mark[i]:
continue
if not path or self.isSquare(path[-1]+v):
mark[i] = True
self.dfs(A, path+[v], mark)
mark[i] = False
def isSquare(self, num):
return True if int((num**0.5))**2 == num else False
842 - Split Array into Fibonacci Sequence
Problem
Given a string S of digits, such as S = "123456579", we can split it into a Fibonacci-like sequence [123, 456, 579].
Formally, a Fibonacci-like sequence is a list F of non-negative integers such that:
0 <= F[i] <= 2^31 - 1, (that is, each integer fits a 32-bit signed integer type);
F.length >= 3;
and F[i] + F[i+1] = F[i+2] for all 0 <= i < F.length - 2.
Also, note that when splitting the string into pieces, each piece must not have extra leading zeroes, except if the piece is the number 0 itself.
Return any Fibonacci-like sequence split from S, or return [] if it cannot be done.
Example 1:
Input: "123456579"
Output: [123,456,579]
Example 2:
Input: "11235813"
Output: [1,1,2,3,5,8,13]
Example 3:
Input: "112358130"
Output: []
Explanation: The task is impossible.
Example 4:
Input: "0123"
Output: []
Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid.
Example 5:
Input: "1101111"
Output: [110, 1, 111]
Explanation: The output [11, 0, 11, 11] would also be accepted.
Note:
1 <= S.length <= 200
S contains only digits.
Solution
class Solution:
def splitIntoFibonacci(self, S: str) -> List[int]:
self.upper_bound = 2**31-1
self.ret = []
self.dfs(S, 0, [], [])
return self.ret
def dfs(self, S, index, ret, path):
if self.ret:
return
if index == len(S) and "".join(map(str, ret)) == S and len(ret) >= 3:
self.ret = list(ret)
return
elif index == len(S):
return
path.append(S[index])
n = int("".join(path))
if self.isFibonacci(ret, n):
self.dfs(S, index+1, ret+[n], [])
self.dfs(S, index+1, ret, path)
path.pop()
def isFibonacci(self, sequence, number):
if number < 0 or number > self.upper_bound:
return False
if len(sequence) < 2:
return True
return True if number == sequence[-1] + sequence[-2] else False
486 - Predict the Winner
Problem
Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on.
Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.
Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.
Example 1:
Input: [1, 5, 2]
Output: False
Explanation: Initially, player 1 can choose between 1 and 2.
If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2).
So, final score of player 1 is 1 + 2 = 3, and player 2 is 5.
Hence, player 1 will never be the winner and you need to return False.
Example 2:
Input: [1, 5, 233, 7]
Output: True
Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233.
Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.
Constraints:
1 <= length of the array <= 20.
Any scores in the given array are non-negative integers and will not exceed 10,000,000.
If the scores of both players are equal, then player 1 is still the winner.
Solution
class Solution:
def PredictTheWinner(self, nums: List[int]) -> bool:
dp = {}
def getMaxDiff(left, right):
if (left, right) not in dp:
if left == right:
return nums[left]
dp[left, right] = max(nums[left] - getMaxDiff(left+1, right), nums[right] - getMaxDiff(left, right-1))
return dp[left, right]
return getMaxDiff(0, len(nums)-1) >= 0
556 - Next Greater Element III
Problem
Given a positive 32-bit integer n, you need to find the smallest 32-bit integer which has exactly the same digits existing in the integer n and is greater in value than n. If no such positive 32-bit integer exists, you need to return -1.
Example 1:
Input: 12
Output: 21
Example 2:
Input: 21
Output: -1
Solution
class Solution:
def nextGreaterElement(self, n: int) -> int:
s = list(str(n))
first = len(s) - 1
for i in range(len(s)-2, -1, -1):
if s[i] < s[i+1]:
first = i
break
if first == len(s) - 1:
return -1
second = first+1
minimum = s[second]
for i in range(first+1, len(s)):
if s[i] < minimum and s[i] > s[first]:
minumum = s[i]
second = i
s[first], s[second] = s[second], s[first]
ret = int("".join(s[:first+1] + sorted(s[first+1:])))
return ret if ret <= (1<<31-1) else -1
381 - Insert Delete GetRandom O(1) - Duplicates allowed
Problem
Design a data structure that supports all following operations in average O(1) time.
Note: Duplicate elements are allowed.
insert(val): Inserts an item val to the collection.
remove(val): Removes an item val from the collection if present.
getRandom: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.
Example:
// Init an empty collection.
RandomizedCollection collection = new RandomizedCollection();
// Inserts 1 to the collection. Returns true as the collection did not contain 1.
collection.insert(1);
// Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1].
collection.insert(1);
// Inserts 2 to the collection, returns true. Collection now contains [1,1,2].
collection.insert(2);
// getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3.
collection.getRandom();
// Removes 1 from the collection, returns true. Collection now contains [1,2].
collection.remove(1);
// getRandom should return 1 and 2 both equally likely.
collection.getRandom();
Solution
Notes:
Remove is tricky.
- Find the index of the element to remove in Hash.
- Replace the element with the last element in array, and pop the last one.
- Don’t forget to update the index of copied element in Hash.
- You have to firstly add the new index and then delete. There is a corner case when there is only one element. If you delete it first and then add, the same index is added back to hash.
class RandomizedCollection:
def __init__(self):
"""
Initialize your data structure here.
"""
self.list = []
self.dict = defaultdict(set)
def insert(self, val: int) -> bool:
"""
Inserts a value to the collection. Returns true if the collection did not already contain the specified element.
"""
self.list.append(val)
if val not in self.dict:
ret = True
else:
ret = False
self.dict[val].add(len(self.list) - 1)
return ret
def remove(self, val: int) -> bool:
"""
Removes a value from the collection. Returns true if the collection contained the specified element.
"""
if self.dict[val]:
rm_index = self.dict[val].pop()
last = self.list[-1]
self.list[rm_index] = last
self.dict[last].add(rm_index)
self.dict[last].discard(len(self.list) - 1)
self.list.pop()
return True
else:
return False
def getRandom(self) -> int:
"""
Get a random element from the collection.
"""
return random.choice(self.list)
# Your RandomizedCollection object will be instantiated and called as such:
# obj = RandomizedCollection()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()
134 - Gas Station
Problem
There are N gas stations along a circular route, where the amount of gas at station i is gas[i].
You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.
Return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1.
Note:
If there exists a solution, it is guaranteed to be unique.
Both input arrays are non-empty and have the same length.
Each element in the input arrays is a non-negative integer.
Example 1:
Input:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
Output: 3
Explanation:
Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 4. Your tank = 4 - 1 + 5 = 8
Travel to station 0. Your tank = 8 - 2 + 1 = 7
Travel to station 1. Your tank = 7 - 3 + 2 = 6
Travel to station 2. Your tank = 6 - 4 + 3 = 5
Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3.
Therefore, return 3 as the starting index.
Example 2:
Input:
gas = [2,3,4]
cost = [3,4,3]
Output: -1
Explanation:
You can't start at station 0 or 1, as there is not enough gas to travel to the next station.
Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4
Travel to station 0. Your tank = 4 - 3 + 2 = 3
Travel to station 1. Your tank = 3 - 3 + 3 = 3
You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3.
Therefore, you can't travel around the circuit once no matter where you start.
Solution
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
start = gap = left = 0
for i in range(len(gas)):
left += gas[i] - cost[i]
if left < 0:
start = i+1
gap += left
left = 0
return start if left+gap >= 0 else -1
135 - Candy
Problem
There are N children standing in a line. Each child is assigned a rating value.
You are giving candies to these children subjected to the following requirements:
Each child must have at least one candy.
Children with a higher rating get more candies than their neighbors.
What is the minimum candies you must give?
Example 1:
Input: [1,0,2]
Output: 5
Explanation: You can allocate to the first, second and third child with 2, 1, 2 candies respectively.
Example 2:
Input: [1,2,2]
Output: 4
Explanation: You can allocate to the first, second and third child with 1, 2, 1 candies respectively.
The third child gets 1 candy because it satisfies the above two conditions.
Solution
class Solution:
def candy(self, ratings: List[int]) -> int:
candy = [1] * len(ratings)
for i in range(len(candy)-1):
if ratings[i+1] > ratings[i]:
candy[i+1] = candy[i] + 1
for i in range(len(candy)-1, 0, -1):
if ratings[i-1] > ratings[i]:
candy[i-1] = max(candy[i]+1, candy[i-1])
return sum(candy)
140 - Word Break II
Problem
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:
Input:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
"cats and dog",
"cat sand dog"
]
Example 2:
Input:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
"pine apple pen apple",
"pineapple pen apple",
"pine applepen apple"
]
Explanation: Note that you are allowed to reuse a dictionary word.
Example 3:
Input:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]
Solution
class Solution(object):
def wordBreak(self, s, wordDict):
return self.dfs(s, set(wordDict), 0, {})
def dfs(self, s, wordDict, start, memo):
if start >= len(s): return [""]
if start in memo: return memo[start]
ans = []
for i in range(start+1, len(s)+1):
w = s[start:i]
if w in wordDict:
ans += [" ".join([w, x]) if x else w for x in self.dfs(s, wordDict, i, memo)]
memo[start] = ans
return ans
139 - Word Break
Problem
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.
Example 1:
Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
Example 2:
Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.
Example 3:
Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false
Solution
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
memo = {}
def dfs(index):
if index in memo: return memo[index]
if index >= len(s): return True
ret = False
for i in range(index+1, len(s)+1):
if s[index:i] in wordDict:
if dfs(i):
ret = True
break
memo[index] = ret
return ret
return dfs(0)
443 - String Compression
Problem
Given an array of characters chars, compress it using the following algorithm:
Begin with an empty string s. For each group of consecutive repeating characters in chars:
If the group's length is 1, append the character to s.
Otherwise, append the character followed by the group's length.
The compressed string s should not be returned separately, but instead be stored in the input character array chars. Note that group lengths that are 10 or longer will be split into multiple characters in chars.
After you are done modifying the input array, return the new length of the array.
Follow up:
Could you solve it using only O(1) extra space?
Example 1:
Input: chars = ["a","a","b","b","c","c","c"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]
Explanation: The groups are "aa", "bb", and "ccc". This compresses to "a2b2c3".
Example 2:
Input: chars = ["a"]
Output: Return 1, and the first character of the input array should be: ["a"]
Explanation: The only group is "a", which remains uncompressed since it's a single character.
Example 3:
Input: chars = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
Output: Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].
Explanation: The groups are "a" and "bbbbbbbbbbbb". This compresses to "ab12".
Example 4:
Input: chars = ["a","a","a","b","b","a","a"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","3","b","2","a","2"].
Explanation: The groups are "aaa", "bb", and "aa". This compresses to "a3b2a2". Note that each group is independent even if two groups have the same character.
Constraints:
1 <= chars.length <= 2000
chars[i] is a lower-case English letter, upper-case English letter, digit, or symbol.
Solution
class Solution:
def compress(self, chars: List[str]) -> int:
if len(chars) < 2: return len(chars)
read, write = 1, 0
count = 1
while read < len(chars):
if chars[read] == chars[read-1]:
count += 1
elif count > 1:
chars[write] = chars[read-1]
for s in list(str(count)):
write += 1
chars[write] = s
write += 1
count = 1
else:
chars[write] = chars[read-1]
write += 1
read += 1
if count == 1:
chars[write] = chars[read-1]
write += 1
else:
chars[write] = chars[read-1]
for s in list(str(count)):
write += 1
chars[write] = s
write += 1
return write
30 - Substring with Concatenation of All Words
Problem
You are given a string s and an array of strings words of the same length. Return all starting indices of substring(s) in s that is a concatenation of each word in words exactly once, in any order, and without any intervening characters.
You can return the answer in any order.
Example 1:
Input: s = "barfoothefoobarman", words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoo" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:
Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
Output: []
Example 3:
Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
Output: [6,9,12]
Solution
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
length = len(words[0])
total_words = len(words)
words = Counter(words)
ans = []
def dfs(start, i, path):
if len(path) == total_words:
ans.append(start)
return
if i >= len(s):
return
w = s[i:i+length]
if words[w]:
words[w] -= 1
dfs(start, i+length, path+[w])
words[w] += 1
for i in range(len(s)-length*total_words+1):
dfs(i, i, [])
return ans
1010 - Pairs of Songs With Total Durations Divisible by 60
Problem
You are given a list of songs where the ith song has a duration of time[i] seconds.
Return the number of pairs of songs for which their total duration in seconds is divisible by 60. Formally, we want the number of indices i, j such that i < j with (time[i] + time[j]) % 60 == 0.
Example 1:
Input: time = [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
Example 2:
Input: time = [60,60,60]
Output: 3
Explanation: All three pairs have a total duration of 120, which is divisible by 60.
Constraints:
1 <= time.length <= 6 * 104
1 <= time[i] <= 500
Solution
class Solution:
def numPairsDivisibleBy60(self, time: List[int]) -> int:
counter = Counter()
ans = 0
for t in time:
ans += counter[(60 - t%60)%60]
counter[t%60] += 1
return ans
235 - Lowest Common Ancestor of a Binary Search Tree
Problem
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.
Solution
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while root and (p.val-root.val) * (q.val-root.val) > 0:
if p.val - root.val < 0:
root = root.left
else:
root = root.right
return root
701 - Insert into a Binary Search Tree
Problem
You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.
Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.
Solution
class Solution:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if not root:
return TreeNode(val)
if val > root.val:
root.right = self.insertIntoBST(root.right, val)
else:
root.left = self.insertIntoBST(root.left, val)
return root
323 - Number of Connected Components in an Undirected Graph
Problem
Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected graph.
Example 1:
Input: n = 5 and edges = [[0, 1], [1, 2], [3, 4]]
0 3
| |
1 --- 2 4
Output: 2
Example 2:
Input: n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]]
0 4
| |
1 --- 2 --- 3
Output: 1
Note:
You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.
Solution
from collections import defaultdict
class Solution():
def ConnectedComponents(self, edges):
graph = defaultdict(list)
marked = {}
for edge in edges:
graph[edge[0]].append(edge[1])
graph[edge[1]].append(edge[0])
marked[edge[0]] = False
marked[edge[1]] = False
ans = 0
for v in graph.keys():
if not marked[v]:
self.dfs(graph, v, marked)
ans += 1
return ans
def dfs(self, graph, v, marked):
marked[v] = True
for w in graph[v]:
if not marked[w]:
self.dfs(graph, w, marked)
print(Solution().ConnectedComponents([[0, 1], [1, 2], [3, 4]]))
863 - All Nodes Distance K in Binary Tree
Problem
We are given a binary tree (with root node root), a target node, and an integer value K.
Return a list of the values of all nodes that have a distance K from the target node. The answer can be returned in any order.
Example 1:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
Output: [7,4,1]
Explanation:
The nodes that are a distance 2 from the target node (with value 5)
have values 7, 4, and 1.
Note:
1. The given tree is non-empty.
2. Each node in the tree has unique values 0 <= node.val <= 500.
3. The target node is a node in the tree.
4. 0 <= K <= 1000.
Solution
BFS
- Build a graph. Connet the parent and child in both directions.
- Start BFS search from the target value, with step of K.
In K step, the element in the new level are the answer.
class Solution:
def distanceK(self, root: TreeNode, target: TreeNode, K: int) -> List[int]:
graph = defaultdict(list)
self.connect(graph, root, root.left)
self.connect(graph, root, root.right)
level = [target.val]
marked = set(level)
for i in range(K):
new_level = []
for x in level:
for w in graph[x]:
if w not in marked:
new_level.append(w)
level = new_level
marked |= set(new_level)
return level
def connect(self, graph, parent, child):
if parent and child:
graph[parent.val].append(child.val)
graph[child.val].append(parent.val)
self.connect(graph, child, child.left)
self.connect(graph, child, child.right)
515 - Find Largest Value in Each Tree Row
Problem
Given the root of a binary tree, return an array of the largest value in each row of the tree (0-indexed).
Solution
class Solution:
def largestValues(self, root: TreeNode) -> List[int]:
if not root:
return []
ans = []
level = [root]
seen = set(level)
while level:
new_level = []
max_val = -float('inf')
for node in level:
if node:
if node.left:
new_level.append(node.left)
if node.right:
new_level.append(node.right)
max_val = max(max_val, node.val)
ans.append(max_val)
level = new_level
return ans
42 - Trapping Rain Water
Problem
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.
Solution
class Solution:
def trap(self, height: List[int]) -> int:
ans = 0
leftHeight = self.leftMaxHeight(height)
rightHeight = self.rightMaxHeight(height)
for i in range(len(height)):
max_bar = min(leftHeight[i], rightHeight[i])
if max_bar > height[i]:
ans += max_bar - height[i]
return ans
def leftMaxHeight(self, height):
ans = [0] * len(height)
max_left = 0
for i in range(len(height)):
ans[i] = max_left
max_left = max(height[i], max_left)
return ans
def rightMaxHeight(self, height):
ans = [0] * len(height)
max_right = 0
for i in range(len(height)-1, -1, -1):
ans[i] = max_right
max_right = max(height[i], max_right)
return ans
236 - Lowest Common Ancestor of a Binary Tree
Problem
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Solution
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root:
return None
if root.val == p.val or root.val == q.val:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left:
return right
elif not right:
return left
else:
return root