From 5460104c4d77604804cd4b1bb6aab1cd3b866266 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Tue, 14 Jan 2025 23:38:25 +0900 Subject: [PATCH 1/8] Valid Parentheses --- valid-parentheses/forest000014.java | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 valid-parentheses/forest000014.java diff --git a/valid-parentheses/forest000014.java b/valid-parentheses/forest000014.java new file mode 100644 index 000000000..cee295624 --- /dev/null +++ b/valid-parentheses/forest000014.java @@ -0,0 +1,33 @@ +/* +Time Complexity: O(n) +Space Complexity: O(n) + +유효한 문자열이라면, 인접한 열고 닫는 괄호 쌍을 하나씩 캔슬시켰을 때, 빈 문자열만 남게 된다. +매치되는 쌍이 서로 떨어져있을 수 있기 때문에, 그 안의 유효한 쌍들을 미리 모두 캔슬시킨 뒤에 판단해야 매칭 여부를 쉽게 판단할 수 있는데, 이 과정을 스택을 이용해 구현할 수 있다. +*/ +class Solution { + public boolean isValid(String s) { + Stack st = new Stack<>(); + + for (char ch : s.toCharArray()) { + if (ch == '(' || ch == '{' || ch == '[') { + st.push(ch); + } else { + if (st.empty()) + return false; + if (ch == ')') { + if (st.peek() != '(') + return false; + } else if (ch == '}') { + if (st.peek() != '{') + return false; + } else { + if (st.peek() != '[') + return false; + } + st.pop(); + } + } + return st.empty(); + } +} From 40aff0f77d3427658fe2c3a6f5d5124b1d651e50 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Tue, 14 Jan 2025 23:49:05 +0900 Subject: [PATCH 2/8] Container With Most Water --- container-with-most-water/forest000014.java | 115 ++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 container-with-most-water/forest000014.java diff --git a/container-with-most-water/forest000014.java b/container-with-most-water/forest000014.java new file mode 100644 index 000000000..046c3e6e2 --- /dev/null +++ b/container-with-most-water/forest000014.java @@ -0,0 +1,115 @@ +/* +solution 1. brute force +Time Complexity: O(n^2) +Space Complexity: O(1) +모든 선분 i에 대해, 가장 많은 물을 담을 수 있는 반대쪽 선분 j를 찾는다. + + +solution 2. PQ +Time Complexity: O(nlogn) +- 정렬 : O(nlogn) +- i번째 선분에 대해 (자신보다 크거나 같은) 가장 멀리있는 선분 탐색(O(logn)) * n = O(nlogn) +Space Complexity: O(n) + +(사고의 흐름을 적기 위해 편의상 반말로 적었습니다... ^^) +brute force 에서 불필요한 탐색을 줄여보자. +일단 어떤 선분 i가 주어졌다고 가정하자. i를 한쪽 벽으로 해서 가장 많은 물을 담는 방법을 찾으려면, 반드시 나머지 모든 선분을 탐색해야만 할까? + +(1) 자신보다 작은 선분은 탐색하지 않는다. + +자신보다 큰 선분만을 탐색해도 충분하다. +왜냐하면, 설령 자신보다 더 작은 선분 중에 정답이 있었다고 하더라도, 그 선분을 기준으로 탐색할 때 정답에 해당하는 쌍을 확인하게 될 것이기 때문이다. + +(2) 자신보다 크거나 같은 선분만을 탐색 대상으로 삼는다면, 가장 멀리있는 선분만 확인하면 된다. + +탐색 대상이 자신보다 크거나 같은 선분들이라면, 어차피 담을 수 있는 물의 높이는 자신의 높이로 일정하다. +따라서, 멀리있는 선분일수록 더 많은 물을 담게 된다. +즉, "자신보다 크거나 같으면서", "가장 멀리 있는(오른쪽이든 왼쪽이든)" 선분만을 후보로 삼아 확인하면 충분하다. +(그 외의 나머지, 즉 자신보다 크거나 같으면서, 가장 멀리있지 않은 나머지 선분들은, 자연스럽게 탐색하지 않을 수 있다.) + +정리하자면, 주어진 선분 i에 대해서, 단 2번의 탐색만 하면 충분하다. + +(3) 내림차순 정렬과 2개의 PQ를 활용하면, 위 탐색을 O(logn) 시간에 수행할 수 있다. +선분들을 내림차순으로 정렬해놓고, 하나씩 순회함 (say, i번째 선분) +PQ는 각각 x 좌표 기준 max heap(가장 오른쪽의 선분을 찾기 위함), x 좌표 기준 min heap(가장 왼쪽의 선분을 찾기 위함)을 사용한다. +자신보다 크거나 같으면서 가장 오른쪽으로 멀리 있는 선분의 위치를 찾아서(= PQ(max heap)의 root), 최대 물의 양을 계산한다. +자신보다 크거나 같으면서 가장 왼쪽으로 멀리 있는 선분의 위치를 찾아서(= PQ(min heap)의 root), 최대 물의 양을 계산한다. +i번째 선분을 PQ 2개에 각각 넣는다. + + +solution 3. two pointers +(AlgoDale 풀이를 참고함) + +Time Complexity: O(n) +Space Complexity: O(1) + +2 포인터를 활용하면, PQ도 없이 시간 복잡도를 O(n)으로 줄일 수 있었다. +단순히 "작은 쪽을 줄이기보다는, 큰 쪽을 줄이는 게 유리하겠지" 정도의 greedy한 논리는 충분하지 않은 것 같고, 더 명확한 근거가 있을 것 같은데 시간 관계상 고민해보지는 못했다. + +To-Do : 풀이가 대강 이해는 되었지만, 이게 왜 되는지, 엄밀하게 이해하기 위해 PQ를 사용했던 논리를 좀 더 발전시켜볼 필요가 있다. + +*/ +class Solution { + public int maxArea(int[] height) { + int i = 0, j = height.length - 1; + + int ans = 0; + while (i < j) { + int area = (j - i) * Math.min(height[i], height[j]); + if (area > ans) { + ans = area; + } + + if (height[i] <= height[j]) { + i++; + } else { + j--; + } + } + + return ans; + } + + ////// 아래는 solution 2 + private static class Tuple { + private int x; + private int h; + + Tuple(int x, int h) { + this.x = x; + this.h = h; + } + } + + public int maxArea2(int[] height) { + List tuples = IntStream.range(0, height.length) + .mapToObj(i -> new Tuple(i, height[i])) + .collect(Collectors.toList()); + Collections.sort(tuples, (a, b) -> b.h - a.h); + + PriorityQueue minPq = new PriorityQueue<>((a, b) -> a.x - b.x); + PriorityQueue maxPq = new PriorityQueue<>((a, b) -> b.x - a.x); + + int ans = 0; + for (int i = 0; i < height.length; i++) { + minPq.add(tuples.get(i)); + maxPq.add(tuples.get(i)); + + Tuple curr = tuples.get(i); + + Tuple left = minPq.peek(); + int leftArea = (curr.x - left.x) * curr.h; + if (leftArea > ans) { + ans = leftArea; + } + + Tuple right = maxPq.peek(); + int rightArea = (right.x - curr.x) * curr.h; + if (rightArea > ans) { + ans = rightArea; + } + } + + return ans; + } +} From daa753fb08bdd3f21ac7fc01d76805da53c5da08 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Tue, 14 Jan 2025 23:58:38 +0900 Subject: [PATCH 3/8] Design Add and Search Words Data Structure --- .../forest000014.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 design-add-and-search-words-data-structure/forest000014.java diff --git a/design-add-and-search-words-data-structure/forest000014.java b/design-add-and-search-words-data-structure/forest000014.java new file mode 100644 index 000000000..9f39800af --- /dev/null +++ b/design-add-and-search-words-data-structure/forest000014.java @@ -0,0 +1,80 @@ +/* +Time Complexity: +- add: O(w) +- search: O(26^2 * w) = O(w) +Space Complexity: O(w) + +Trie를 활용하되, '.'의 탐색이 필요한 경우에는 for문을 사용한다. + +*/ +class WordDictionary { + class Node { + public char ch; + public boolean ends; + public Map children; + + Node() { + this.children = new HashMap<>(); + } + + Node(char ch) { + this.ch = ch; + this.children = new HashMap<>(); + } + } + + Node root; + + public WordDictionary() { + this.root = new Node(); + } + + public void addWord(String word) { + Node curr = this.root; + + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + if (!curr.children.containsKey(ch)) { + curr.children.put(ch, new Node(ch)); + } + + if (i == word.length() - 1) { + curr.children.get(ch).ends = true; + } else { + curr = curr.children.get(ch); + } + } + } + + public boolean search(String word) { + return searchChar(word, 0, this.root); + } + + private boolean searchChar(String word, int idx, Node curr) { + if (curr == null) { + return false; + } else if (idx == word.length()) { + return curr.ends; + } + + char ch = word.charAt(idx); + + if (ch == '.') { + for (Character key : curr.children.keySet()) { + if (searchChar(word, idx + 1, curr.children.get(key))) { + return true; + } + } + return false; + } else { + return searchChar(word, idx + 1, curr.children.get(ch)); + } + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary obj = new WordDictionary(); + * obj.addWord(word); + * boolean param_2 = obj.search(word); + */ From a59d354f2a08217a1e7564178fc162b1c3527c5f Mon Sep 17 00:00:00 2001 From: forest000014 Date: Wed, 15 Jan 2025 00:01:05 +0900 Subject: [PATCH 4/8] Spiral Matrix --- spiral-matrix/forest000014.java | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 spiral-matrix/forest000014.java diff --git a/spiral-matrix/forest000014.java b/spiral-matrix/forest000014.java new file mode 100644 index 000000000..f21494639 --- /dev/null +++ b/spiral-matrix/forest000014.java @@ -0,0 +1,35 @@ +/* +Time Complexity: O(m * n) +Space Complexity: O(1) + +현재 위치를 r, c라는 변수를 사용해서 나타내고, 이 r, c를 직접 제어하는 방식으로 행렬을 순회한다. +*/ +class Solution { + public List spiralOrder(int[][] matrix) { + List ans = new ArrayList<>(); + + int m = matrix.length, n = matrix[0].length; + int r = 0, c = 0; + int[] dr = {0, 1, 0, -1}; + int[] dc = {1, 0, -1, 0}; + int d = 0; + final int VISITED = -999; + + while (ans.size() < m * n) { + ans.add(matrix[r][c]); + matrix[r][c] = VISITED; + + int nr = r + dr[d]; + int nc = c + dc[d]; + if (nr < 0 || nr >= m || nc < 0 || nc >= n || matrix[nr][nc] == VISITED) { + d = (d + 1) % 4; + nr = r + dr[d]; + nc = c + dc[d]; + } + r = nr; + c = nc; + } + + return ans; + } +} From 7ae434478fd5eb5101f26a4dbe112b4b570df7b7 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Wed, 15 Jan 2025 00:29:19 +0900 Subject: [PATCH 5/8] =?UTF-8?q?Container=20With=20Most=20Water=20-=20?= =?UTF-8?q?=ED=92=80=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- container-with-most-water/forest000014.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/container-with-most-water/forest000014.java b/container-with-most-water/forest000014.java index 046c3e6e2..232d3c2cd 100644 --- a/container-with-most-water/forest000014.java +++ b/container-with-most-water/forest000014.java @@ -27,14 +27,14 @@ 즉, "자신보다 크거나 같으면서", "가장 멀리 있는(오른쪽이든 왼쪽이든)" 선분만을 후보로 삼아 확인하면 충분하다. (그 외의 나머지, 즉 자신보다 크거나 같으면서, 가장 멀리있지 않은 나머지 선분들은, 자연스럽게 탐색하지 않을 수 있다.) -정리하자면, 주어진 선분 i에 대해서, 단 2번의 탐색만 하면 충분하다. +정리하자면, 주어진 선분 i에 대해서, 단 2번(오른쪽, 왼쪽)의 탐색만 하면 충분하다. -(3) 내림차순 정렬과 2개의 PQ를 활용하면, 위 탐색을 O(logn) 시간에 수행할 수 있다. -선분들을 내림차순으로 정렬해놓고, 하나씩 순회함 (say, i번째 선분) +(3) 내림차순 정렬과 2개의 PQ를 아래처럼 활용하면, 위 탐색을 O(logn) 시간에 수행할 수 있다. PQ는 각각 x 좌표 기준 max heap(가장 오른쪽의 선분을 찾기 위함), x 좌표 기준 min heap(가장 왼쪽의 선분을 찾기 위함)을 사용한다. -자신보다 크거나 같으면서 가장 오른쪽으로 멀리 있는 선분의 위치를 찾아서(= PQ(max heap)의 root), 최대 물의 양을 계산한다. -자신보다 크거나 같으면서 가장 왼쪽으로 멀리 있는 선분의 위치를 찾아서(= PQ(min heap)의 root), 최대 물의 양을 계산한다. -i번째 선분을 PQ 2개에 각각 넣는다. +선분들을 길이 내림차순으로 정렬해놓고, 하나씩 순회하면서 (say, i번째 선분), 아래 과정을 반복한다. +- 자신보다 크거나 같으면서 가장 오른쪽으로 멀리 있는 선분의 위치를 찾아서(= 현재 PQ(max heap)의 root), 최대 물의 양을 계산한다. +- 자신보다 크거나 같으면서 가장 왼쪽으로 멀리 있는 선분의 위치를 찾아서(= 현재 PQ(min heap)의 root), 최대 물의 양을 계산한다. +- i번째 선분을 PQ 2개에 각각 넣는다. solution 3. two pointers @@ -44,7 +44,7 @@ Space Complexity: O(1) 2 포인터를 활용하면, PQ도 없이 시간 복잡도를 O(n)으로 줄일 수 있었다. -단순히 "작은 쪽을 줄이기보다는, 큰 쪽을 줄이는 게 유리하겠지" 정도의 greedy한 논리는 충분하지 않은 것 같고, 더 명확한 근거가 있을 것 같은데 시간 관계상 고민해보지는 못했다. +단순히 "큰 쪽을 줄이기보다는, 작은 쪽을 줄이는 게 유리하겠지" 정도의 greedy한 논리는 충분하지 않은 것 같고, 더 명확한 근거가 있을 것 같은데 시간 관계상 고민해보지는 못했다. To-Do : 풀이가 대강 이해는 되었지만, 이게 왜 되는지, 엄밀하게 이해하기 위해 PQ를 사용했던 논리를 좀 더 발전시켜볼 필요가 있다. From f848beb03dbc65f7cc314d44be0a8808c376a014 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Wed, 15 Jan 2025 00:32:08 +0900 Subject: [PATCH 6/8] =?UTF-8?q?Design=20Add=20and=20Search=20Words=20Data?= =?UTF-8?q?=20Structure=20-=20addWord()=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../forest000014.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/design-add-and-search-words-data-structure/forest000014.java b/design-add-and-search-words-data-structure/forest000014.java index 9f39800af..3d2ca1fa4 100644 --- a/design-add-and-search-words-data-structure/forest000014.java +++ b/design-add-and-search-words-data-structure/forest000014.java @@ -38,12 +38,10 @@ public void addWord(String word) { curr.children.put(ch, new Node(ch)); } - if (i == word.length() - 1) { - curr.children.get(ch).ends = true; - } else { - curr = curr.children.get(ch); - } + curr = curr.children.get(ch); } + + curr.ends = true; } public boolean search(String word) { From 4d49d49688a833982dcc7911c20f7bd9a71d76ff Mon Sep 17 00:00:00 2001 From: forest000014 Date: Wed, 15 Jan 2025 00:36:12 +0900 Subject: [PATCH 7/8] =?UTF-8?q?Valid=20Parentheses=20-=20s=EC=9D=98=20?= =?UTF-8?q?=EA=B8=B8=EC=9D=B4=EA=B0=80=20=ED=99=80=EC=88=98=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20early=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- valid-parentheses/forest000014.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/valid-parentheses/forest000014.java b/valid-parentheses/forest000014.java index cee295624..f0118d8c1 100644 --- a/valid-parentheses/forest000014.java +++ b/valid-parentheses/forest000014.java @@ -9,6 +9,10 @@ class Solution { public boolean isValid(String s) { Stack st = new Stack<>(); + if (s.length() % 2 == 1) { + return false; + } + for (char ch : s.toCharArray()) { if (ch == '(' || ch == '{' || ch == '[') { st.push(ch); From 8d3745dd4cd6d1af9be2edb0dd36453b9652d378 Mon Sep 17 00:00:00 2001 From: forest000014 Date: Wed, 15 Jan 2025 22:36:14 +0900 Subject: [PATCH 8/8] Longest Increasing Subsequence --- .../forest000014.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 longest-increasing-subsequence/forest000014.java diff --git a/longest-increasing-subsequence/forest000014.java b/longest-increasing-subsequence/forest000014.java new file mode 100644 index 000000000..9780ad5e5 --- /dev/null +++ b/longest-increasing-subsequence/forest000014.java @@ -0,0 +1,85 @@ +/* +Time Complexity: O(nlogn) +Space Complexity: O(nlogn) +*/ +class Solution { + + int[] tree; + int L = 1; + + public int lengthOfLIS(int[] nums) { + init(nums); + ArrayList tuples = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + tuples.add(new Tuple(i, nums[i])); + } + + Collections.sort(tuples, (a, b) -> { + if (a.val == b.val) { + return b.ref - a.ref; // 2순위 : ref 내림차순 + } else { + return a.val - b.val; // 1순위 : val 오름차순 + } + }); + + int ans = 0; + for (int i = 0; i < nums.length; i++) { + int curr = getMax(0, tuples.get(i).ref - 1) + 1; + ans = Math.max(ans, curr); + insert(tuples.get(i).ref, curr); + } + + return ans; + } + + public class Tuple { + public int ref; + public int val; + + public Tuple(int ref, int val) { + this.ref = ref; + this.val = val; + } + } + + public void init(int[] nums) { + while (L < nums.length) { + L *= 2; + } + + tree = new int[L * 2]; + + for (int i = 1; i < L * 2; i++) { + tree[i] = 0; + } + } + + public void insert(int idx, int v) { + int i = idx + L; + tree[i] = v; + i /= 2; + while (i >= 1) { + tree[i] = Math.max(tree[i * 2], tree[i * 2 + 1]); + i /= 2; + } + } + + public int getMax(int l, int r) { + int i = l + L; + int j = r + L; + int ret = 0; + while (i <= j) { + if (i % 2 == 1) { + ret = Math.max(ret, tree[i++]); + } + if (j % 2 == 0) { + ret = Math.max(ret, tree[j--]); + } + i /= 2; + j /= 2; + } + + return ret; + } +}