Skip to content

Commit 20e96ce

Browse files
authored
Merge branch 'DaleStudy:main' into main
2 parents 36743b0 + cfc0c46 commit 20e96ce

File tree

16 files changed

+933
-0
lines changed

16 files changed

+933
-0
lines changed

container-with-most-water/EGON.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def maxArea(self, height: List[int]) -> int:
7+
return self.solveWithTwoPointer(height)
8+
9+
"""
10+
Runtime: 527 ms (Beats 47.12%)
11+
Time Complexity: O(n)
12+
- 투 포인터의 총합 조회 범위가 height의 길이와 같으므로 O(n)
13+
- area 갱신을 위한 계산에서 항이 2개인 max와 항이 2개인 min 중첩에 O(2) * O(2)
14+
> O(n) * O(4) ~= O(n)
15+
16+
Memory: 29.61 MB (Beats 38.93%)
17+
Space Complexity: O(1)
18+
> 정수형 변수들만 사용했으므로 O(1)
19+
"""
20+
def solveWithTwoPointer(self, height: List[int]) -> int:
21+
left, right = 0, len(height) - 1
22+
area = 0
23+
while left < right:
24+
area = max(area, (right - left) * min(height[right], height[left]))
25+
26+
if height[left] <= height[right]:
27+
left += 1
28+
else:
29+
right -= 1
30+
31+
return area
32+
33+
34+
class _LeetCodeTestCases(TestCase):
35+
def test_1(self):
36+
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
37+
output = 49
38+
self.assertEqual(Solution.maxArea(Solution(), height), output)
39+
40+
def test_2(self):
41+
height = [1, 1]
42+
output = 1
43+
self.assertEqual(Solution.maxArea(Solution(), height), output)
44+
45+
46+
if __name__ == '__main__':
47+
main()

container-with-most-water/HC-kang.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* https://leetcode.com/problems/container-with-most-water/
3+
* T.C. O(n)
4+
* S.C. O(1)
5+
*/
6+
function maxArea(height: number[]): number {
7+
let [res, i, j] = [0, 0, height.length - 1];
8+
9+
while (i < j) {
10+
const volume = Math.min(height[i], height[j]) * (j - i);
11+
res = Math.max(res, volume);
12+
13+
if (height[i] < height[j]) {
14+
i++;
15+
} else {
16+
j--;
17+
}
18+
}
19+
20+
return res;
21+
}

