diff --git a/3sum/forest000014.java b/3sum/forest000014.java new file mode 100644 index 000000000..fc514a476 --- /dev/null +++ b/3sum/forest000014.java @@ -0,0 +1,63 @@ +/* +# Time Complexity: O(n^2) +# Space Complexity: O(1) +*/ + +class Solution { + public List> threeSum1(int[] nums) { // solution 1 + int n = nums.length; + Arrays.sort(nums); + Set> ans = new HashSet<>(); + + for (int i = 0; i < n - 2; i++) { + for (int j = i + 1; j < n - 1; j++) { + int target = -nums[i] - nums[j]; // nums[i], nums[j]와 더해서 합이 0이 되기 위해 nums[k]가 가져야하는 값 + int k = -1; + int l = j + 1; + int r = n - 1; + while (l <= r) { + int m = (r - l) / 2 + l; + if (nums[m] == target) { + k = m; + break; + } else if (nums[m] < target) { + l = m + 1; + } else { + r = m - 1; + } + } + if (k != -1) { // binary search에서 target을 찾은 경우 + ans.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[k]))); + } + } + } + + return new ArrayList<>(ans); + } + + public List> threeSum(int[] nums) { // solution 2 + int n = nums.length; + Arrays.sort(nums); + Set> ans = new HashSet<>(); + + for (int i = 0; i < n - 2; i++) { + int l = i + 1; + int r = n - 1; + int target = -nums[i]; + while (l < r) { + int sum = nums[l] + nums[r]; + if (sum == target) { + ans.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r]))); + l++; + r--; // 또 다른 (l, r) 조합이 있을 수 있으므로, loop를 계속 이어간다. + } else if (sum < target) { + l++; + } else { + r--; + } + } + } + + return new ArrayList<>(ans); + } +} diff --git a/binary-tree-maximum-path-sum/forest000014.java b/binary-tree-maximum-path-sum/forest000014.java new file mode 100644 index 000000000..2894cd14b --- /dev/null +++ b/binary-tree-maximum-path-sum/forest000014.java @@ -0,0 +1,50 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(n) + - 재귀 호출 내부에서 left, right 변수를 사용하고, 재귀 호출 최대 깊이는 n이므로 +# Solution +전체 문제를 각 subtree에 대한 문제로 쪼개어 생각할 수 있습니다. +임의의 노드 x에 대해, x의 왼쪽 자식을 x_l, x의 오른쪽 자식을 x_r, x의 값을 x.val이라고 정의하겠습니다. +x를 root로 하는 subtree에서 'x를 path의 한쪽 끝으로 하는 path sum 중 최대값'을 dp[x]라고 정의하겠습니다. +그러면 dp[x] = max(max(0, dp[x_l]) + x.val, max(0, dp[x_r]) + x.val) 로 구할 수 있습니다. (subtree의 dp 값이 음수인 경우는 버리면 되기 때문에.) +이제 root로부터 출발해서 DFS로 전체 노드를 순회하며 이 점화식을 적용하면, 전체 tree에 대해 dp값을 구할 수 있습니다. +단, 문제에서 원하는 답은 root를 반드시 path의 한쪽 끝으로 원하는 것은 아니고, 심지어 root가 path에 포함되지 않아도 되기 때문에, +어중간한(?) (= root를 path에 포함하지 않는) path도 고려할 필요가 있는데요. +이를 고려하기 위해, 각 재귀 함수 호출마다 max(0, dp[x_l]) + root.val + max(0, dp[x_r]) 값이 정답이 될 수 있는지 체크하는 과정이 필요합니다. +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + public int ans = -30_000_001; + public int maxPathSum(TreeNode root) { + maxInTree(root); + + return ans; + } + + public int maxInTree(TreeNode root) { + if (root == null) { + return 0; + } + + int left = Math.max(0, maxInTree(root.left)); + int right = Math.max(0, maxInTree(root.right)); + + ans = Math.max(ans, left + root.val + right); + + return root.val + Math.max(left, right); + } +} diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/forest000014.java b/construct-binary-tree-from-preorder-and-inorder-traversal/forest000014.java new file mode 100644 index 000000000..dc89ef6e9 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/forest000014.java @@ -0,0 +1,46 @@ +/** + 이 문제는 힌트의 도움을 받아서 풀었습니다. + - preorder의 첫 원소는 항상 root node임 + - inorder에서 root node의 왼쪽의 원소들은 root node의 왼쪽 subtree, 오른쪽 원소들은 오른쪽 subtree임 + - 왼쪽 subtree와 오른쪽 subtree는 각각 preorder에서 연속하게 있음. (root, 왼쪽 subtree, 오른쪽 subtree 순) + + 시간 복잡도 : O(n) + 공간 복잡도 : O(n^2) + (skewed tree의 경우, 최악의 공간 복잡도를 가짐) + */ +class Solution { + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length == 0) { + return null; + } + if (preorder.length == 1) { + return new TreeNode(preorder[0]); + } + int currIdx; + for (currIdx = 0; currIdx < inorder.length; currIdx++) { + if (inorder[currIdx] == preorder[0]) { + break; + } + } + + int[] lp = new int[currIdx]; + int[] li = new int[currIdx]; + int[] rp = new int[inorder.length - currIdx - 1]; + int[] ri = new int[inorder.length - currIdx - 1]; + for (int i = 0; i < currIdx; i++) { + lp[i] = preorder[i + 1]; + li[i] = inorder[i]; + } + for (int i = currIdx + 1; i < inorder.length; i++) { + rp[i - currIdx - 1] = preorder[i]; + ri[i - currIdx - 1] = inorder[i]; + } + + TreeNode lc = buildTree(lp, li); + TreeNode rc = buildTree(rp, ri); + + TreeNode curr = new TreeNode(preorder[0], lc, rc); + + return curr; + } +} diff --git a/decode-ways/forest000014.java b/decode-ways/forest000014.java new file mode 100644 index 000000000..b066fd714 --- /dev/null +++ b/decode-ways/forest000014.java @@ -0,0 +1,55 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(n) +*/ +class Solution { + + private boolean check12(char ch) { + return (ch == '1' || ch == '2'); + } + + private boolean check1(char ch) { + return ch == '1'; + } + + private boolean check2(char ch) { + return ch == '2'; + } + + private boolean check0(char ch) { + return ch == '0'; + } + + private boolean check6(char ch) { + return ch <= '6'; + } + + public int numDecodings(String s) { + int n = s.length(); + + if (n == 0) + return 0; + + int[] dp = new int[n + 1]; + + if (check0(s.charAt(0))) + return 0; + + dp[0] = 1; + dp[1] = 1; + if (n == 1) + return dp[1]; + + for (int i = 1; i < n; i++) { + if (check0(s.charAt(i)) && !check12(s.charAt(i - 1))) + return 0; + + if (!check0(s.charAt(i))) + dp[i + 1] = dp[i]; + + if (check1(s.charAt(i - 1)) || (check6(s.charAt(i)) && check2(s.charAt(i - 1)))) + dp[i + 1] += dp[i - 1]; + } + return dp[n]; + } +} diff --git a/graph-valid-tree/forest000014.java b/graph-valid-tree/forest000014.java new file mode 100644 index 000000000..d857edbe8 --- /dev/null +++ b/graph-valid-tree/forest000014.java @@ -0,0 +1,65 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(n + m) + - m은 edges.length + +# Solution +edges[0][0]에서 출발하여 인접한 모든 edge를 DFS로 순회한다. + - cycle이 있는 경우 (이미 방문한 적이 있는 node를 재방문) + - 순회를 마쳤는데 방문하지 않은 node가 있는 경우 +위 2경우는 invalid tree이고, 그렇지 않으면 valid tree이다. +*/ +class Solution { + public ArrayList> adj = new ArrayList<>(); + public boolean[] visited; + public boolean validTree(int n, int[][] edges) { + if (edges.length == 0) { + return n == 1; + } + + visited = new boolean[n]; + + for (int i = 0; i < n; i++) { + adj.add(new ArrayList()); + } + + for (int i = 0; i < edges.length; i++) { + int a = edges[i][0]; + int b = edges[i][1]; + adj.get(a).add(b); + adj.get(b).add(a); + } + + if (!dfs(-1, edges[0][0])) { + return false; + } + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + return false; + } + } + + return true; + } + + public boolean dfs(int prev, int curr) { + visited[curr] = true; + + for (Integer next : adj.get(curr)) { + if (next == prev) { + continue; + } + + if (visited[next]) { + return false; + } + + if (!dfs(curr, next)) { + return false; + } + } + + return true; + } +} diff --git a/jump-game/forest000014.java b/jump-game/forest000014.java new file mode 100644 index 000000000..d81a240fe --- /dev/null +++ b/jump-game/forest000014.java @@ -0,0 +1,24 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(n) +*/ + +class Solution { + public boolean canJump(int[] nums) { + int n = nums.length; + boolean[] dp = new boolean[n]; + + dp[0] = true; + + for (int i = 0; i < n; i++) { + if (!dp[i]) return false; + int j = Math.min(n - 1, i + nums[i]); + for (; j >= i + 1; j--) { + if (dp[j]) break; + dp[j] = true; + } + } + + return true; + } +} diff --git a/maximum-depth-of-binary-tree/forest000014.java b/maximum-depth-of-binary-tree/forest000014.java new file mode 100644 index 000000000..27831ef4d --- /dev/null +++ b/maximum-depth-of-binary-tree/forest000014.java @@ -0,0 +1,28 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(1) +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; + } +} diff --git a/merge-intervals/forest000014.java b/merge-intervals/forest000014.java new file mode 100644 index 000000000..eaf429b6e --- /dev/null +++ b/merge-intervals/forest000014.java @@ -0,0 +1,29 @@ +/* +# Time Complexity: O(nlogn) +# Space Complexity: O(n) +*/ + +class Solution { + public int[][] merge(int[][] intervals) { + int n = intervals.length; + + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + + ArrayList ans = new ArrayList<>(); + ans.add(new int[2]); + ans.get(0)[0] = intervals[0][0]; + ans.get(0)[1] = intervals[0][1]; + + for (int i = 1; i < n; i++) { + if (ans.get(ans.size() - 1)[1] < intervals[i][0]) { + ans.add(new int[2]); + ans.get(ans.size() - 1)[0] = intervals[i][0]; + ans.get(ans.size() - 1)[1] = intervals[i][1]; + } else { + ans.get(ans.size() - 1)[1] = Math.max(ans.get(ans.size() - 1)[1], intervals[i][1]); + } + } + + return ans.toArray(new int[ans.size()][]); + } +} diff --git a/reorder-list/forest000014.java b/reorder-list/forest000014.java new file mode 100644 index 000000000..94f91e3da --- /dev/null +++ b/reorder-list/forest000014.java @@ -0,0 +1,49 @@ +/* +# Time Complexity: O(n) +# Space Complexity: O(1) +# Solution +Dale 님의 솔루션을 참고했습니다. +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public void reorderList(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + ListNode curr = slow.next; + slow.next = null; + + ListNode prev = null; + while (curr != null) { + ListNode tempNext = curr.next; + curr.next = prev; + prev = curr; + curr = tempNext; + } + + ListNode left = head; + ListNode right = prev; + while (right != null) { + ListNode leftNext = left.next; + ListNode rightNext = right.next; + left.next = right; + right.next = leftNext; + left = leftNext; + right = rightNext; + } + } +}