Skip to content

Commit 1e56f95

Browse files
committed
update
1 parent 0554114 commit 1e56f95

14 files changed

+297
-29
lines changed

README.md

Lines changed: 21 additions & 9 deletions
Large diffs are not rendered by default.

[A]tree/tree.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- connected
99
- acyclic
1010
- non-direction edges
11-
- one path/edge between any two vertices/node
11+
- one path between any two vertices/nodes
1212
- important tree concepts
1313
- traversal of the tree
1414
- depth/height of tree
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class Solution:
2+
def removeKdigits(self, num: str, k: int) -> str:
3+
stack = []
4+
for c in num:
5+
while stack and stack[- 1] > c and k:
6+
stack.pop()
7+
k -= 1
8+
stack.append(c)
9+
10+
while stack and k:
11+
stack.pop()
12+
k -= 1
13+
14+
res = ''.join(stack).lstrip('0')
15+
return res if res else '0'
16+
17+
# time O(n)
18+
# space O(n)
19+
# using stack and queue and montonic and monotonic stack (consider one side’s relationship)
20+
'''
21+
1. 21: when met 1, we pop 2 out and choose 1 to make num smaller
22+
2. 12: when met 1, we cannot pop 1 cause it will make num larger
23+
'''
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Solution:
2+
def nextGreaterElements(self, nums: List[int]) -> List[int]:
3+
res = [- 1 for _ in range(len(nums))]
4+
stack = []
5+
for i in range(2 * len(nums)):
6+
j = i % len(nums)
7+
while stack and nums[stack[- 1]] < nums[j]:
8+
idx = stack.pop()
9+
res[idx] = nums[j]
10+
if i < len(nums):
11+
stack.append(i)
12+
return res
13+
14+
# time O(n), each num will only pop and push once and both cost O(1)
15+
# space O(n), due to deque's size
16+
# using stack and queue and montonic and monotonic stack (consider one side’s relationship)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Solution:
2+
def largestRectangleArea(self, heights: List[int]) -> int:
3+
heights = [0] + heights + [0]
4+
res = 0
5+
stack = []
6+
for i in range(len(heights)):
7+
while stack and heights[stack[- 1]] > heights[i]:
8+
idx = stack.pop()
9+
height = heights[idx]
10+
left_bound = stack[- 1]
11+
right_bound = i
12+
width = right_bound - left_bound - 1
13+
res = max(res, height * width)
14+
stack.append(i)
15+
return res
16+
17+
# time O(n)
18+
# space O(n), due to stack
19+
# using stack and queue and montonic and monotonic stack (consider two side’s relationship)
20+
'''
21+
1. decide height
22+
2. then find the left smaller element and right smaller element
23+
3. count the area between these two bound
24+
'''
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class Solution:
2+
def maximalRectangle(self, matrix: List[List[str]]) -> int:
3+
4+
def helper(heights):
5+
heights = [0] + heights + [0]
6+
stack = []
7+
res = 0
8+
for i in range(len(heights)):
9+
while stack and heights[stack[- 1]] > heights[i]:
10+
idx = stack.pop()
11+
res = max(res, heights[idx] * (i - stack[- 1] - 1))
12+
stack.append(i)
13+
return res
14+
15+
res = 0
16+
rows, cols = len(matrix), len(matrix[0])
17+
prefix = [0 for _ in range(cols)]
18+
for r in range(rows):
19+
for c in range(cols):
20+
if matrix[r][c] == "1":
21+
prefix[c] += 1
22+
else:
23+
prefix[c] = 0
24+
res = max(res, helper(prefix))
25+
return res
26+
27+
# time O(RC)
28+
# space O(C)
29+
# using stack and queue and montonic and monotonic stack (consider two side’s relationship) and prefix