container-with-most-water/flynn.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* 풀이
3+
* - container의 밑변이 넓고 높이가 높을 수록 저장하는 물의 양이 많습니다
4+
* - 따라서 밑변이 가장 넓은 container부터 탐색하면 탐색 효율을 높일 수 있다는 생각을 했습니다
5+
* - 양 끝에서부 two pointer를 이용하여 탐색을 시작합니다
6+
* - lo, hi 중에서 높이가 더 낮은 pointer를 안쪽으로 이동시킵니다
7+
* - 왜냐하면 높이가 더 낮은 pointer를 이동시켰을 때 기존 container보다 더 높이가 높은 container를 만들 수 있는 가능성이 생기기 때문입니다
8+
*
9+
* Big O
10+
* - N: 주어진 배열 height의 크기
11+
*
12+
* - Time complexity: O(N)
13+
* - 배열 height를 조회하므로 전체 실행시간 또한 N에 비례하여 선형적으로 증가합니다
14+
*
15+
* - Space complexity: O(1)
16+
*/
17+
18+
class Solution {
19+
public:
20+
int maxArea(vector<int>& height) {
21+
int n = height.size();
22+
int lo = 0;
23+
int hi = n - 1;
24+
25+
int res = 0;
26+
while (lo < hi) {
27+
int w = hi - lo;
28+
int h = min(height[lo], height[hi]);
29+
30+
res = max(res, w * h);
31+
32+
if (height[lo] > height[hi]) {
33+
--hi;
34+
} else {
35+
++lo;
36+
}
37+
}
38+
39+
return res;
40+
}
41+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from unittest import TestCase, main
2+
3+
4+
class Node:
5+
6+
def __init__(self, key):
7+
self.key = key
8+
self.isWordEnd = False
9+
self.children = {}
10+
11+
12+
class Trie:
13+
WILD_CARD = '.'
14+
15+
def __init__(self):
16+
self.root = Node(None)
17+
18+
def insert(self, word: str) -> None:
19+
curr_node = self.root
20+
for char in word:
21+
if char not in curr_node.children:
22+
curr_node.children[char] = Node(char)
23+
24+
curr_node = curr_node.children[char]
25+
curr_node.isWordEnd = True
26+
27+
def search(self, node: Node, word: str, idx: int) -> bool:
28+
if idx == len(word):
29+
return node.isWordEnd
30+
31+
for idx in range(idx, len(word)):
32+
if word[idx] == self.WILD_CARD:
33+
for child in node.children.values():
34+
if self.search(child, word, idx + 1) is True:
35+
return True
36+
else:
37+
return False
38+
39+
if word[idx] in node.children:
40+
return self.search(node.children[word[idx]], word, idx + 1)
41+
else:
42+
return False
43+
44+
45+
"""
46+
Runtime: 1810 ms (Beats 22.46%)
47+
Time Complexity:
48+
> addWord: word의 길이만큼 순회하므로 O(L)
49+
> search:
50+
- word의 평균 길이를 W이라하면,
51+
- '.'가 포함되어 있지 않는 경우 O(W), early return 가능하므로 upper bound
52+
- '.'가 포함되어 있는 경우, 해당 노드의 child만큼 재귀, trie의 평균 자식 수를 C라 하면 O(W) * O(C), early return 가능하므로 upper bound
53+
- trie의 평균 자식 수는 addWord의 실행횟수 C'에 선형적으로 비레(겹치는 char가 없는 경우 upper bound)
54+
> O(W) * O(C) ~= O(W) * O(C') ~= O(W * C'), upper bound
55+
56+
Memory: 66.78 MB (Beats 12.26%)
57+
Space Complexity: O(1)
58+
> addWord:
59+
- 삽입한 word의 평균 길이 L만큼 Node가 생성 및 Trie에 추가, O(L)
60+
- addWord의 실행횟수 C'에 비례, O(C')
61+
> O(L) * O(C') ~= O(L * C')
62+
> search:
63+
> 만들어진 Trie와 패러미터 word, 정수 변수 idx를 사용하므로 O(1)
64+
65+
"""
66+
class WordDictionary:
67+
68+
def __init__(self):
69+
self.trie = Trie()
70+
71+
def addWord(self, word: str) -> None:
72+
self.trie.insert(word)
73+
74+
def search(self, word: str) -> bool:
75+
return self.trie.search(self.trie.root, word, 0)
76+
77+
78+
class _LeetCodeTestCases(TestCase):
79+
def test_1(self):
80+
wordDictionary = WordDictionary()
81+
wordDictionary.addWord("at")
82+
self.assertEqual(wordDictionary.search(".at"), False)
83+
84+
85+
if __name__ == '__main__':
86+
main()
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* https://leetcode.com/problems/design-add-and-search-words-data-structure
3+
*/
4+
// Using Trie
5+
class WordDictionary {
6+
constructor(private root: Record<string, any> = {}) {}
7+
8+
/**
9+
* T.C. O(L) L: length of a word
10+
* S.C. O(L)
11+
*/
12+
addWord(word: string): void {
13+
let node = this.root;
14+
for (const char of word) {
15+
if (!node[char]) {
16+
node[char] = {};
17+
}
18+
node = node[char];
19+
}
20+
node['isEnd'] = true;
21+
}
22+
23+
/**
24+
* T.C. O(N) - there are only 2 dots in the word(26 * 26 * N)
25+
* S.C. O(N * L) N: number of words, L: length of a word
26+
*/
27+
search(word: string): boolean {
28+
return this.dfs(word, this.root);
29+
}
30+
31+
private dfs(word: string, node: Record<string, any>): boolean {
32+
for (let i = 0; i < word.length; i++) {
33+
if (word[i] === '.') {
34+
for (const key in node) {
35+
if (this.dfs(word.slice(i + 1), node[key])) {
36+
return true;
37+
}
38+
}
39+
return false;
40+
}
41+
if (!node[word[i]]) {
42+
return false;
43+
}
44+
node = node[word[i]];
45+
}
46+
return !!node['isEnd'];
47+
}
48+
}
49+
50+
// Using Array and Set
51+
class WordDictionary {
52+
constructor(
53+
private words: Set<string>[] = Array.from({ length: 25 }, () => new Set())
54+
) {}
55+
56+
/**
57+
* T.C. O(1)
58+
* S.C. O(N * L)
59+
*/
60+
addWord(word: string): void {
61+
this.words[word.length - 1].add(word);
62+
}
63+
64+
/**
65+
* T.C. O(N * L) N: number of words, L: length of a word
66+
* S.C. O(1)
67+
*/
68+
search(word: string): boolean {
69+
const hasDot = word.indexOf('.') !== -1;
70+
const set = this.words[word.length - 1];
71+
72+
if (!hasDot) {
73+
return set.has(word);
74+
}
75+
76+
for (const w of set) {
77+
let i = 0;
78+
while (i < word.length) {
79+
if (word[i] == '.') {
80+
i++;
81+
continue;
82+
}
83+
if (word[i] !== w[i]) {
84+
break;
85+
}
86+
i++;
87+
}
88+
89+
if (i === word.length) {
90+
return true;
91+
}
92+
}
93+
return false;
94+
}
95+
}
96+
97+
/**
98+
* Your WordDictionary object will be instantiated and called as such:
99+
* var obj = new WordDictionary()
100+
* obj.addWord(word)
101+
* var param_2 = obj.search(word)
102+
*/
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* 풀이
3+
* - Trie 구조를 활용하여 풀이할 수 있습니다
4+
* - wildcard인 '.'에 대한 처리가 필요합니다
5+
*
6+
* Big O
7+
* - N: 주어지는 문자열 word의 길이
8+
* - M: 현재 WordDictionary에 저장되어 있는 TrieNode의 수
9+
*
10+
* - void addWord(string word)
11+
* - Time complexity: O(N)
12+
* - Space complexity: O(N)
13+
* - 최악의 경우 word의 모든 문자에 대해 새로운 TrieNode를 추가해야 합니다
14+
*
15+
* - bool search(string word)
16+
* - bool _search(string word, int idx, TrieNode* node)
17+
* - Time complexity: best O(N), worst O(M) < O(26^N)
18+
* - wildcard 사용 및 기존에 저장된 word의 상태에 따라 달라집니다
19+
* - Space complexity: O(N)
20+
* - _search가 재귀적으로 호출되므로 재귀 호출 스택의 깊이만큼 추가적인 공간이 사용됩니다
21+
* - 재귀 호출 스택의 깊이는 현재 찾는 word의 길이에 선형적으로 비례합니다
22+
*/
23+
24+
class TrieNode {
25+
public:
26+
array<TrieNode*, 27> links;
27+
bool word;
28+
29+
TrieNode(): word(false) {
30+
links.fill(nullptr);
31+
}
32+
};
33+
34+
class WordDictionary {
35+
public:
36+
WordDictionary(): root(new TrieNode()) {}
37+
38+
void addWord(string word) {
39+
TrieNode* current = root;
40+
41+
for (char c : word) {
42+
if (current->links[c - 'a'] == nullptr) {
43+
current->links[c - 'a'] = new TrieNode();
44+
}
45+
current = current->links[c - 'a'];
46+
}
47+
48+
current->word = true;
49+
}
50+
51+
bool search(string word) {
52+
return _search(word, 0, root);
53+
}
54+
55+
private:
56+
TrieNode* root;
57+
58+
bool _search(string word, int idx, TrieNode* node) {
59+
if (node == nullptr) return false;
60+
61+
if (word.size() == idx) return node->word;
62+
63+
char c = word[idx];
64+
65+
if (c != '.') {
66+
TrieNode* next_node = node->links[c - 'a'];
67+
int next_idx = idx + 1;
68+
69+
return _search(word, next_idx, next_node);
70+
} else {
71+
for (TrieNode* next_node : node->links) {
72+
int next_idx = idx + 1;
73+
if (_search(word, next_idx, next_node)) return true;
74+
}
75+
return false;
76+
}
77+
}
78+
};
79+
80+
/**
81+
* Your WordDictionary object will be instantiated and called as such:
82+
* WordDictionary* obj = new WordDictionary();
83+
* obj->addWord(word);
84+
* bool param_2 = obj->search(word);
85+
*/

0 commit comments

Comments
 (0)