Skip to content

Commit cc52aba

Browse files
authored
Merge branch 'DaleStudy:main' into main
2 parents dde3d5d + 9452e8f commit cc52aba

File tree

20 files changed

+927
-0
lines changed

20 files changed

+927
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//
2+
// 102. Binary Tree Level Order Traversal
3+
// https://leetcode.com/problems/binary-tree-level-order-traversal/description/
4+
// Dale-Study
5+
//
6+
// Created by WhiteHyun on 2024/06/12.
7+
//
8+
9+
// MARK: - Node
10+
11+
private class Node<T> {
12+
let element: T
13+
var next: Node<T>?
14+
15+
init(element: T, next: Node<T>? = nil) {
16+
self.element = element
17+
self.next = next
18+
}
19+
}
20+
21+
// MARK: - Queue
22+
23+
final class Queue<T> {
24+
private var head: Node<T>?
25+
private var tail: Node<T>?
26+
private var count: Int = 0
27+
28+
var isEmpty: Bool {
29+
count == 0
30+
}
31+
32+
func enqueue(_ element: T) {
33+
let newNode = Node(element: element)
34+
35+
if let tailNode = tail {
36+
tailNode.next = newNode
37+
} else {
38+
head = newNode
39+
}
40+
41+
tail = newNode
42+
count += 1
43+
}
44+
45+
func dequeue() -> T? {
46+
guard let headNode = head
47+
else {
48+
return nil
49+
}
50+
51+
let element = headNode.element
52+
head = headNode.next
53+
54+
if head == nil {
55+
tail = nil
56+
}
57+
58+
count -= 1
59+
return element
60+
}
61+
62+
func peek() -> T? {
63+
head?.element
64+
}
65+
66+
func clear() {
67+
head = nil
68+
tail = nil
69+
count = 0
70+
}
71+
}
72+
73+
// MARK: ExpressibleByArrayLiteral
74+
75+
extension Queue: ExpressibleByArrayLiteral {
76+
convenience init(arrayLiteral elements: T...) {
77+
self.init()
78+
for element in elements {
79+
enqueue(element)
80+
}
81+
}
82+
}
83+
84+
class Solution {
85+
func levelOrder(_ root: TreeNode?) -> [[Int]] {
86+
guard let root else { return [] }
87+
var array: [[Int]] = []
88+
let queue: Queue<(node: TreeNode, layer: Int)> = [(root, 0)]
89+
90+
while let (node, layer) = queue.dequeue() {
91+
// array index 범위에 layer가 들어있지 않으면, 마지막 요소에 빈 배열 추가
92+
if (array.indices ~= layer) == false {
93+
array.append([])
94+
}
95+
array[layer].append(node.val)
96+
97+
if node.left != nil {
98+
queue.enqueue((node.left!, layer + 1))
99+
}
100+
if node.right != nil {
101+
queue.enqueue((node.right!, layer + 1))
102+
}
103+
}
104+
105+
return array
106+
}
107+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
102. Binary Tree Level Order Traversal
3+
https://leetcode.com/problems/binary-tree-level-order-traversal/
4+
"""
5+
6+
from typing import List, Optional
7+
8+
9+
# Definition for a binary tree node.
10+
class TreeNode:
11+
def __init__(self, val=0, left=None, right=None):
12+
self.val = val
13+
self.left = left
14+
self.right = right
15+
16+
17+
"""
18+
Solution
19+
Breadth First Search (BFS) using Queue
20+
21+
The problem is asking to return the node values at each level of the binary tree.
22+
To solve this problem, we can use BFS algorithm with a queue.
23+
We will traverse the tree level by level and store the node values at each level.
24+
25+
1. Initialize an empty list to store the output.
26+
2. Initialize an empty queue.
27+
3. Add the root node to the queue.
28+
4. While the queue is not empty, do the following:
29+
- Get the size of the queue to know the number of nodes at the current level.
30+
- Initialize an empty list to store the node values at the current level.
31+
- Traverse the nodes at the current level and add the node values to the list.
32+
- If the node has left or right child, add them to the queue.
33+
- Decrease the level size by 1.
34+
- Add the list of node values at the current level to the output.
35+
5. Return the output.
36+
37+
Time complexity: O(N)
38+
- We visit each node once
39+
40+
Space complexity: O(N)
41+
- The maximum number of nodes in the queue is the number of nodes at the last level
42+
- The maximum number of nodes at the last level is N/2
43+
- The output list stores the node values at each level which is N
44+
- Thus, the space complexity is O(N)
45+
46+
"""
47+
48+
49+
class Solution:
50+
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
51+
if root is None:
52+
return
53+
output = []
54+
queue = []
55+
queue.append(root)
56+
57+
while len(queue) > 0:
58+
level_size = len(queue)
59+
level_output = []
60+
61+
while level_size > 0:
62+
node = queue.pop(0)
63+
level_output.append(node.val)
64+
65+
if node.left is not None:
66+
queue.append(node.left)
67+
if node.right is not None:
68+
queue.append(node.right)
69+
70+
level_size -= 1
71+
72+
output.append(level_output)
73+
74+
return output
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Time Complexity: O(n)
2+
// Space Complexity: O(n)
3+
4+
var levelOrder = function (root) {
5+
// if the root is null, return an empty array.
6+
if (root === null) return [];
7+
8+
const result = [];
9+
const queue = [root];
10+
11+
// while there are nodes in the queue,
12+
while (queue.length > 0) {
13+
const levelSize = queue.length;
14+
const currentLevel = [];
15+
16+
// loop nodes in the current level.
17+
for (let i = 0; i < levelSize; i++) {
18+
// dequeue the front node.
19+
const currentNode = queue.shift();
20+
// add value to the current level array.
21+
currentLevel.push(currentNode.val);
22+
// enqueue left child if exists.
23+
if (currentNode.left) queue.push(currentNode.left);
24+
// enqueue right child if exists.
25+
if (currentNode.right) queue.push(currentNode.right);
26+
}
27+
28+
// add the current level array to the result.
29+
result.push(currentLevel);
30+
}
31+
32+
return result;
33+
};

combination-sum/evan.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from typing import List
2+
3+
4+
class Solution:
5+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
6+
def backtrack(remain, combination, candidateIndex):
7+
if remain == 0:
8+
# if we reach the target, add the combination to the result
9+
result.append(list(combination))
10+
return
11+
elif remain < 0:
12+
# if we exceed the target, no need to proceed
13+
return
14+
15+
for i in range(candidateIndex, len(candidates)):
16+
# add the number to the current combination
17+
combination.append(candidates[i])
18+
# continue the exploration with the current number
19+
backtrack(remain - candidates[i], combination, i)
20+
# backtrack by removing the number from the combination
21+
combination.pop()
22+
23+
result = []
24+
backtrack(target, [], 0)
25+
26+
return result
27+
28+
29+
# Time Complexity: O(N^(target / min(candidates)))
30+
# The time complexity depends on:
31+
# - N: The number of candidates (branching factor at each level).
32+
# - target: The target sum we need to achieve.
33+
# - min(candidates): The smallest element in candidates influences the maximum depth of the recursion tree.
34+
# In the worst case, we branch N times at each level, and the depth can be target / min(candidates).
35+
# Therefore, the overall time complexity is approximately O(N^(target / min(candidates))).
36+
37+
# Space Complexity: O(target / min(candidates))
38+
# The space complexity is influenced by the maximum depth of the recursion tree:
39+
# - target: The target sum we need to achieve.
40+
# - min(candidates): The smallest element in candidates influences the maximum depth.
41+
# The recursion stack can go as deep as target / min(candidates), so the space complexity is O(target / min(candidates)).
42+
# Additionally, storing the results can take significant space, but it depends on the number of valid combinations found.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from typing import List, Optional
2+
3+
4+
class TreeNode:
5+
def __init__(self, val=0, left=None, right=None):
6+
self.val = val
7+
self.left = left
8+
self.right = right
9+
10+
11+
class Solution:
12+
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
13+
if not preorder or not inorder:
14+
return None
15+
16+
root_val = preorder.pop(0)
17+
18+
root_inorder_index = inorder.index(root_val)
19+
20+
left_tree_inorder = inorder[:root_inorder_index]
21+
right_tree_inorder = inorder[root_inorder_index + 1 :]
22+
23+
return TreeNode(
24+
root_val,
25+
self.buildTree(preorder, left_tree_inorder),
26+
self.buildTree(preorder, right_tree_inorder),
27+
)
28+
29+
30+
# Overall time complexity: O(N^2)
31+
# - Finding the root in the inorder list and splitting it takes O(N) time in each call.
32+
# - There are N nodes, so this operation is repeated N times.
33+
34+
# Overall space complexity: O(N)
35+
# - The primary space usage is the recursion stack which goes as deep as the height of the tree.
36+
# - In the worst case (unbalanced tree), this can be O(N).
37+
# - In the best case (balanced tree), this is O(log N).

implement-trie-prefix-tree/evan.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
class Trie:
2+
def __init__(self):
3+
self.children = {}
4+
self.is_end_of_word = False
5+
6+
def insert(self, word: str) -> None:
7+
node = self
8+
9+
for char in word:
10+
if char not in node.children:
11+
node.children[char] = Trie()
12+
13+
node = node.children[char]
14+
15+
node.is_end_of_word = True
16+
17+
def search(self, word: str) -> bool:
18+
node = self
19+
20+
for char in word:
21+
if char not in node.children:
22+
return False
23+
24+
node = node.children[char]
25+
26+
return node.is_end_of_word
27+
28+
def startsWith(self, prefix: str) -> bool:
29+
node = self
30+
31+
for char in prefix:
32+
if char not in node.children:
33+
return False
34+
35+
node = node.children[char]
36+
37+
return True

kth-smallest-element-in-a-bst/evan.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Definition for a binary tree node.
2+
from typing import Optional
3+
4+
5+
class TreeNode:
6+
def __init__(self, val=0, left=None, right=None):
7+
self.val = val
8+
self.left = left
9+
self.right = right
10+
11+
12+
class Solution:
13+
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
14+
result = []
15+
16+
def inorderTraverse(node: Optional[TreeNode]):
17+
if node is None or len(result) >= k:
18+
return
19+
20+
inorderTraverse(node.left)
21+
22+
if len(result) < k:
23+
result.append(node.val)
24+
25+
inorderTraverse(node.right)
26+
27+
inorderTraverse(root)
28+
29+
return result[k - 1]
30+
31+
32+
# Time Complexity: O(N)
33+
# In the worst case, we need to visit all the nodes in the tree.
34+
# Thus, the time complexity is O(N), where N is the number of nodes in the tree.
35+
36+
# Space Complexity: O(N)
37+
# The space complexity is determined by the recursion stack and the result list.
38+
# 1. Recursion stack: In the worst case (unbalanced tree), the recursion stack can go up to N levels deep, so the space complexity is O(N).
39+
# In the best case (balanced tree), the recursion stack depth is log(N), so the space complexity is O(log N).
40+
# 2. Result list: The result list stores up to k elements, so the space complexity is O(k).

0 commit comments

Comments
 (0)