diff --git a/jump-game/dev-jonghoonpark.md b/jump-game/dev-jonghoonpark.md new file mode 100644 index 000000000..af21905f8 --- /dev/null +++ b/jump-game/dev-jonghoonpark.md @@ -0,0 +1,110 @@ +- 문제: https://leetcode.com/problems/jump-game/ +- 풀이: https://algorithm.jonghoonpark.com/2024/07/17/leetcode-55 + +## dfs로 풀기 + +```java +class Solution { + boolean canJump = false; // 마지막 위치에 도착 할 수 있으면 true 로 변경 + + public boolean canJump(int[] nums) { + dfs(nums, 0); + + return canJump; + } + + private void dfs(int[] nums, int pointer) { + // 위치가 범위를 벗어났을 경우 + // 이미 방문한 위치일 경우 + // 이미 마지막에 도달 가능하다는 것을 확인했을 경우 + if (pointer >= nums.length || nums[pointer] == -1 || canJump) { + return; + } + + int maxHeight = nums[pointer]; + nums[pointer] = -1; + + // 마지막이 아닌데 0 이 나왔을 경우 이동 불가능 + if (maxHeight == 0 && pointer != nums.length - 1) { + return; + } + + if (pointer == nums.length - 1) { + canJump = true; + } else { + while (maxHeight > 0) { + dfs(nums, pointer + maxHeight); + maxHeight--; + } + } + } +} +``` + +### TC, SC + +시간복잡도는 `O(n^2)`, 공간복잡도는 `O(n)` 이다. + +## dp로 풀기 + +중간에 도달하지 못하는 위치가 있을 경우 false를 반환한다. dp 라고 해도 되려나 애매한 것 같다. + +```java +class Solution { + public boolean canJump(int[] nums) { + int[] dp = new int[nums.length]; + int lastIndex = nums.length - 1; + dp[0] = 1; + + for (int i = 0; i < nums.length; i++) { + if (dp[i] == 0) { + return false; + } + + int current = nums[i]; + int toIndex = i + current + 1; + if(toIndex > lastIndex) { + toIndex = nums.length; + } + Arrays.fill(dp, i, toIndex, 1); + if (dp[lastIndex] > 0) { + return true; + } + } + + return dp[lastIndex] != 0; + } +} +``` + +### TC, SC + +시간복잡도는 `O(n^2)`, 공간복잡도는 `O(n)` 이다. + +## greedy 방식으로 풀기 + +greedy 문제는 항상 어떻게 증명할 수 있는지를 고민을 많이 해봐야 하는 것 같다. + +```java +class Solution { + public boolean canJump(int[] nums) { + int maxReach = 0; // 현재까지 도달할 수 있는 가장 먼 인덱스 + + for (int i = 0; i < nums.length; i++) { + if (i > maxReach) { + return false; // 현재 인덱스에 도달할 수 없는 경우 + } + maxReach = Math.max(maxReach, i + nums[i]); + if (maxReach >= nums.length - 1) { + return true; // 마지막 인덱스에 도달하거나 그 이상일 경우 + } + } + + return false; + } +} +``` + +### TC, SC + +시간복잡도는 `O(n)`, 공간복잡도는 `O(1)` 이다. diff --git a/longest-common-subsequence/dev-jonghoonpark.md b/longest-common-subsequence/dev-jonghoonpark.md new file mode 100644 index 000000000..1708c9afb --- /dev/null +++ b/longest-common-subsequence/dev-jonghoonpark.md @@ -0,0 +1,26 @@ +- 문제: https://leetcode.com/problems/longest-common-subsequence/ +- 풀이: https://algorithm.jonghoonpark.com/2024/07/17/leetcode-1143 + +```java +class Solution { + public int longestCommonSubsequence(String text1, String text2) { + int[][] dp = new int[text1.length() + 1][text2.length() + 1]; + + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + return dp[text1.length()][text2.length()]; + } +} +``` + +### TC, SC + +시간 복잡도는 `O(m * n)` 공간 복잡도는 `O(m * n)` 이다. diff --git a/longest-increasing-subsequence/dev-jonghoonpark.md b/longest-increasing-subsequence/dev-jonghoonpark.md new file mode 100644 index 000000000..e1ffce15f --- /dev/null +++ b/longest-increasing-subsequence/dev-jonghoonpark.md @@ -0,0 +1,104 @@ +- 문제: https://leetcode.com/problems/longest-increasing-subsequence/ +- 풀이: https://algorithm.jonghoonpark.com/2024/02/27/leetcode-300 + +Beats 62.23% + +```java +class Solution { + public int lengthOfLIS(int[] nums) { + int[] dp = new int[nums.length]; + Arrays.fill(dp, 1); + + int max = 1; + for (int i = 1; i < nums.length; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + dp[i] = Math.max(dp[j] + 1, dp[i]); + } + } + max = Math.max(max, dp[i]); + } + return max; + } +} +``` + +### TC, SC + +시간복잡도는 O(n^2), 공간복잡도는 O(n)이다. + +## Follow up 문제 + +> Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity? + +파이썬 에서는 bisect_left 를 쓰면 된다고 하지만 자바에는 존재하지 않는다. 하지만 방법을 찾아보자. + +### dp 를 사용하지 않은 풀이 (Beats 82.20%) + +우선은 ArrayList를 이용하여 비슷하게 모방해보았다. + +```java +public int lengthOfLIS(int[] nums) { + ArrayList subsequence = new ArrayList<>(); + subsequence.add(nums[0]); + + for (int i = 1; i < nums.length; i++) { + int current = nums[i]; + + if(current > subsequence.get(subsequence.size() - 1)) { + subsequence.addLast(current); + } else if (current < subsequence.get(0)) { + subsequence.set(0, current); + } else { + for (int j = 1; j < subsequence.size(); j++) { + if(current > subsequence.get(j - 1) && current < subsequence.get(j)) { + subsequence.set(j, current); + } + } + } + } + + return subsequence.size(); +} +``` + +#### TC, SC + +아직은 여전히 시간복잡도는 O(n^2), 공간복잡도는 O(n)이다. +빅오 표기법 상으로는 동일하나 실제 동작 시간은 감소하였다. + +### 진짜 binary search를 도입해보기 (Beats 92.57%) + +자바 Collections 에서는 binarySearch 메소드를 제공해준다. +적용해보면 다음과 같다. + +```java +public int lengthOfLIS(int[] nums) { + ArrayList subsequence = new ArrayList<>(); + + for (int current : nums) { + // Collections.binarySearch : 목록에 포함된 경우 검색 키의 인덱스, 그렇지 않으면 (-(삽입점) - 1) 을 반환함. + int pos = Collections.binarySearch(subsequence, current); + if (pos < 0) pos = -(pos + 1); + if (pos >= subsequence.size()) { + subsequence.add(current); + } else { + subsequence.set(pos, current); + } + } + + return subsequence.size(); +} +``` + +#### Collections.binarySearch + +해당 메소드의 리턴값은 다음과 같다. + +> 목록에 포함된 경우 검색 키의 인덱스, 그렇지 않으면 (-(삽입점) - 1). +> 삽입 지점은 키가 목록에 삽입되는 지점, 즉 키보다 큰 첫 번째 요소의 인덱스 또는 목록의 모든 요소가 지정된 키보다 작은 경우 list.size()로 정의됩니다. +> 키가 발견되는 경우에만 반환값이 >= 0이 되도록 보장합니다. + +#### TC, SC + +시간복잡도는 `O(n * logn)`, 공간복잡도는 `O(n)`이다. diff --git a/maximum-subarray/dev-jonghoonpark.md b/maximum-subarray/dev-jonghoonpark.md new file mode 100644 index 000000000..165065be6 --- /dev/null +++ b/maximum-subarray/dev-jonghoonpark.md @@ -0,0 +1,26 @@ +- 문제: https://leetcode.com/problems/maximum-subarray/ +- 풀이: https://algorithm.jonghoonpark.com/2024/05/07/leetcode-53 + +```java +class Solution { + public int maxSubArray(int[] nums) { + int maxSum = Integer.MIN_VALUE; + int currentSum = 0; + + for (int i = 0; i < nums.length; i++) { + currentSum += nums[i]; + maxSum = Math.max(maxSum, currentSum); + currentSum = Math.max(currentSum, 0); + } + + return maxSum; + } +} +``` + +- currentSum이 maxSum보다 클 경우 maxSum을 갱신한다. +- currentSum은 음수일 경우 (0보다 작을 경우) 0으로 초기화 한다. + +### TC, SC + +시간 복잡도는 O(n), 공간 복잡도는 O(1) 이다. diff --git a/unique-paths/dev-jonghoonpark.md b/unique-paths/dev-jonghoonpark.md new file mode 100644 index 000000000..de0243005 --- /dev/null +++ b/unique-paths/dev-jonghoonpark.md @@ -0,0 +1,44 @@ +- 문제: https://leetcode.com/problems/unique-paths/ +- 풀이: https://algorithm.jonghoonpark.com/2024/07/16/leetcode-62 + +```java +class Solution { + public int uniquePaths(int m, int n) { + int[][] matrix = new int[m][n]; + matrix[0][0] = 1; + + Deque deque = new ArrayDeque<>(); + deque.addLast(new Coordinate(1, 0)); + deque.addLast(new Coordinate(0, 1)); + while (!deque.isEmpty()) { + Coordinate coordinate = deque.removeFirst(); + if (coordinate.x >= m || coordinate.y >= n || matrix[coordinate.x][coordinate.y] != 0) { + continue; + } + + int top = coordinate.y > 0 ? matrix[coordinate.x][coordinate.y - 1] : 0; + int left = coordinate.x > 0 ? matrix[coordinate.x - 1][coordinate.y] : 0; + matrix[coordinate.x][coordinate.y] = top + left; + + deque.addLast(new Coordinate(coordinate.x + 1, coordinate.y)); + deque.addLast(new Coordinate(coordinate.x, coordinate.y + 1)); + } + + return matrix[m - 1][n - 1]; + } +} + +class Coordinate { + int x; + int y; + + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + } +} +``` + +### TC, SC + +시간 복잡도는 `O(m * n)` 공간 복잡도는 `O(m * n)` 이다.