From 96f433c138d486c56e2b5c3dfbbb03f101c0422a Mon Sep 17 00:00:00 2001 From: obzva Date: Mon, 16 Sep 2024 11:04:41 +0900 Subject: [PATCH 1/8] Solution: Valid Parentheses --- valid-parentheses/flynn.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 valid-parentheses/flynn.cpp diff --git a/valid-parentheses/flynn.cpp b/valid-parentheses/flynn.cpp new file mode 100644 index 000000000..50f45c25f --- /dev/null +++ b/valid-parentheses/flynn.cpp @@ -0,0 +1,31 @@ +/** + * 풀이 + * - stack 자료구조를 이용합니다 + * + * Big O + * - N: 주어진 문자열 s의 길이 + * + * - Time complexity: O(N) + * - 문자열 s 전체를 순회할 경우 실행시간은 N에 선형적으로 비례하여 증가합니다 + * - Space complexity: O(N) + * - "((((((...((((((" 와 같은 입력을 받으면 stack의 크기가 최대 N까지 증가합니다 + */ + +class Solution { +public: + bool isValid(string s) { + stack st; + for (char ch : s) { + if (ch == '(' || ch == '{' || ch == '[') { + st.push(ch); + } else { + if (st.empty()) return false; + else if (st.top() == '(' && ch == ')') st.pop(); + else if (st.top() == '{' && ch == '}') st.pop(); + else if (st.top() == '[' && ch == ']') st.pop(); + else return false; + } + } + return st.empty(); + } +}; From eefe910acfa148443efd3e58357004857f22e9db Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 17 Sep 2024 15:13:19 +0900 Subject: [PATCH 2/8] Solution: Container with Most Water --- container-with-most-water/flynn.cpp | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 container-with-most-water/flynn.cpp diff --git a/container-with-most-water/flynn.cpp b/container-with-most-water/flynn.cpp new file mode 100644 index 000000000..abf5f78a2 --- /dev/null +++ b/container-with-most-water/flynn.cpp @@ -0,0 +1,41 @@ +/** + * 풀이 + * - container의 밑변이 넓고 높이가 높을 수록 저장하는 물의 양이 많습니다 + * - 따라서 밑변이 가장 넓은 container부터 탐색하면 탐색 효율을 높일 수 있다는 생각을 했습니다 + * - 양 끝에서부 two pointer를 이용하여 탐색을 시작합니다 + * - lo, hi 중에서 높이가 더 낮은 pointer를 안쪽으로 이동시킵니다 + * - 왜냐하면 높이가 더 낮은 pointer를 이동시켰을 때 기존 container보다 더 높이가 높은 container를 만들 수 있는 가능성이 생기기 때문입니다 + * + * Big O + * - N: 주어진 배열 height의 크기 + * + * - Time complexity: O(N) + * - 배열 height를 조회하므로 전체 실행시간 또한 N에 비례하여 선형적으로 증가합니다 + * + * - Space complexity: O(1) + */ + +class Solution { +public: + int maxArea(vector& height) { + int n = height.size(); + int lo = 0; + int hi = n - 1; + + int res = 0; + while (lo < hi) { + int w = hi - lo; + int h = min(height[lo], height[hi]); + + res = max(res, w * h); + + if (height[lo] > height[hi]) { + --hi; + } else { + ++lo; + } + } + + return res; + } +}; \ No newline at end of file From b0a4234ec5a54bf2a5eaa5db16cd7312b612146c Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 17 Sep 2024 15:14:26 +0900 Subject: [PATCH 3/8] Fix: line lint --- container-with-most-water/flynn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container-with-most-water/flynn.cpp b/container-with-most-water/flynn.cpp index abf5f78a2..96d95cf6d 100644 --- a/container-with-most-water/flynn.cpp +++ b/container-with-most-water/flynn.cpp @@ -38,4 +38,4 @@ class Solution { return res; } -}; \ No newline at end of file +}; From 02e60bb1e1b341b0c9b838f72c99ca6385f96ca6 Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 20 Sep 2024 15:17:37 +0900 Subject: [PATCH 4/8] Solution: Design Add and Search Words Data Structure --- .../flynn.cpp | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 design-add-and-search-words-data-structure/flynn.cpp diff --git a/design-add-and-search-words-data-structure/flynn.cpp b/design-add-and-search-words-data-structure/flynn.cpp new file mode 100644 index 000000000..404239c10 --- /dev/null +++ b/design-add-and-search-words-data-structure/flynn.cpp @@ -0,0 +1,89 @@ +/** + * 풀이 + * - Trie 구조를 활용하여 풀이할 수 있습니다 + * - wildcard인 '.'에 대한 처리가 필요합니다 + * + * Big O + * - N: 주어지는 문자열 word의 길이 + * - M: 현재 WordDictionary에 저장되어 있는 TrieNode의 수 + * + * - void addWord(string word) + * - Time complexity: O(N) + * - Space complexity: O(N) + * - 최악의 경우 word의 모든 문자에 대해 새로운 TrieNode를 추가해야 합니다 + * + * - bool search(string word) + * - bool _search(string word, int idx, TrieNode* node) + * - Time complexity: best O(N), worst O(M) < O(26^N) + * - wildcard 사용 및 기존에 저장된 word의 상태에 따라 달라집니다 + * - Space complexity: O(N) + * - _search가 재귀적으로 호출되므로 재귀 호출 스택의 깊이만큼 추가적인 공간이 사용됩니다 + * - 재귀 호출 스택의 깊이는 현재 찾는 word의 길이에 선형적으로 비례합니다 + */ + +class TrieNode { +public: + array links; + bool word; + + TrieNode(): word(false) { + links.fill(nullptr); + } +}; + +class WordDictionary { +public: + WordDictionary(): root(new TrieNode()) {} + + void addWord(string word) { + TrieNode* current = root; + + for (char c : word) { + if (current->links[c - 'a'] == nullptr) { + current->links[c - 'a'] = new TrieNode(); + } + current = current->links[c - 'a']; + } + + current->word = true; + } + + bool search(string word) { + return _search(word, 0, root); + } + +private: + TrieNode* root; + + bool _search(string word, int idx, TrieNode* node) { + if (word.size() == idx) return node->word; + + char c = word[idx]; + + if (c != '.') { + if (node->links[c - 'a'] == nullptr) return false; + + TrieNode* next_node = node->links[c - 'a']; + int next_idx = idx + 1; + + return _search(word, next_idx, next_node); + } else { + for (TrieNode* link : node->links) { + if (link != nullptr) { + TrieNode* next_node = link; + int next_idx = idx + 1; + + if (_search(word, next_idx, next_node)) return true; + } + } + return false; + } + } +}; + +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary* obj = new WordDictionary(); + * obj->addWord(word); + * bool param_2 = obj->search(word); + */ From 159b87f00cb5712f091c1d0c555ace5520c82b8a Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 20 Sep 2024 17:24:33 +0900 Subject: [PATCH 5/8] Solution: Longest Increasing Subsequence --- longest-increasing-subsequence/flynn.cpp | 113 +++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 longest-increasing-subsequence/flynn.cpp diff --git a/longest-increasing-subsequence/flynn.cpp b/longest-increasing-subsequence/flynn.cpp new file mode 100644 index 000000000..fd7c75a2e --- /dev/null +++ b/longest-increasing-subsequence/flynn.cpp @@ -0,0 +1,113 @@ +/** + * 풀이 + * - 아래와 같은 배열 memo를 이용하여 이중 반복문을 실행하는 풀이입니다 + * - memo[i]: nums[i]로 끝나는 subsequence 중에서 길이가 가장 긴 subsequence의 길이 + * + * Big O + * - N: 배열 nums의 길이 + * - Time complexity: O(N^2) + * - Space complexity: O(N) + */ + +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector memo; + memo.push_back(1); + + int res = 1; + for (int i = 1; i < nums.size(); ++i) { + int tmp = 1; + for (int j = 0; j < i; ++j) { + if (nums[j] < nums[i]) tmp = max(tmp, memo[j] + 1); + } + memo.push_back(tmp); + res = max(res, tmp); + } + + return res; + } +}; + +/** + * 풀이 + * - wikipedia의 pseudo code를 참고하였습니다 + * 달레님 블로그에 실린 풀이를 통해서 훨씬 간단하게 문제에서 요구하는 바를 구할 수 있으므로, 문제의 풀이만을 원하신다면 달레님 블로그를 추천드리고 + * 좀 더 general하고 확장성 있는 알고리즘에 대해 궁금하신 분들께서는 wiki도 읽어보시는 걸 추천드립니다 (이해하는 데에는 시간이 좀 걸렸습니다) + * + * 제가 읽고 이해한 결과 wiki 풀이와 달레님 풀이의 비교는 다음과 같습니다 + * + * 공통점: 문제에서 요구하는 바를 구할 수 있음 (LIS의 길이) + * 차이점: wiki 풀이는 문제에서 요구하는 바를 구하는 것에 비해 overkill입니다 (이 문제에서는 굳이 필요 없는 부분이 꽤 있음) + * 대신, wiki 풀이는 확장성이 좀 더 넓은 풀이입니다 (각 길이에 해당하는 increasing subsequence를 재구축할 수 있음) + * + * 관심 있으신 분들께서는 한 번 읽어보시는 것을 추천합니다 :) + * - 참고: https://en.wikipedia.org/wiki/Longest_increasing_subsequence#Efficient_algorithms + * + * - memo[l]: 현재 nums[i]를 탐색중이라고 할 때, l <= i인 l에 대하여 + * 길이가 l인 increasing subsequence들의 마지막 원소 중에서 + * 가장 최소값인 nums[k]의 인덱스 k + * nums를 순차적으로 탐색해 나감에 따라 계속 갱신되며 정렬된 상태를 유지하게 됩니다 (if x < y then nums[memo[x]] < nums[memo[y]]) + * + * - predec[i]: nums[i]를 마지막 원소로 하는 가장 긴 increasing subsequence에서 nums[i] 바로 앞에 오는 원소의 index + * + * - nums를 순차적으로 탐색하며, 현재 탐색 중인 nums[i]를 마지막 원소로 삼는 가장 긴 Increasing subsequence를 찾습니다 + * 가장 긴 Increasing subsequence는 memo 배열에 대해 이분탐색을 실행하여 알아낼 수 있습니다 + * + * Big O + * - N: 배열 nums의 길이 + * + * - Time complexity: O(NlogN) + * - nums의 각 원소마다 memo에 대해 이분탐색을 실행하므로 N이 증가함에 따라 실행 시간은 N * logN 형태로 증가합니다 + * - Space complexity: O(N) + * - memo 배열의 크기는 N이 증가함에 따라 선형적으로 증가합니다 + */ + +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + + vector memo(n + 1, -1); + + // vector predec(n, -1); + // 각 길이에 맞는 increasing subsequence를 재구축할 때 쓰입니다 + + int max_len = 0; + for (int i = 0; i < nums.size(); ++i) { + int lo = 1; + int hi = max_len + 1; + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + if (nums[memo[mid]] < nums[i]) lo = mid + 1; + else hi = mid; + } + // 위 이분탐색을 마치면 lo == hi인데 + // lo (혹은 hi)가 의미하는 바는 `nums[i]가 마지막 원소인 increasing subsequence 중에 길이가 가장 긴 녀석의 길이` 입니다 + int curr_len = lo; + // 이해하기 쉽게끔 curr_len이라는 변수를 선언해줍니다 + + // predec[i] = memo[curr_len - 1]; + // nums[i]가 마지막으로 오는 가장 긴 increasing subsequence에서 nums[i]의 바로 전 원소의 index를 기록해줍니다 + // + memo[curr_len] = i; + + if (curr_len > max_len) { + // 만약 이전까지 찾았던 max_len보다 더 길이가 긴 increasing subsequence를 max_len + max_len = curr_len; + } + } + + return max_len; + + // 길이 L짜리 increasing subsequence 중 하나를 재구축하려면 아래처럼 하면 됩니다 + // [P...P[memo[L]], ..., P[P[memo[L]]], P[memo[L]] ,memo[L]] + + // vector s(L, -1); + // int k = memo[L]; + // for (int i = L - 1; i >= 0; --i) { + // s[i] = nums[k]; + // k = predec[k]; + // } + } +}; From 7ba9cdd2ce92b29ebc804466e2024f752f2b9d2f Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 20 Sep 2024 17:25:18 +0900 Subject: [PATCH 6/8] Comment: Longest Increasing Subsequence --- longest-increasing-subsequence/flynn.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/longest-increasing-subsequence/flynn.cpp b/longest-increasing-subsequence/flynn.cpp index fd7c75a2e..a8e578c58 100644 --- a/longest-increasing-subsequence/flynn.cpp +++ b/longest-increasing-subsequence/flynn.cpp @@ -61,6 +61,7 @@ class Solution { * - nums의 각 원소마다 memo에 대해 이분탐색을 실행하므로 N이 증가함에 따라 실행 시간은 N * logN 형태로 증가합니다 * - Space complexity: O(N) * - memo 배열의 크기는 N이 증가함에 따라 선형적으로 증가합니다 + * (- predec 배열의 크기 또한 N이 증가함에 따라 선형적으로 증가합니다) */ class Solution { From 853a332f12dbdb4b0c1371db54152e78957ecbe3 Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 20 Sep 2024 17:46:53 +0900 Subject: [PATCH 7/8] Solution: Spiral Matrix --- spiral-matrix/flynn.cpp | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 spiral-matrix/flynn.cpp diff --git a/spiral-matrix/flynn.cpp b/spiral-matrix/flynn.cpp new file mode 100644 index 000000000..5f506ddea --- /dev/null +++ b/spiral-matrix/flynn.cpp @@ -0,0 +1,58 @@ +/** + * 풀이 + * - 탐색 방향을 90도씩 회전해나가면서 주어진 2차원 배열 matrix를 탐색합니다 + * - 한계: 주어진 matrix를 변형하게 되며, 해당 변형을 피하기 위해서는 추가적인 공간 사용이 필요합니다 + * + * Big O + * - M: 주어진 matrix의 행의 개수 + * - N: 열의 개수 + * + * - Time complexity: O(MN) + * - Space complexity: O(1) + */ + +class Solution { +public: + pair rotate(pair dir) { + // 시계방향 90도 회전 + // 행렬곱으로 구해줄 수 있습니다 + // | 0 -1 | | dir.first | = | -dir.second | + // | 1 0 | | dir.second | | dir.first | + return {dir.second, -dir.first}; + } + + pair get_next(pair curr, pair dir) { + return {curr.first + dir.first, curr.second + dir.second}; + } + + vector spiralOrder(vector>& matrix) { + int m = matrix.size(); + int n = matrix[0].size(); + int cnt = m * n; + + pair curr = {0, 0}; + pair curr_dir = {0, 1}; + + vector res; + + while (cnt) { + res.push_back(matrix[curr.first][curr.second]); + + matrix[curr.first][curr.second] = 101; // constraint 밖의 값 101로 방문 여부를 표시합니다 + --cnt; + + pair next = get_next(curr, curr_dir); + + if (0 > next.first || next.first >= m + || 0 > next.second || next.second >= n + || matrix[next.first][next.second] == 101) { + curr_dir = rotate(curr_dir); + curr = get_next(curr, curr_dir); + } else { + curr = next; + } + } + + return res; + } +}; From 44ac5dac99f1cfdccc4eccdbd6425310ad955bd1 Mon Sep 17 00:00:00 2001 From: obzva Date: Fri, 20 Sep 2024 23:09:47 +0900 Subject: [PATCH 8/8] Refactor: Design Add and Search Words Data Structure --- .../flynn.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/design-add-and-search-words-data-structure/flynn.cpp b/design-add-and-search-words-data-structure/flynn.cpp index 404239c10..dd5d53744 100644 --- a/design-add-and-search-words-data-structure/flynn.cpp +++ b/design-add-and-search-words-data-structure/flynn.cpp @@ -56,25 +56,21 @@ class WordDictionary { TrieNode* root; bool _search(string word, int idx, TrieNode* node) { + if (node == nullptr) return false; + if (word.size() == idx) return node->word; char c = word[idx]; if (c != '.') { - if (node->links[c - 'a'] == nullptr) return false; - TrieNode* next_node = node->links[c - 'a']; int next_idx = idx + 1; return _search(word, next_idx, next_node); } else { - for (TrieNode* link : node->links) { - if (link != nullptr) { - TrieNode* next_node = link; - int next_idx = idx + 1; - - if (_search(word, next_idx, next_node)) return true; - } + for (TrieNode* next_node : node->links) { + int next_idx = idx + 1; + if (_search(word, next_idx, next_node)) return true; } return false; }