[I]stack-queue/stack-queue.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
- user two stack or dll
112112
- can simulate nested list iterator
113113
- **use monotonic queue and sliding window**
114-
- if we want to maintain the min/max value and there is a constraint/consideration about the length of valid subarray (sliding window)
114+
- if we want to get the min/max value for each subarray, and there is a constraint/consideration about subarray's length (sliding window)
115115
- eg. get every [i, i + k - 1] subarray's min/max value
116116
- naive way might take O(nk)
117117
- use monotonic queue only spend O(n)
@@ -123,15 +123,14 @@
123123
- when to record the cur best res
124124
- (order of these steps is not guaranteed)
125125
- **use monotonic stack (consider one or two side’s relationship)**
126+
- if we want to get first relative smaller/larger and left/right neighbors for each element
127+
- if we want to get smallest/largest element in every subarray
126128
- notice
127129
- when to pop element at stack's end
128130
- when to add element at stack’s end
129131
- monotonic increasing or decreasing array will be generated
130132
- there’s two type of this technique
131133
- consider one side’s relationship
132134
- consider two side’s relationship
133-
- can find out the left and/or right bound for each element in some condition
134-
- finding the left and/or right relative smaller/larger elements for each element
135-
- finding the smallest/largest element in every subarray
136135
- can use sentinel element at left bound or/and right bound
137136
- help us to handle edge case
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Solution:
2+
def letterCombinations(self, digits: str) -> List[str]:
3+
if not digits:
4+
return []
5+
res = []
6+
num_chars = {'2': 'abc', '3': 'def', '4':'ghi', '5':'jkl', '6':'mno', '7':'pqrs', '8':'tuv', '9':'wxyz'}
7+
8+
def dfs(path, idx):
9+
if len(path) == len(digits):
10+
res.append(''.join(path))
11+
return
12+
for c in num_chars[digits[idx]]:
13+
path.append(c)
14+
dfs(path, idx + 1)
15+
path.pop()
16+
17+
dfs([], 0)
18+
return res
19+
20+
# time O(4**n), n is the length of digits
21+
# space O(n), due to recursion stack, output is O(4**n)
22+
# using dfs and backtracking and backtracking with constraints
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class Solution:
2+
def permute(self, nums: List[int]) -> List[List[int]]:
3+
res = []
4+
5+
def dfs(path, visited):
6+
if len(path) == len(nums):
7+
res.append(path[:])
8+
return
9+
for i in range(len(nums)):
10+
if i not in visited:
11+
visited.add(i)
12+
path.append(nums[i])
13+
dfs(path, visited)
14+
visited.remove(i)
15+
path.pop()
16+
17+
dfs([], set())
18+
return res
19+
20+
# time O(n*n!), dfs will calls n! times and each non-leaf node traverse list costs O(n) and leaf node copy a list to answer costs O(n)
21+
# space O(n*n!), because answer contains n! permutations and each permutaiton can cost O(n). Besides, memory stack size is O(n)
22+
# using dfs and backtracking and permutation
23+
'''
24+
1. type: permutation
25+
2. duplicate elements: no
26+
3. selectable repeatedly: no
27+
'''
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class Solution:
2+
def combine(self, n: int, k: int) -> List[List[int]]:
3+
res = []
4+
5+
def dfs(path, idx):
6+
if len(path) == k:
7+
res.append(path[:])
8+
return
9+
for i in range(idx, n + 1):
10+
path.append(i)
11+
dfs(path, i + 1)
12+
path.pop()
13+
14+
dfs([], 1)
15+
return res
16+
17+
# time O(n!/(n-k)!k! * k)
18+
# space O(k), not count output
19+
# using dfs and backtracking and combination
20+
'''
21+
1. type: combination
22+
2. duplicate elements: no
23+
3. selectable repeatedly: no
24+
'''
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
class Solution:
2+
def subsets(self, nums: List[int]) -> List[List[int]]:
3+
res = []
4+
5+
def dfs(path, idx):
6+
if idx == len(nums):
7+
res.append(path[:])
8+
return
9+
dfs(path, idx + 1)
10+
11+
path.append(nums[idx])
12+
dfs(path, idx + 1)
13+
path.pop()
14+
15+
dfs([], 0)
16+
return res
17+
18+
# time O(n*(2**n)), due to each element can take or not take (2**n), and n for copy list
19+
# space O(n), due to recursion stack, not counting output here
20+
# using dfs and backtracking and subset
21+
'''
22+
1. type: subset
23+
2. duplicate elements: no
24+
3. selectable repeatedly: no
25+
'''
26+
'''
27+
this approach focus on considering each element:
28+
for cur element,
29+
we don't take, then goto next element
30+
or we take, then goto next element
31+
till no more element to be considered, we record path
32+
'''
33+
34+
class Solution:
35+
def subsets(self, nums: List[int]) -> List[List[int]]:
36+
res = []
37+
38+
def dfs(path, idx):
39+
if idx == len(nums):
40+
res.append(path[:])
41+
return
42+
43+
res.append(path[:])
44+
for i in range(idx, len(nums)):
45+
path.append(nums[i])
46+
dfs(path, i + 1)
47+
path.pop()
48+
49+
dfs([], 0)
50+
return res
51+
52+
# time O(n*(2**n)), due to each element can take or not take (2**n), and n for copy list
53+
# space O(n), due to recursion stack, not counting output here
54+
# using dfs and backtracking and subset
55+
'''
56+
1. type: subset
57+
2. duplicate elements: no
58+
3. selectable repeatedly: no
59+
'''
60+
'''
61+
this approach focus on constructing the path:
62+
record cur path,
63+
then choose a element to add in path
64+
65+
more specific:
66+
we have chosen nothing, record, try to add first element
67+
we have chosen one element, record, try to add another element
68+
we have chosen two elements, record, try to add another element
69+
we have chosen three elements, record, try to add another element
70+
'''

