From 9715128793fb2d186cc146b3f6de2b71f673283d Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 00:51:57 +0900 Subject: [PATCH 1/6] week9 mission - Design Add and Search Words Data Structure --- .../dev-jonghoonpark.md | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 design-add-and-search-words-data-structure/dev-jonghoonpark.md diff --git a/design-add-and-search-words-data-structure/dev-jonghoonpark.md b/design-add-and-search-words-data-structure/dev-jonghoonpark.md new file mode 100644 index 000000000..b54e9e27a --- /dev/null +++ b/design-add-and-search-words-data-structure/dev-jonghoonpark.md @@ -0,0 +1,198 @@ +- https://leetcode.com/problems/design-add-and-search-words-data-structure +- https://algorithm.jonghoonpark.com/2024/06/30/leetcode-211 + +## brute force + +아슬아슬하게 통과한다. + +```java +class WordDictionary { + + Set wordSet; + + public WordDictionary() { + wordSet = new HashSet<>(); + } + + public void addWord(String word) { + wordSet.add(word); + } + + public boolean search(String word) { + Deque queue = new ArrayDeque<>(); + queue.push(word); + + while (queue.getFirst().contains(".")) { + String _word = queue.removeFirst(); + String pre = _word.substring(0, _word.indexOf(".")); + String post = _word.substring(_word.indexOf(".") + 1); + + for (char c = 'a'; c <= 'z'; c++) { + queue.addLast(pre + c + post); + } + } + + while (!queue.isEmpty()) { + String _word = queue.removeFirst(); + if (wordSet.contains(_word)) { + return true; + } + } + + return false; + } +} +``` + +### TC, SC + +- `.` 이 없을 때 + - 시간 복잡도 : `O(1)` + - 공간 복잡도 : `O(1)` +- `.` 이 있을 때 + - 시간 복잡도 : `O(26^N)` + - 공간 복잡도 : `O(26^N)` + - 여기서 N은 `.` 의 수 + +## trie + +[208. Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) 문제 에서 사용한 Trie 재사용. + +```java +class WordDictionary { + + Trie trie; // Trie 구현은 생략 + + public WordDictionary() { + trie = new Trie(); + } + + public void addWord(String word) { + trie.insert(word); + } + + public boolean search(String word) { + if (word.contains(".")) { + String pre = word.substring(0, word.indexOf(".")); + String post = word.substring(word.indexOf(".") + 1); + + if (trie.startsWith(pre)) { + for (char c = 'a'; c <= 'z'; c++) { + if (search(pre + c + post)) { + return true; + } + } + } + + return false; + } + + return trie.search(word); + } + + +} +``` + +### TC, SC + +입력된 문자열의 길이를 `L`, `.` 의 수를 `N` 이라고 하였을 때 + +addWord 메소드의 경우 시간 복잡도는 `O(L)`이다. +search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 `O(L * 26 ^ N)`이다. + +공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. `insert된 문자열 길이의 평균` 를 `avg(L)`이라고 하였을 때 `O(avg(L) * 26)`이다. 26은 계수이기 때문에 생략할 수 있다. + +## trie 개선 + +이 문제에 적합하도록 search를 수정하였다. + +```java +class WordDictionary { + + Trie trie; + + public WordDictionary() { + trie = new Trie(); + } + + public void addWord(String word) { + trie.insert(word); + } + + public boolean search(String word) { + return trie.search(word); + } + + +} + +class Trie { + + Node root = new Node(); + + public Trie() { + + } + + public void insert(String word) { + Node currentNode = root; + for (char c : word.toCharArray()) { + if (currentNode.nodes[c - 97] == null) { + currentNode.nodes[c - 97] = new Node(); + } + currentNode = currentNode.nodes[c - 97]; + } + currentNode.val = word; + } + + public boolean search(String word) { + return search(root, word, 0); + } + + public boolean search(Node node, String word, int index) { + if (node == null) { + return false; + } + + if (node.val != null && node.val.length() == word.length()) { + return true; + } + + if (index >= word.length()) { + return false; + } + + char c = word.charAt(index); + + if (c == '.') { + for (char _c = 'a'; _c <= 'z'; _c++) { + if (search(node.nodes[_c - 97], word, index + 1)) { + return true; + } + } + return false; + } else if (node.nodes[c - 97] == null) { + return false; + } + + return search(node.nodes[c - 97], word, index + 1); + } +} + +class Node { + String val; + Node[] nodes = new Node[26]; +} +``` + +### TC, SC + +입력된 문자열의 길이를 `L`, `.` 의 수를 `N` 이라고 하였을 때 + +addWord 메소드의 경우 시간 복잡도는 `O(L)`이다. +search 메소드의 경우 입력된 문자열의 길이를 n 이라 하였을 때 시간 복잡도는 `O(L * 26 ^ N)`이다. +개선 전과 비교해봤을 때 표기상으로는 차이가 없으나, 불필요한 과정을 제거하게되어서 시간이 매우 단축된다. +(`trie.startsWith(pre)`이 사라졌고, search의 호출 횟수가 줄어듬.) + +공간 복잡도는 Trie 구조를 만드는데 사용된 공간이다. `insert된 문자열 길이의 평균` 를 `avg(L)`이라고 하였을 때 `O(avg(L) * 26)`이다. 26은 계수이기 때문에 생략할 수 있다. From a36214cd45ebef2a490a5c4d74df12b0082d6110 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 01:33:46 +0900 Subject: [PATCH 2/6] week9 mission - Clone Graph --- clone-graph/dev-jonghoonpark.md | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 clone-graph/dev-jonghoonpark.md diff --git a/clone-graph/dev-jonghoonpark.md b/clone-graph/dev-jonghoonpark.md new file mode 100644 index 000000000..dd9f1da4d --- /dev/null +++ b/clone-graph/dev-jonghoonpark.md @@ -0,0 +1,35 @@ +- 문제: https://leetcode.com/problems/clone-graph/ +- 풀이: https://algorithm.jonghoonpark.com/2024/02/13/leetcode-133 + +```java +class Solution { + public Node cloneGraph(Node node) { + return cloneGraph(new HashMap<>(), node); + } + + private Node cloneGraph(Map map, Node node) { + if(node == null) { + return null; + } + + if (map.containsKey(node.val)) { + return map.get(node.val); + } + + Node copy = new Node(node.val); + map.put(node.val, copy); + + for (int i = 0; i < node.neighbors.size(); i++) { + Node neighborNode = node.neighbors.get(i); + copy.neighbors.add(map.getOrDefault(neighborNode.val, cloneGraph(map, node.neighbors.get(i)))); + } + + return copy; + } +} +``` + +### TC, SC + +node(vertex)의 수를 `V`, edge의 수를 `E` 라고 하였을 때 각 노드 마다 edge의 수만큼 반복을 해야한다. +시간 복잡도는 `O(V + E)` 이다. 공간 복잡도는 `O(V)`이다. From b2c1388512afa207c108121c17d6b779d8b12616 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 01:42:17 +0900 Subject: [PATCH 3/6] week9 mission - Number of Islands --- number-of-islands/dev-jonghoonpark.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 number-of-islands/dev-jonghoonpark.md diff --git a/number-of-islands/dev-jonghoonpark.md b/number-of-islands/dev-jonghoonpark.md new file mode 100644 index 000000000..b4517ff13 --- /dev/null +++ b/number-of-islands/dev-jonghoonpark.md @@ -0,0 +1,40 @@ +- 문제: https://leetcode.com/problems/number-of-islands/ +- 풀이: https://algorithm.jonghoonpark.com/2024/03/31/leetcode-200 + +```java +class Solution { + public int numIslands(char[][] grid) { + int w = grid.length; + int h = grid[0].length; + + int count = 0; + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + if (grid[i][j] == '1') { + dfs(grid, i,j); + count++; + } + } + } + return count; + } + + public void dfs(char[][] grid, int i, int j) { + if(i < 0 || i >= w || j < 0 || j >= h || grid[i][j] == '0') { + return; + } + + grid[i][j] = '0'; + + dfs(grid, i-1, j); + dfs(grid, i, j-1); + dfs(grid, i+1, j); + dfs(grid, i, j+1); + } +} +``` + +## TC, SC + +코드에 정의한 대로 grid의 길이를 `w`, grid[0]의 길이를 `h`로 정의했을 때, +이 코드의 시간 복잡도는 O(w \* h), 공간 복잡도는 O(w \* h) 이다. From c93bf8270f72ec43be3a394e72a7ece586780ec5 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 03:53:25 +0900 Subject: [PATCH 4/6] week9 mission - Pacific Atlantic Water Flow --- .../dev-jonghoonpark.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 pacific-atlantic-water-flow/dev-jonghoonpark.md diff --git a/pacific-atlantic-water-flow/dev-jonghoonpark.md b/pacific-atlantic-water-flow/dev-jonghoonpark.md new file mode 100644 index 000000000..76d8a4e7f --- /dev/null +++ b/pacific-atlantic-water-flow/dev-jonghoonpark.md @@ -0,0 +1,60 @@ +- 문제: https://leetcode.com/problems/pacific-atlantic-water-flow/ +- 풀이: https://algorithm.jonghoonpark.com/2024/03/03/leetcode-417 + +```java +public class Solution { + public List> pacificAtlantic(int[][] heights) { + List> result = new ArrayList<>(); + + for (int i = 0; i < heights.length; i++) { + for (int j = 0; j < heights[0].length; j++) { + System.out.printf("\n\ninit Flow : %d, %d\n", i, j); + Flow flow = new Flow(); + dfs(flow, heights, Integer.MAX_VALUE, i, j); + if (flow.flowToAtlanticOcean && flow.flowToPacificOcean) { + result.add(List.of(i, j)); + } + } + } + + return result; + } + + private void dfs(Flow flow, int[][] heights, int prev, int i, int j) { + if (i == -1 || j == -1) { + flow.flowToPacificOcean = true; + return; + } + + if (i == heights.length || j == heights[0].length) { + flow.flowToAtlanticOcean = true; + return; + } + + if (heights[i][j] == -1 || heights[i][j] > prev || flow.flowToAtlanticOcean && flow.flowToPacificOcean) { + return; + } + + int currentHeight = heights[i][j]; + heights[i][j] = -1; + + dfs(flow, heights, currentHeight, i + 1, j); + dfs(flow, heights, currentHeight, i - 1, j); + dfs(flow, heights, currentHeight, i, j + 1); + dfs(flow, heights, currentHeight, i, j - 1); + + heights[i][j] = currentHeight; + } +} + +class Flow { + boolean flowToPacificOcean; + boolean flowToAtlanticOcean; +} +``` + +## TC, SC + +heights의 길이를 `w`, heights[0]의 길이를 `h`로 정의했을 때, +이 코드의 시간 복잡도는 `O((w \* h)^2)`, 공간 복잡도는 `O(w \* h)` 이다. +시간 복잡도가 저렇게 나온 이유는 모든 좌표에 대해서 dfs를 진행하기 때문이다. From a99c9889b515860d29d47040c9966bc5b510897c Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 04:55:12 +0900 Subject: [PATCH 5/6] week9 mission - Course Schedule --- course-schedule/dev-jonghoonpark.md | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 course-schedule/dev-jonghoonpark.md diff --git a/course-schedule/dev-jonghoonpark.md b/course-schedule/dev-jonghoonpark.md new file mode 100644 index 000000000..495b16575 --- /dev/null +++ b/course-schedule/dev-jonghoonpark.md @@ -0,0 +1,64 @@ +- 문제: https://leetcode.com/problems/course-schedule/ +- 풀이: https://algorithm.jonghoonpark.com/2024/03/01/leetcode-207 + +```java +public class Solution { + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (prerequisites.length == 0) { + return true; + } + + Map vertexMap = new HashMap<>(); + + for (int[] prerequisite : prerequisites) { + vertexMap.putIfAbsent(prerequisite[0], new Node(prerequisite[0])); + vertexMap.putIfAbsent(prerequisite[1], new Node(prerequisite[1])); + + Node vertex1 = vertexMap.get(prerequisite[0]); + Node vertex2 = vertexMap.get(prerequisite[1]); + + vertex1.edges.add(vertex2); + vertex2.reversedEdges.add(vertex1); + } + + Deque deque = new LinkedList<>(); + + int[] degrees = new int[numCourses]; + for (int i = 0; i < degrees.length; i++) { + Node vertex = vertexMap.get(i); + if (vertex != null) { + degrees[i] = vertex.edges.size(); + if (degrees[i] == 0) { + deque.addLast(i); + } + } + } + + while(!deque.isEmpty()) { + int vertexId = deque.removeFirst(); + Node vertex = vertexMap.get(vertexId); + for (Node node : vertex.reversedEdges) { + degrees[node.id]--; + if (degrees[node.id] == 0) { + deque.addLast(node.id); + } + } + vertexMap.remove(vertexId); + } + + return vertexMap.isEmpty(); + } +} + +class Node { + int id; + List edges; + List reversedEdges; + + public Node(int id) { + this.id = id; + edges = new ArrayList<>(); + reversedEdges = new ArrayList<>(); + } +} +``` From fad9b25d621815abc801d1b2a4ba20d7548a09ad Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sun, 30 Jun 2024 04:56:49 +0900 Subject: [PATCH 6/6] fix indent --- course-schedule/dev-jonghoonpark.md | 20 ++--- .../dev-jonghoonpark.md | 88 +++++++++---------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/course-schedule/dev-jonghoonpark.md b/course-schedule/dev-jonghoonpark.md index 495b16575..436d89e73 100644 --- a/course-schedule/dev-jonghoonpark.md +++ b/course-schedule/dev-jonghoonpark.md @@ -3,12 +3,12 @@ ```java public class Solution { - public boolean canFinish(int numCourses, int[][] prerequisites) { + public boolean canFinish(int numCourses, int[][] prerequisites) { if (prerequisites.length == 0) { return true; } - Map vertexMap = new HashMap<>(); + Map vertexMap = new HashMap<>(); for (int[] prerequisite : prerequisites) { vertexMap.putIfAbsent(prerequisite[0], new Node(prerequisite[0])); @@ -23,7 +23,7 @@ public class Solution { Deque deque = new LinkedList<>(); - int[] degrees = new int[numCourses]; + int[] degrees = new int[numCourses]; for (int i = 0; i < degrees.length; i++) { Node vertex = vertexMap.get(i); if (vertex != null) { @@ -47,18 +47,18 @@ public class Solution { } return vertexMap.isEmpty(); - } + } } class Node { - int id; - List edges; + int id; + List edges; List reversedEdges; - public Node(int id) { - this.id = id; - edges = new ArrayList<>(); + public Node(int id) { + this.id = id; + edges = new ArrayList<>(); reversedEdges = new ArrayList<>(); - } + } } ``` diff --git a/pacific-atlantic-water-flow/dev-jonghoonpark.md b/pacific-atlantic-water-flow/dev-jonghoonpark.md index 76d8a4e7f..33bc4d443 100644 --- a/pacific-atlantic-water-flow/dev-jonghoonpark.md +++ b/pacific-atlantic-water-flow/dev-jonghoonpark.md @@ -3,53 +3,53 @@ ```java public class Solution { - public List> pacificAtlantic(int[][] heights) { - List> result = new ArrayList<>(); - - for (int i = 0; i < heights.length; i++) { - for (int j = 0; j < heights[0].length; j++) { - System.out.printf("\n\ninit Flow : %d, %d\n", i, j); - Flow flow = new Flow(); - dfs(flow, heights, Integer.MAX_VALUE, i, j); - if (flow.flowToAtlanticOcean && flow.flowToPacificOcean) { - result.add(List.of(i, j)); - } - } - } - - return result; - } - - private void dfs(Flow flow, int[][] heights, int prev, int i, int j) { - if (i == -1 || j == -1) { - flow.flowToPacificOcean = true; - return; - } - - if (i == heights.length || j == heights[0].length) { - flow.flowToAtlanticOcean = true; - return; - } - - if (heights[i][j] == -1 || heights[i][j] > prev || flow.flowToAtlanticOcean && flow.flowToPacificOcean) { - return; - } - - int currentHeight = heights[i][j]; - heights[i][j] = -1; - - dfs(flow, heights, currentHeight, i + 1, j); - dfs(flow, heights, currentHeight, i - 1, j); - dfs(flow, heights, currentHeight, i, j + 1); - dfs(flow, heights, currentHeight, i, j - 1); - - heights[i][j] = currentHeight; - } + public List> pacificAtlantic(int[][] heights) { + List> result = new ArrayList<>(); + + for (int i = 0; i < heights.length; i++) { + for (int j = 0; j < heights[0].length; j++) { + System.out.printf("\n\ninit Flow : %d, %d\n", i, j); + Flow flow = new Flow(); + dfs(flow, heights, Integer.MAX_VALUE, i, j); + if (flow.flowToAtlanticOcean && flow.flowToPacificOcean) { + result.add(List.of(i, j)); + } + } + } + + return result; + } + + private void dfs(Flow flow, int[][] heights, int prev, int i, int j) { + if (i == -1 || j == -1) { + flow.flowToPacificOcean = true; + return; + } + + if (i == heights.length || j == heights[0].length) { + flow.flowToAtlanticOcean = true; + return; + } + + if (heights[i][j] == -1 || heights[i][j] > prev || flow.flowToAtlanticOcean && flow.flowToPacificOcean) { + return; + } + + int currentHeight = heights[i][j]; + heights[i][j] = -1; + + dfs(flow, heights, currentHeight, i + 1, j); + dfs(flow, heights, currentHeight, i - 1, j); + dfs(flow, heights, currentHeight, i, j + 1); + dfs(flow, heights, currentHeight, i, j - 1); + + heights[i][j] = currentHeight; + } } class Flow { - boolean flowToPacificOcean; - boolean flowToAtlanticOcean; + boolean flowToPacificOcean; + boolean flowToAtlanticOcean; } ```