[J]backtracking/backtracking.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,24 @@
77
- making decisions
88
- each round we got multi choices (must pick one)
99
- each round we choose sth or not choose
10-
- notice elements are duplicate or not
10+
- notice elements are duplicate or unique
1111
- if so, we need to take care of pruning
1212
- notice elements can be chosen repeatedly or not
1313
- if not, we need to maintain a memo
1414
- notice that inside backtracking, we can use the boolean value as a return value to indicate whether there is a valid answer or not
1515

1616
```python
1717

18-
def backtrack(res, path, count, memo, index/node):
19-
if BOUND_REACHED:
20-
return
21-
22-
if GOAL_REACHED:
23-
res.append(COPIED_PATH)
18+
def backtrack(res, path, count, visited, index/node):
19+
if BOUND_REACHED:
20+
if GOAL_REACHED:
21+
RECORD_RESULT
2422
return
2523

2624
for CHOCIE in CHOICES:
2725
if CHOICE is VALID:
2826
MAKE_CHOICE
29-
backtrack(res, path, count, memo, index/node)
27+
backtrack(res, path, count, visited, index/node)
3028
UNDO_CHOICE
3129
```
3230

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Solution:
2+
def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
3+
res = 0
4+
rows, cols = len(matrix), len(matrix[0])
5+
counts = [[- 1 for _ in range(cols)] for _ in range(rows)]
6+
7+
def dfs(r, c):
8+
next_count = 0
9+
for next_r, next_c in [(r+1, c), (r-1, c), (r, c+1), (r, c-1)]:
10+
if next_r not in range(rows) or next_c not in range(cols) or matrix[r][c] >= matrix[next_r][next_c]:
11+
continue
12+
if counts[next_r][next_c] == - 1:
13+
next_count = max(next_count, dfs(next_r, next_c))
14+
else:
15+
next_count = max(next_count, counts[next_r][next_c])
16+
counts[r][c] = 1 + next_count
17+
return counts[r][c]
18+
19+
for r in range(rows):
20+
for c in range(cols):
21+
res = max(res, dfs(r, c))
22+
return res
23+
24+
# time O(RC)
25+
# space O(RC)
26+
# using graph and dfs

[K]graph/graph.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,32 @@
22

33
## intro
44

5-
- A graph is most commonly stored as a hashmap/list of adjacency lists/sets: for each vertex, store a list of its neighbors
5+
- A graph is most commonly stored as a hashmap/list of adjacency lists/sets: for each vertex, store a list/set of its neighbors
66
- how to build graph is important
77
- often use
88
- elements inside set can be tuples
9-
109
```python
1110
graph = defaultdict(set)
1211
```
13-
14-
- tree is a special graph with properties that
12+
- notice: tree is a special graph with properties that
1513
- connected
1614
- acyclic
1715
- non-direction edges
18-
- one path/edge between any two vertices/node
16+
- one path between any two vertices/nodes
1917

2018
## graph dfs pattern
2119

2220
- DFS
2321
- time `O(|V| + |E|)`
24-
- space `O(|V| + |E|)` for visited hashset, and `O(|V|)` for recursion stack
22+
- space `O(|V|)` for visited hashset, and recursion stack
2523
- DFS is better at
2624
- finding nodes far away from the root
2725

2826
## graph bfs pattern
2927

3028
- BFS
3129
- time `O(|V| + |E|)`
32-
- space `O(|V| + |E|)` for visited hashset, and `O(|V|)` for queue
30+
- space `O(|V|)` for visited hashset, and queue
3331
- tips
3432
- shortest distance of a to b is equal to the distance of b to a
3533
- sometimes can use topological sort’s idea

0 commit comments

Comments
 (0)