From 66eb10cd0a46c2d5a37c8731774c3269cdd70f8b Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 24 Sep 2024 22:39:35 +0900 Subject: [PATCH 01/46] 217 / Contains Duplicate / easy / 22m --- wjsdncl/217_contains_duplicate.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 wjsdncl/217_contains_duplicate.js diff --git a/wjsdncl/217_contains_duplicate.js b/wjsdncl/217_contains_duplicate.js new file mode 100644 index 0000000..a25dff2 --- /dev/null +++ b/wjsdncl/217_contains_duplicate.js @@ -0,0 +1,19 @@ +/* +/ 22분 소요 +/ +/ 시간 복잡도: O(n log n) +/ +/ 이중 for문 이용해서 풀었는데 Time Limit Exceeded가 나와서 다시 풀었습니다. +/ +/ 배열을 오름차순으로 정렬한 뒤, 배열을 순회하며 중복된 요소가 있는지 확인합니다. +/ 중복된 요소가 있다면 true를 반환하고, 없다면 false를 반환합니다. +*/ +var containsDuplicate = function (nums) { + nums.sort((a, b) => a - b); // nums 배열을 오름차순으로 정렬 (O(n log n)) + + // 정렬된 nums 배열을 순회 (O(n)) + for (let i = 0; i < nums.length; i++) { + if (nums[i] === nums[i + 1]) return true; // 현재 요소와 다음 요소가 같다면 true 반환 + } + return false; // 중복된 요소가 없다면 false 반환 +}; From 57cd14b336fbd55eaacfff1710c065ad6be8cac9 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 24 Sep 2024 22:40:36 +0900 Subject: [PATCH 02/46] 268 / Missing Number / easy / 13m --- wjsdncl/268_missing_number.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 wjsdncl/268_missing_number.js diff --git a/wjsdncl/268_missing_number.js b/wjsdncl/268_missing_number.js new file mode 100644 index 0000000..2de691c --- /dev/null +++ b/wjsdncl/268_missing_number.js @@ -0,0 +1,23 @@ +/* +/ 13분 소요 +/ +/ 시간복잡도: O(n log n) +/ +/ 배열을 오름차순으로 정렬한 뒤, 배열을 순회하며 누락된 숫자를 찾아 반환합니다. +/ 맨 앞의 숫자가 0이 아니라면 0을 반환합니다. +*/ +var missingNumber = function (nums) { + nums.sort((a, b) => a - b); // nums 배열을 오름차순으로 정렬 (O(n log n)) + + // 맨 앞의 숫자가 0이 아닌 경우 0 반환 + if (nums[0] !== 0) return 0; + + // 배열 순회하여 누락된 숫자 반환 (O(n)) + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i + 1] - nums[i] !== 1) return nums[i] + 1; + } + + return 0; +}; + +console.log(missingNumber([3, 0, 1])); // 2 From 51856a18e1f1442d638a0da3fcc60b640c47cb95 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 24 Sep 2024 22:42:03 +0900 Subject: [PATCH 03/46] 121 / Best Time to Buy and Sell Stock / easy / 16m --- .../121_best_time_to_buy_and_sell_stock.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 wjsdncl/121_best_time_to_buy_and_sell_stock.js diff --git a/wjsdncl/121_best_time_to_buy_and_sell_stock.js b/wjsdncl/121_best_time_to_buy_and_sell_stock.js new file mode 100644 index 0000000..e692356 --- /dev/null +++ b/wjsdncl/121_best_time_to_buy_and_sell_stock.js @@ -0,0 +1,26 @@ +/* +/ 16분 소요 +/ +/ 시간복잡도: O(n) +/ +/ 최소값과 최대 이익을 저장해두고 배열을 순회하며 최소값과 최대 이익을 갱신합니다. +/ 마지막 최대 이익을 반환합니다. +*/ +var maxProfit = function (prices) { + let minPrice = prices[0]; + let maxProfit = 0; + + // 배열 순회 (O(n)) + for (let i = 1; i < prices.length; i++) { + // 최소값 갱신 + if (prices[i] < minPrice) { + minPrice = prices[i]; + } + // 최대 이익 갱신 + else if (prices[i] - minPrice > maxProfit) { + maxProfit = prices[i] - minPrice; + } + } + + return maxProfit; +}; From c9adf07b1c5129af16877b8011b1abb87125f03e Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 24 Sep 2024 22:42:50 +0900 Subject: [PATCH 04/46] 252 / Meeting Room / easy / 26m --- wjsdncl/252_meeting_schedule.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 wjsdncl/252_meeting_schedule.js diff --git a/wjsdncl/252_meeting_schedule.js b/wjsdncl/252_meeting_schedule.js new file mode 100644 index 0000000..b3e3a9f --- /dev/null +++ b/wjsdncl/252_meeting_schedule.js @@ -0,0 +1,23 @@ +/* +/ 26분 소요 +/ +/ 시간 복잡도: O(n log n) +/ +/ 시작 시간을 기준으로 오름차순 정렬한 뒤, 배열을 순회하며 현재 회의의 시작 시간이 이전 회의의 종료 시간보다 빠른지 확인합니다. +/ 만약 빠르다면 false를 반환하고, 모든 회의에 대해 충돌이 없을 경우 true를 반환합니다. +*/ +class Solution { + canAttendMeetings(intervals) { + // 시작 시간을 기준으로 오름차순 정렬 (O(n log n)) + intervals.sort((a, b) => a.start - b.start); + + // 배열 순회 (O(n)) + for (let i = 1; i < intervals.length; i++) { + // 현재 회의의 시작 시간이 이전 회의의 종료 시간보다 빠른 경우 false 반환 + if (intervals[i].start < intervals[i - 1].end) return false; + } + + // 모든 회의에 대해 충돌이 없을 경우 true 반환 + return true; + } +} From bf51cd9db8f7d09de5817efd7727842d87e9b9cc Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 24 Sep 2024 22:43:34 +0900 Subject: [PATCH 05/46] 15 / 3sum / medium / 42m --- wjsdncl/15_3sum.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 wjsdncl/15_3sum.js diff --git a/wjsdncl/15_3sum.js b/wjsdncl/15_3sum.js new file mode 100644 index 0000000..b1b5fba --- /dev/null +++ b/wjsdncl/15_3sum.js @@ -0,0 +1,54 @@ +/* +/ 42분 소요 +/ +/ 시간복잡도: O(n^2) +/ +/ 투 포인터 알고리즘을 몰라서 푸는데 시간이 오래 걸렸습니다. +/ 막 풀다가 for문 3개로 풀려고 했는데 시간 복잡도가 O(n^3)이 되어버려서 이건 좀... 싶었습니다. +/ 좀 찾아보니 투 포인터 알고리즘을 찾아서 풀 수 있었습니다. +/ 알고리즘을 잘 몰라서 공부 좀 해야 할 것 같습니다. +/ +/ 배열을 오름차순으로 정렬한 뒤, 배열을 순회하며 투 포인터 알고리즘을 사용하여 3개의 숫자의 합이 0이 되는 경우를 찾습니다. +/ 중복된 숫자는 건너뛰고, 결과 배열에 추가합니다. +*/ +var threeSum = function (nums) { + // nums 배열을 오름차순으로 정렬 (O(n log n)) + nums.sort((a, b) => a - b); + const result = []; + + // 배열 순회 (O(n)) + for (let i = 0; i < nums.length - 2; i++) { + // 중복된 숫자는 건너뛰기 + if (i > 0 && nums[i] === nums[i - 1]) continue; + + let left = i + 1; + let right = nums.length - 1; + + // 투 포인터 알고리즘 (O(n)) + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + + // 0보다 작으면 start++ + if (sum < 0) { + left++; + } + // 0보다 크면 end-- + else if (sum > 0) { + right--; + } + // 0이면 결과 배열에 추가 + else { + result.push([nums[i], nums[left], nums[right]]); + + // 현재 요소와 다음/이전 요소가 같으면 건너뛰기 + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + + left++; + right--; + } + } + } + + return result; +}; From 910b70ec6986a84c88026c3e22d78880ac34b7e0 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 1 Oct 2024 23:44:01 +0900 Subject: [PATCH 06/46] 242 / Valid Anagram / Easy / 15m33 --- wjsdncl/242_valid_anagram.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 wjsdncl/242_valid_anagram.js diff --git a/wjsdncl/242_valid_anagram.js b/wjsdncl/242_valid_anagram.js new file mode 100644 index 0000000..1f0e633 --- /dev/null +++ b/wjsdncl/242_valid_anagram.js @@ -0,0 +1,19 @@ +/** + * 15m33s 소요 + * + * 시간 복잡도: O(n log n) + * + * split을 사용하면 배열로 변환 할 수 있어서 이걸 사용했습니다. + * 배열을 sort로 정렬하고 join으로 다시 문자열로 변환하여 비교했습니다. + * + * 두 문자열의 길이가 다르다면 false를 반환합니다. + * 두 문자열을 배열로 변환하고, 알파벳 순으로 정렬한 후 다시 문자열로 변환하여 비교합니다. + * 두 문자열이 동일하면 애너그램이므로 true를 반환하고 그렇지 않으면 false를 반환합니다. + */ +var isAnagram = function (s, t) { + // 두 문자열의 길이가 다르다면 false 반환 (O(1)) + if (s.length !== t.length) return false; + + // 문자열을 배열로 변환하고 알파벳 순으로 정렬한 후 다시 문자열로 변환하여 비교 (O(n log n)) + return s.split("").sort().join("") === t.split("").sort().join(""); +}; From cdb702c4a09b088d19c750a3db95307125f00895 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 1 Oct 2024 23:45:33 +0900 Subject: [PATCH 07/46] 238 / Product of Array Except Self / Medium / 35m16 --- wjsdncl/238_product_of_array_except_self.js | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 wjsdncl/238_product_of_array_except_self.js diff --git a/wjsdncl/238_product_of_array_except_self.js b/wjsdncl/238_product_of_array_except_self.js new file mode 100644 index 0000000..3dde254 --- /dev/null +++ b/wjsdncl/238_product_of_array_except_self.js @@ -0,0 +1,31 @@ +/** + * 35m16s 소요 + * + * 시간복잡도: O(n) + * + * 어렵네요. 나눗셈을 사용 안하고 어떻게 풀어야 할지 감이 안 잡혀서 풀이를 보고 이해했습니다. + * + * 왼쪽에서부터 곱을 계산하여 answer에 저장합니다. + * 오른쪽에서부터 곱을 계산하여 answer에 곱해줍니다. + * answer를 반환합니다. + */ +var productExceptSelf = function (nums) { + const n = nums.length; + const answer = new Array(n); + + // 왼쪽에서부터 곱을 계산하여 answer에 저장 (O(n)) + let leftProduct = 1; + for (let i = 0; i < n; i++) { + answer[i] = leftProduct; + leftProduct *= nums[i]; + } + + // 오른쪽에서부터 곱을 계산하여 answer에 곱해줌 (O(n)) + let rightProduct = 1; + for (let i = n - 1; i >= 0; i--) { + answer[i] *= rightProduct; + rightProduct *= nums[i]; + } + + return answer; +}; From 2bdc3fc271e8cfaa3ee7726b91ef3cda81b1f076 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 1 Oct 2024 23:46:01 +0900 Subject: [PATCH 08/46] 191 / Number of 1 Bits / Easy / 14m32s --- wjsdncl/191_number_of_1_bits.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 wjsdncl/191_number_of_1_bits.js diff --git a/wjsdncl/191_number_of_1_bits.js b/wjsdncl/191_number_of_1_bits.js new file mode 100644 index 0000000..c629dc5 --- /dev/null +++ b/wjsdncl/191_number_of_1_bits.js @@ -0,0 +1,21 @@ +/** + * 14m32s 소요 + * + * 시간복잡도: O(n) n = 1의 개수 + * + * 비트 연산자를 사용하는게 익숙하지 않아서 찾아보면서 풀었습니다. + * 찾아보면서 Brian Kernighan's 알고리즘을 알게 되었습니다. + * 이 알고리즘은 n과 n-1을 AND 연산하면 1의 개수가 1개 줄어든다는 것을 이용합니다. + * + * n이 0이 될 때까지 n과 n-1을 AND 연산하고, 1의 개수를 세어 반환합니다. + */ +var hammingWeight = function (n) { + let count = 0; + + while (n !== 0) { + n &= n - 1; + count++; + } + + return count; +}; From 82a66a5088d6afbef075627882572a9017b54fd7 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 1 Oct 2024 23:49:17 +0900 Subject: [PATCH 09/46] 100 / Same Tree / Easy / 24m40s --- wjsdncl/100_same_tree.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 wjsdncl/100_same_tree.js diff --git a/wjsdncl/100_same_tree.js b/wjsdncl/100_same_tree.js new file mode 100644 index 0000000..10dbb90 --- /dev/null +++ b/wjsdncl/100_same_tree.js @@ -0,0 +1,24 @@ +/** + * 24m40s 소요 + * + * 시간복잡도: O(n) n = 트리의 노드 개수 + * + * & 비트 연산자를 사용하여 두 트리의 노드를 비교합니다. + * 이진 트리를 처음 알게 되었는데 이진 트리의 구조를 이해하는 데 도움이 되었습니다. + * val은 현재 노드의 값이고 left와 right는 왼쪽과 오른쪽 자식 노드를 가리킵니다. + * + * 두 트리가 모두 null인 경우 두 트리는 동일합니다. + * 둘 중 하나가 null이거나 노드의 값이 다르면 트리는 동일하지 않습니다. + * 현재 노드가 같다면 왼쪽과 오른쪽 자식을 재귀적으로 비교합니다. + * 두 트리가 동일하다면 true를 반환하고 그렇지 않다면 false를 반환합니다. + */ +var isSameTree = function (p, q) { + // 둘 다 null인 경우 두 트리는 동일 + if (!p && !q) return true; + + // 둘 중 하나가 null이거나 노드의 값이 다르면 트리는 동일하지 않음 + if (!p || !q || p.val !== q.val) return false; + + // 현재 노드가 같다면 왼쪽과 오른쪽 자식을 재귀적으로 비교 (O(n)) + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); +}; From c01c5842dc3933ce85132d8ab192941a04fc8b9b Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 1 Oct 2024 23:50:55 +0900 Subject: [PATCH 10/46] 746 / Min Cost Climbing Stairs / Easy / 29m35s --- wjsdncl/746_min_cost_climbing_stairs.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 wjsdncl/746_min_cost_climbing_stairs.js diff --git a/wjsdncl/746_min_cost_climbing_stairs.js b/wjsdncl/746_min_cost_climbing_stairs.js new file mode 100644 index 0000000..76f2e27 --- /dev/null +++ b/wjsdncl/746_min_cost_climbing_stairs.js @@ -0,0 +1,23 @@ +/** + * 29m35s 소요 + * + * 시간복잡도: O(n) + * + * 저한테는 전혀 easy한 문제가 아니었습니다. + * 감이 안잡혀서 찾아보니 동적 계획법(Dynamic Programming)을 사용하여 풀 수 있다는 것을 알게 되었습니다. + * DP는 복잡한 문제를 간단한 하위 문제로 나누어 푸는 방법입니다. + * + * 배열 끝에서부터 역순으로 최소 비용을 계산합니다. + * i번째 계단에 다음 두 계단 중 작은 비용을 더합니다. + * 0번째 또는 1번째 계단에서 출발할 수 있으므로 둘 중 작은 값을 반환합니다. + */ +var minCostClimbingStairs = function (cost) { + // 배열 끝에서부터 역순으로 최소 비용을 계산 (O(n)) + for (let i = cost.length - 3; i >= 0; i--) { + // i번째 계단에 다음 두 계단 중 작은 비용을 더함 + cost[i] += Math.min(cost[i + 1], cost[i + 2]); + } + + // 0번째 또는 1번째 계단에서 출발할 수 있으므로 둘 중 작은 값을 반환 + return Math.min(cost[0], cost[1]); +}; From ef1983a3684e02fde60567a851e7aaffecfea8c4 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 2 Oct 2024 00:08:42 +0900 Subject: [PATCH 11/46] =?UTF-8?q?746=20/=20Min=20Cost=20Climbing=20Stairs?= =?UTF-8?q?=20/=20Easy=20/=2029m35s=20(=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wjsdncl/191_number_of_1_bits.js | 2 +- wjsdncl/238_product_of_array_except_self.js | 3 ++- wjsdncl/746_min_cost_climbing_stairs.js | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wjsdncl/191_number_of_1_bits.js b/wjsdncl/191_number_of_1_bits.js index c629dc5..156a3a6 100644 --- a/wjsdncl/191_number_of_1_bits.js +++ b/wjsdncl/191_number_of_1_bits.js @@ -3,7 +3,7 @@ * * 시간복잡도: O(n) n = 1의 개수 * - * 비트 연산자를 사용하는게 익숙하지 않아서 찾아보면서 풀었습니다. + * 비트 연산자를 사용하는게 오랜만이라 기억이 안나서 찾아보았습니다. * 찾아보면서 Brian Kernighan's 알고리즘을 알게 되었습니다. * 이 알고리즘은 n과 n-1을 AND 연산하면 1의 개수가 1개 줄어든다는 것을 이용합니다. * diff --git a/wjsdncl/238_product_of_array_except_self.js b/wjsdncl/238_product_of_array_except_self.js index 3dde254..1e928ea 100644 --- a/wjsdncl/238_product_of_array_except_self.js +++ b/wjsdncl/238_product_of_array_except_self.js @@ -3,7 +3,8 @@ * * 시간복잡도: O(n) * - * 어렵네요. 나눗셈을 사용 안하고 어떻게 풀어야 할지 감이 안 잡혀서 풀이를 보고 이해했습니다. + * 나눗셈을 사용 안하고 어떻게 풀어야 할지 감이 안 잡혀서 풀이를 보고 이해했습니다. + * 이해하는 데 시간이 걸렸지만 이해하고 나니 쉬운 문제였습니다. * * 왼쪽에서부터 곱을 계산하여 answer에 저장합니다. * 오른쪽에서부터 곱을 계산하여 answer에 곱해줍니다. diff --git a/wjsdncl/746_min_cost_climbing_stairs.js b/wjsdncl/746_min_cost_climbing_stairs.js index 76f2e27..53fc409 100644 --- a/wjsdncl/746_min_cost_climbing_stairs.js +++ b/wjsdncl/746_min_cost_climbing_stairs.js @@ -3,7 +3,6 @@ * * 시간복잡도: O(n) * - * 저한테는 전혀 easy한 문제가 아니었습니다. * 감이 안잡혀서 찾아보니 동적 계획법(Dynamic Programming)을 사용하여 풀 수 있다는 것을 알게 되었습니다. * DP는 복잡한 문제를 간단한 하위 문제로 나누어 푸는 방법입니다. * From 20cf77313778d37dacf0142e48ec95f19c36dfc9 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 16 Oct 2024 16:47:51 +0900 Subject: [PATCH 12/46] 1 / Two Sum / easy / 20m 31s --- wjsdncl/1_two_sum.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 wjsdncl/1_two_sum.js diff --git a/wjsdncl/1_two_sum.js b/wjsdncl/1_two_sum.js new file mode 100644 index 0000000..b4473ed --- /dev/null +++ b/wjsdncl/1_two_sum.js @@ -0,0 +1,31 @@ +/** + * 20m31s 소요 + * + * 시간 복잡도: O(n) + * + * 중첩 for문을 사용해서 만들었는데 시간 복잡도가 O(n^2)이 되어버렸습니다. + * 시간복잡도를 O(n)으로 줄이기 위해 해시맵을 사용하여 풀었습니다. + * + * 해시맵을 사용하여 타겟에서 현재 숫자를 뺀 값을 키로 인덱스를 값으로 저장합니다. + * 현재 숫자와 뺀 값이 해시맵에 존재하면 해당 값을 반환합니다. + * 존재하지 않으면 현재 숫자와 인덱스를 해시맵에 저장합니다. + */ +var twoSum = function (nums, target) { + // 값을 저장할 해시맵 초기화 + const hashMap = {}; + + // 배열을 인덱스와 함께 순회 + for (let i = 0; i < nums.length; i++) { + // 현재 숫자와 더해서 타겟이 되는 값을 계산 + const complement = target - nums[i]; + + // 해시맵에 해당 값(complement)이 존재하는지 확인 + if (hashMap.hasOwnProperty(complement)) { + // 존재하면 현재 인덱스와 해시맵에 저장된 인덱스를 반환 + return [hashMap[complement], i]; + } + + // 존재하지 않으면 현재 숫자와 그 인덱스를 해시맵에 저장 + hashMap[nums[i]] = i; + } +}; From 60169b41180e2e0b12bb51670a3e226fddeb0718 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 16 Oct 2024 16:48:26 +0900 Subject: [PATCH 13/46] 104 / Maximun Depth of Binary Tree / easy / 38m 21s --- wjsdncl/104_maximum_depth_of_binary_tree.js | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 wjsdncl/104_maximum_depth_of_binary_tree.js diff --git a/wjsdncl/104_maximum_depth_of_binary_tree.js b/wjsdncl/104_maximum_depth_of_binary_tree.js new file mode 100644 index 0000000..cf5e10d --- /dev/null +++ b/wjsdncl/104_maximum_depth_of_binary_tree.js @@ -0,0 +1,22 @@ +/** + * 38m21s + * + * 시간 복잡도: O(n) + * + * 이진 트리의 최대 깊이를 구하는 문제입니다. + * 재귀적으로 왼쪽 서브트리와 오른쪽 서브트리의 최대 깊이를 구하고 더 큰 값에 1을 더해 반환합니다. + * 루트가 null인 경우 깊이는 0입니다. + */ +var maxDepth = function (root) { + // 루트가 null인 경우, 깊이는 0 + if (root === null) { + return 0; + } + + // 왼쪽과 오른쪽 서브트리의 최대 깊이를 구함 + const leftDepth = maxDepth(root.left); + const rightDepth = maxDepth(root.right); + + // 더 큰 깊이에 1을 더해 반환 + return Math.max(leftDepth, rightDepth) + 1; +}; From 8709186c72ed17bdade4bbf98f7cfb63814e22b0 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 16 Oct 2024 16:49:17 +0900 Subject: [PATCH 14/46] 141 / Linked List Cycle / easy / 32m 15s --- wjsdncl/141_linked_list_cycle.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 wjsdncl/141_linked_list_cycle.js diff --git a/wjsdncl/141_linked_list_cycle.js b/wjsdncl/141_linked_list_cycle.js new file mode 100644 index 0000000..4c46aff --- /dev/null +++ b/wjsdncl/141_linked_list_cycle.js @@ -0,0 +1,30 @@ +/** + * 32m15s + * + * 시간 복잡도: O(n) + * + * 찾아보니 토끼와 거북이 알고리즘으로 풀 수 있는 문제였습니다. + * + * 두 포인터를 이용하여 사이클이 존재하는지 확인합니다. + * slow는 한 칸씩 fast는 두 칸씩 이동합니다. + * 만약 slow와 fast가 만나면 사이클이 존재합니다. + * 만나지 않으면 사이클이 존재하지 않습니다. + */ +var hasCycle = function (head) { + let slow = head; + let fast = head; + + // fast와 fast.next가 null이 아닌 동안 반복 + while (fast !== null && fast.next !== null) { + slow = slow.next; // slow는 한 칸 이동 + fast = fast.next.next; // fast는 두 칸 이동 + + // slow와 fast가 만나는 경우 사이클 존재 + if (slow === fast) { + return true; + } + } + + // 반복이 끝날 때까지 만나지 않으면 사이클이 없음 + return false; +}; From 4cad49622c7c3e2d6fd9ac23f472b7134579c31a Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 16 Oct 2024 16:49:33 +0900 Subject: [PATCH 15/46] 202 / Happy Number / Easy / 29m 21s --- wjsdncl/202_happy_number.js | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 wjsdncl/202_happy_number.js diff --git a/wjsdncl/202_happy_number.js b/wjsdncl/202_happy_number.js new file mode 100644 index 0000000..08bafe9 --- /dev/null +++ b/wjsdncl/202_happy_number.js @@ -0,0 +1,38 @@ +/** + * 29m21s + * + * 시간 복잡도: O(log n) + * + * 어떻게 풀어야 할지 감이 안 잡혀서 챗지피티에게 도움을 받았습니다 + * + * 이 문제도 토끼와 거북이 알고리즘을 사용하여 풀 수 있습니다. + * + * slow는 n으로 초기화하고 fast는 n의 다음 수로 초기화합니다. + * slow와 fast가 만나지 않을 때까지 반복합니다. + * 만약 fast가 1이면 true를 반환합니다. + * 그렇지 않으면 false를 반환합니다. + */ +var isHappy = function (n) { + // 각 자리의 제곱합을 계산하는 함수 + const getNext = (num) => { + let sum = 0; + while (num > 0) { + const digit = num % 10; + sum += digit * digit; + num = Math.floor(num / 10); + } + return sum; + }; + + let slow = n; + let fast = getNext(n); + + // 사이클이 감지될 때까지 반복 + while (fast !== 1 && slow !== fast) { + slow = getNext(slow); + fast = getNext(getNext(fast)); + } + + // fast가 1이면 true, 그렇지 않으면 false + return fast === 1; +}; From 3fd5282e9c8de0bd2a2cdcad1dedaa20f74cc02b Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 16 Oct 2024 17:08:13 +0900 Subject: [PATCH 16/46] 20 / Valid Parentheses / easy / 22m 41s --- wjsdncl/20_valid_parentheses.js | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 wjsdncl/20_valid_parentheses.js diff --git a/wjsdncl/20_valid_parentheses.js b/wjsdncl/20_valid_parentheses.js new file mode 100644 index 0000000..5188ce1 --- /dev/null +++ b/wjsdncl/20_valid_parentheses.js @@ -0,0 +1,39 @@ +/** + * 22m41s + * + * 시간 복잡도: O(n) + * + * 맵을 사용해서 괄호의 짝을 저장하고 스택을 사용해서 괄호의 짝이 맞는지 확인하는 방법으로 풀었습니다. + * + * 스택을 사용해서 여는 괄호를 저장하고 닫는 괄호를 만나면 스택에서 가장 최근의 여는 괄호를 꺼내서 짝이 맞는지 확인합니다. + * 짝이 맞지 않으면 false를 반환합니다. + * 모든 괄호를 확인한 후 스택이 비어 있으면 true를 반환합니다. + * 스택이 비어 있지 않으면 false를 반환합니다. + */ +var isValid = function (s) { + const stack = []; + // 괄호의 짝을 저장한 맵 + const map = { + ")": "(", + "}": "{", + "]": "[", + }; + + for (let char of s) { + // 닫는 괄호인 경우 + if (map[char]) { + // 스택에서 가장 최근의 여는 괄호를 꺼냄 + const topElement = stack.length ? stack.pop() : "#"; + // 짝이 맞지 않으면 false 반환 + if (topElement !== map[char]) { + return false; + } + } else { + // 여는 괄호는 스택에 추가 + stack.push(char); + } + } + + // 스택이 비어 있어야 모든 괄호가 짝을 이룬 것 + return stack.length === 0; +}; From a40ac6851393eceffef09fba5523a5fd9607c7aa Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 23 Oct 2024 23:56:41 +0900 Subject: [PATCH 17/46] 338 / Counting Bits / Easy / 17m --- wjsdncl/338_counting_bits.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 wjsdncl/338_counting_bits.js diff --git a/wjsdncl/338_counting_bits.js b/wjsdncl/338_counting_bits.js new file mode 100644 index 0000000..429d2e4 --- /dev/null +++ b/wjsdncl/338_counting_bits.js @@ -0,0 +1,22 @@ +/** + * 17m26s 소요 + * + * 시간복잡도 : O(n log n) + * + * O(n)으로 풀 수 있는거 같은데 잘 모르겠습니다. + * + * 1. 0부터 n까지의 수를 이진수로 변환한다. + * 2. 0의 개수를 세고 배열에 저장한다. + * 3. 배열을 반환한다. + */ +var countBits = function (n) { + const result = []; + + // 0부터 n까지 반복 (O(n)) + for (let i = 0; i <= n; i++) { + // 이진수로 변환 후 0의 개수를 세어 배열에 저장 (O(log n)) + result.push(i.toString(2).replace(/0/g, "").length); + } + + return result; +}; From 36ff4f83cee492507a59f457673d7aa2d17e8bd4 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 23 Oct 2024 23:57:47 +0900 Subject: [PATCH 18/46] 70 / Climbing Stairs / Easy / 19m --- wjsdncl/70_climbing_stairs.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 wjsdncl/70_climbing_stairs.js diff --git a/wjsdncl/70_climbing_stairs.js b/wjsdncl/70_climbing_stairs.js new file mode 100644 index 0000000..4753078 --- /dev/null +++ b/wjsdncl/70_climbing_stairs.js @@ -0,0 +1,26 @@ +/** + * 19m54s 소요 + * + * 시간복잡도: O(n) + * + * dp(동적 프로그래밍)를 사용해서 풀 수 있는 문제입니다. + * + * 1. dp 배열을 생성하고 0으로 초기화합니다. + * 2. dp[0]과 dp[1]을 1로 초기화합니다. + * 3. 2부터 n까지 반복하면서 dp[i] = dp[i - 1] + dp[i - 2]를 수행합니다. + * 4. dp[n]을 반환합니다. + */ +var climbStairs = function (n) { + // dp 배열을 생성하고 0으로 초기화 + const dp = Array(n + 1).fill(0); + + dp[0] = 1; + dp[1] = 1; + + // 2부터 n까지 반복 (O(n)) + for (let i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + + return dp[n]; +}; From 03c5b104f3bb0800d86484a54eb3426b10e10ebd Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 23 Oct 2024 23:58:16 +0900 Subject: [PATCH 19/46] 21 / Merge Two Sorted Lists / easy / 28m --- wjsdncl/21_merge_two_sorted_lists.js | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 wjsdncl/21_merge_two_sorted_lists.js diff --git a/wjsdncl/21_merge_two_sorted_lists.js b/wjsdncl/21_merge_two_sorted_lists.js new file mode 100644 index 0000000..7bc5d45 --- /dev/null +++ b/wjsdncl/21_merge_two_sorted_lists.js @@ -0,0 +1,35 @@ +/** + * 28m11s 소요 + * + * 시간복잡도: O(n) + * + * 어떻게 풀어야할지 모르겠어서 찾아봐서 풀었습니다. + * 노드 어렵네요. + * + * 1. 두 리스트를 비교하면서 작은 값을 새로운 리스트에 추가합니다. + * 2. 작은 값을 추가한 리스트의 다음 값을 작은 값이 아닌 리스트로 변경합니다. + * 3. 1, 2를 반복합니다. + * 4. 작은 값이 없는 리스트를 추가합니다. + * 5. 새로운 리스트를 반환합니다. + */ +var mergeTwoLists = function (list1, list2) { + let head = new ListNode(); + let current = head; + + // 두 리스트를 비교하면서 작은 값을 새로운 리스트에 추가 (O(n)) + while (list1 && list2) { + if (list1.val < list2.val) { + current.next = list1; + list1 = list1.next; + } else { + current.next = list2; + list2 = list2.next; + } + + current = current.next; + } + + current.next = list1 || list2; + + return head.next; +}; From 7f76659ab4d5b0228a7da6bde0f1504a930feb47 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 23 Oct 2024 23:58:43 +0900 Subject: [PATCH 20/46] 48 / Rotate Image / medium / 31m --- wjsdncl/48_rotate_image.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 wjsdncl/48_rotate_image.js diff --git a/wjsdncl/48_rotate_image.js b/wjsdncl/48_rotate_image.js new file mode 100644 index 0000000..dd74465 --- /dev/null +++ b/wjsdncl/48_rotate_image.js @@ -0,0 +1,28 @@ +/** + * 31m11s 소요 + * + * 시간 복잡도 O(n^2) + * + * 어려워서 찾아봤습니다. 풀이를 보니까 쉬운 문제인데 생각을 못했습니다. + * + * 1. 행과 열을 바꿉니다. (i, j) -> (j, i) + * 2. 열을 뒤집습니다. + * 3. 행렬을 반환합니다. + */ +var rotate = function (matrix) { + const n = matrix.length; + + // 행과 열을 바꿉니다. (i, j) -> (j, i) (O(n^2)) + for (let i = 0; i < n; i++) { + for (let j = i; j < n; j++) { + [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; + } + } + + // 열을 뒤집습니다. (O(n^2)) + for (let i = 0; i < n; i++) { + matrix[i].reverse(); + } + + return matrix; +}; From 4e445b4bc69f106b3bf44fef0add3ae3003a332e Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 23 Oct 2024 23:59:12 +0900 Subject: [PATCH 21/46] 1046 / Last Stone Weight / easy / 21m --- wjsdncl/1046_last_stone_weight.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 wjsdncl/1046_last_stone_weight.js diff --git a/wjsdncl/1046_last_stone_weight.js b/wjsdncl/1046_last_stone_weight.js new file mode 100644 index 0000000..8fec0bf --- /dev/null +++ b/wjsdncl/1046_last_stone_weight.js @@ -0,0 +1,26 @@ +/** + * 21m44s 소요 + * + * 1. sort()로 내림차순 정렬한다. + * 2. 가장 큰 두개를 빼고 뺀 값이 0이 아니면 다시 배열에 넣는다. + * 3. 2를 반복한다. 배열의 길이가 1이하면 종료한다. + * 4. 배열의 길이가 0이면 0을 반환하고 아니면 배열의 첫번째 값을 반환한다. + */ +var lastStoneWeight = function (stones) { + // 내림차순 정렬 후 가장 큰 두개를 빼고 뺀 값이 0이 아니면 다시 배열에 넣는다. (O(n^2)) + while (stones.length > 1) { + // 내림차순 정렬 (O(n log n)) + stones.sort((a, b) => b - a); + + // 가장 큰 두개를 빼고 뺀 값이 0이 아니면 다시 배열에 넣는다. (O(n)) + const first = stones.shift(); + const second = stones.shift(); + + // 뺀 값이 0이 아니면 다시 배열에 넣는다. + if (first !== second) { + stones.push(first - second); + } + } + + return stones.length ? stones[0] : 0; +}; From a58ae738afae92c45b50df78471a82c626b1f1cf Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 5 Nov 2024 22:36:14 +0900 Subject: [PATCH 22/46] 136 / Single Number / Easy / 12m --- wjsdncl/136_single_number.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 wjsdncl/136_single_number.js diff --git a/wjsdncl/136_single_number.js b/wjsdncl/136_single_number.js new file mode 100644 index 0000000..d83442f --- /dev/null +++ b/wjsdncl/136_single_number.js @@ -0,0 +1,19 @@ +/** + * 12m 21s 소요 + * + * 시간복잡도: O(n) + * + * 1. XOR 연산을 이용한다. + * 2. XOR 연산은 두 값이 같으면 0, 다르면 1을 반환한다. + * 3. 배열의 모든 요소를 XOR 연산하면 한번만 나오는 요소만 남는다. + * 4. 남은 요소를 반환한다. + */ +var singleNumber = function (nums) { + let result = 0; + + for (let num of nums) { + result ^= num; + } + + return result; +}; From 708741337912350be0354ee0db1dd4776293eaa8 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 5 Nov 2024 22:37:04 +0900 Subject: [PATCH 23/46] 110 / Balanced Binary Tree / Easy / 35m --- wjsdncl/110_balanced_binary_tree.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 wjsdncl/110_balanced_binary_tree.js diff --git a/wjsdncl/110_balanced_binary_tree.js b/wjsdncl/110_balanced_binary_tree.js new file mode 100644 index 0000000..b96cc68 --- /dev/null +++ b/wjsdncl/110_balanced_binary_tree.js @@ -0,0 +1,29 @@ +/** + * 35m 33s 소요 + * + * 시간복잡도: O(n) + * + * 이진 트리 공부좀 더 해야할 거 같습니다 잘 모르겠어서 풀이를 참고했습니다. + * + * 1. 재귀를 이용하여 트리의 높이를 구합니다. + * 2. 재귀적으로 왼쪽과 오른쪽 자식 노드의 높이를 비교합니다. + * 3. 높이 차이가 1 이상이면 false를 반환합니다. + * 4. 높이 차이가 1 이하이면 현재 노드의 높이를 반환합니다. + */ +var isBalanced = function (root) { + const checkHeight = (node) => { + if (node === null) return 0; + + const leftHeight = checkHeight(node.left); + if (leftHeight === -1) return -1; + + const rightHeight = checkHeight(node.right); + if (rightHeight === -1) return -1; + + if (Math.abs(leftHeight - rightHeight) > 1) return -1; + + return Math.max(leftHeight, rightHeight) + 1; + }; + + return checkHeight(root) !== -1; +}; From 223921dc9fc9bbb2f0094d54b2543906e35e4ec8 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 5 Nov 2024 22:38:02 +0900 Subject: [PATCH 24/46] 54 / Spiral Matrix / Medium / 38m --- wjsdncl/54_spiral_matrix.js | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 wjsdncl/54_spiral_matrix.js diff --git a/wjsdncl/54_spiral_matrix.js b/wjsdncl/54_spiral_matrix.js new file mode 100644 index 0000000..0f8bfc3 --- /dev/null +++ b/wjsdncl/54_spiral_matrix.js @@ -0,0 +1,55 @@ +/** + * 38m 41s 소요 + * + * 시간복잡도: O(n * m) (n: 행의 길이, m: 열의 길이) + * + * 풀어보려고 했는데 어렵네요 풀이 참고해서 풀었습니다. + * + * 1. matrix의 행과 열의 길이를 구한다. + * 2. top이 bottom보다 작거나 같고 left가 right보다 작거나 같을 때까지 반복한다. + * 3. 왼쪽에서 오른쪽으로 이동하면서 result에 값을 추가한다. + * 4. 위에서 아래로 이동하면서 result에 값을 추가한다. + * 5. top이 bottom보다 작거나 같으면 오른쪽에서 왼쪽으로 이동하면서 result에 값을 추가한다. + * 6. left가 right보다 작거나 같으면 아래에서 위로 이동하면서 result에 값을 추가한다. + */ +var spiralOrder = function (matrix) { + const result = []; + if (matrix.length === 0) return result; + + let top = 0; + let bottom = matrix.length - 1; + let left = 0; + let right = matrix[0].length - 1; + + while (top <= bottom && left <= right) { + // 왼쪽에서 오른쪽으로 이동 + for (let i = left; i <= right; i++) { + result.push(matrix[top][i]); + } + top++; + + // 위에서 아래로 이동 + for (let i = top; i <= bottom; i++) { + result.push(matrix[i][right]); + } + right--; + + if (top <= bottom) { + // 오른쪽에서 왼쪽으로 이동 + for (let i = right; i >= left; i--) { + result.push(matrix[bottom][i]); + } + bottom--; + } + + if (left <= right) { + // 아래에서 위로 이동 + for (let i = bottom; i >= top; i--) { + result.push(matrix[i][left]); + } + left++; + } + } + + return result; +}; From fe7de3e3d67cae308d934d577e0f4684c7b37f03 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 5 Nov 2024 22:38:33 +0900 Subject: [PATCH 25/46] 973 / K Closest Points to Origin / Medium / 21m --- wjsdncl/973_k_closest_points_to_origin.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 wjsdncl/973_k_closest_points_to_origin.js diff --git a/wjsdncl/973_k_closest_points_to_origin.js b/wjsdncl/973_k_closest_points_to_origin.js new file mode 100644 index 0000000..25d780d --- /dev/null +++ b/wjsdncl/973_k_closest_points_to_origin.js @@ -0,0 +1,11 @@ +/** + * 21m 16s 소요 + * + * 시간복잡도: O(n log n) + * + * 1. 배영의 요소를 x^2 + y^2를 기준으로 정렬합니다. + * 2. 가장 k개의 요소를 반환합니다. + */ +var kClosest = function (points, k) { + return points.sort((a, b) => a[0] ** 2 + a[1] ** 2 - (b[0] ** 2 + b[1] ** 2)).slice(0, k); +}; From ad007dcf8da49d51323dc917b24d64c4b0ac638c Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 5 Nov 2024 22:38:51 +0900 Subject: [PATCH 26/46] 155 / Min Stack / Medium / 31m --- wjsdncl/155_min_stack.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 wjsdncl/155_min_stack.js diff --git a/wjsdncl/155_min_stack.js b/wjsdncl/155_min_stack.js new file mode 100644 index 0000000..5d42e87 --- /dev/null +++ b/wjsdncl/155_min_stack.js @@ -0,0 +1,33 @@ +/** + * 31m 11s 소요 + * + * 시간복잡도 : O(1) + * + * 지피티와 함께 풀었습니다 어렵네요 + */ +var MinStack = function () { + this.stack = []; + this.minStack = []; +}; + +MinStack.prototype.push = function (val) { + this.stack.push(val); + if (this.minStack.length === 0 || val <= this.minStack[this.minStack.length - 1]) { + this.minStack.push(val); + } +}; + +MinStack.prototype.pop = function () { + const poppedValue = this.stack.pop(); + if (poppedValue === this.minStack[this.minStack.length - 1]) { + this.minStack.pop(); + } +}; + +MinStack.prototype.top = function () { + return this.stack[this.stack.length - 1]; +}; + +MinStack.prototype.getMin = function () { + return this.minStack[this.minStack.length - 1]; +}; From a4eac3a3d3ad520f9dd6c264fb5afaf8cc85fb22 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 13 Nov 2024 00:33:46 +0900 Subject: [PATCH 27/46] 128 / Longest Consecutive Sequence / Medium / 26m --- wjsdncl/128_longest_consecutive_sequence.js | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 wjsdncl/128_longest_consecutive_sequence.js diff --git a/wjsdncl/128_longest_consecutive_sequence.js b/wjsdncl/128_longest_consecutive_sequence.js new file mode 100644 index 0000000..64d9f25 --- /dev/null +++ b/wjsdncl/128_longest_consecutive_sequence.js @@ -0,0 +1,59 @@ +/** + * 26m13s 소요 + * + * 시간복잡도: O(n) + * + * 배열이 주어졌을 때 연속된 숫자로 이루어진 가장 긴 부분 배열의 길이를 구하는 문제입니다. + * 기존에 풀었던 방식은 O(n log n)이 소요되는데 O(n)으로 줄일 방법을 잘 모르겠어서 풀이를 참고했습니다. + */ +var longestConsecutive = function (nums) { + if (nums.length === 0) return 0; + + // 중복된 숫자를 제거합니다. + let set = new Set(nums); + let max = 1; + + for (let num of set) { + // num - 1이 set에 없다면 num을 시작으로 연속된 숫자들을 찾습니다. + if (!set.has(num - 1)) { + let currentMax = 1; + + // num + 1이 set에 있다면 연속된 숫자를 찾습니다. + while (set.has(num + 1)) { + num++; + currentMax++; + } + + // 최대길이를 구합니다. + max = Math.max(max, currentMax); + } + } + + return max; +}; + +/* +기존 풀이 방법 + +var longestConsecutive = function (nums) { + if (nums.length === 0) return 0; + + 중복된 숫자를 제거하고 오름차순으로 정렬합니다. + const arr = Array.from(new Set(nums)).sort((a, b) => a - b); + let count = 1; + let long = 1; + + for (let i = 0; i < arr.length; i++) { + 현재 숫자가 이전 숫자보다 1 크다면 연속된 숫자입니다. + if (arr[i] === arr[i - 1] + 1) { + count++; + } else { + 연속된 숫자가 아니라면 최대값을 갱신합니다. + long = Math.max(long, count); + count = 1; + } + } + + return Math.max(long, count); +}; +*/ From bae77a2b5eec6c9b28c96fc3b374b496e15ad67e Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 13 Nov 2024 00:34:36 +0900 Subject: [PATCH 28/46] 371 / Sum of Two Integers / Medium / 17m --- wjsdncl/371_sum_of_two_integers.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 wjsdncl/371_sum_of_two_integers.js diff --git a/wjsdncl/371_sum_of_two_integers.js b/wjsdncl/371_sum_of_two_integers.js new file mode 100644 index 0000000..013962e --- /dev/null +++ b/wjsdncl/371_sum_of_two_integers.js @@ -0,0 +1,20 @@ +/** + * 17m41s 소요 + * + * 시간복잡도: O(1) + * + * 두 정수 a, b가 주어졌을 때 두 정수의 합을 +, - 연산자를 사용하지 않고 구하는 문제입니다. + * 어떻게 풀어야할지 모르겠어서 ChatGPT의 도움을 받았습니다. + * 비트 연산자를 사용하여 풀 수 있습니다. + */ +var getSum = function (a, b) { + while (b !== 0) { + // 자리 올림 없이 더한 결과를 저장 + let temp = a ^ b; + // 자리 올림 값을 계산 + b = (a & b) << 1; + // a에 자리 올림 없는 결과를 저장 + a = temp; + } + return a; +}; From 21e3e7ee9573e7a3831b77a93d398509366177bd Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 13 Nov 2024 00:35:04 +0900 Subject: [PATCH 29/46] 208 / Implement Trie (Prefix Tree) / Medium / 24m --- wjsdncl/208_implement_trie.js | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 wjsdncl/208_implement_trie.js diff --git a/wjsdncl/208_implement_trie.js b/wjsdncl/208_implement_trie.js new file mode 100644 index 0000000..b17d3f2 --- /dev/null +++ b/wjsdncl/208_implement_trie.js @@ -0,0 +1,51 @@ +/** + * 24m12s 소요 + * + * 시간복잡도: O(m) (m: 문자열의 길이) + * + * Trie 구조를 처음 접해봐서 풀이와 ChatGPT의 도움을 받았습니다. + * + * Trie 구조를 구현하는 문제입니다. + * Trie 구조는 문자열을 저장하고 검색하는 데 사용되는 트리 자료구조입니다. + */ +var TrieNode = function () { + this.children = {}; + this.isEnd = false; +}; + +var Trie = function () { + this.root = new TrieNode(); +}; + +Trie.prototype.insert = function (word) { + let node = this.root; + for (let char of word) { + if (!node.children[char]) { + node.children[char] = new TrieNode(); + } + node = node.children[char]; + } + node.isEnd = true; +}; + +Trie.prototype.search = function (word) { + let node = this.root; + for (let char of word) { + if (!node.children[char]) { + return false; + } + node = node.children[char]; + } + return node.isEnd; +}; + +Trie.prototype.startsWith = function (prefix) { + let node = this.root; + for (let char of prefix) { + if (!node.children[char]) { + return false; + } + node = node.children[char]; + } + return true; +}; From 9f78d1ad5f63b092ca83e545b61c319ee6edff34 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 13 Nov 2024 00:35:29 +0900 Subject: [PATCH 30/46] 226 / Invert Binary Tree / Easy / 13m --- wjsdncl/226_invert_binary_tree.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 wjsdncl/226_invert_binary_tree.js diff --git a/wjsdncl/226_invert_binary_tree.js b/wjsdncl/226_invert_binary_tree.js new file mode 100644 index 0000000..7b208c7 --- /dev/null +++ b/wjsdncl/226_invert_binary_tree.js @@ -0,0 +1,17 @@ +/** + * 13m23s 소요 + * + * 시간복잡도: O(n) + * + * 이진 트리가 주어졌을 때 이진 트리를 좌우반전한 결과를 반환하는 문제입니다. + */ +var invertTree = function (root) { + if (!root) return null; + + // 왼쪽과 오른쪽을 재귀적으로 바꿔줍니다. + let temp = root.left; + root.left = invertTree(root.right); + root.right = invertTree(temp); + + return root; +}; From 1414bf757d62cafa3713d2956bfb88fd594bdbeb Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 13 Nov 2024 00:36:04 +0900 Subject: [PATCH 31/46] 543 / Diameter of Binary Tree / Easy / 26m --- wjsdncl/543_diameter_of_binary_tree.js | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 wjsdncl/543_diameter_of_binary_tree.js diff --git a/wjsdncl/543_diameter_of_binary_tree.js b/wjsdncl/543_diameter_of_binary_tree.js new file mode 100644 index 0000000..e659af5 --- /dev/null +++ b/wjsdncl/543_diameter_of_binary_tree.js @@ -0,0 +1,29 @@ +/** + * 26m53s 소요 + * + * 시간복잡도: O(n) + * + * 이진 트리가 주어졌을 때 가장 긴 경로의 길이를 구하는 문제입니다. + * + * 시도해보다가 잘 모르겠어서 풀이를 참고해서 풀었습니다. + */ +var diameterOfBinaryTree = function (root) { + let diameter = 0; + + const depth = (node) => { + if (!node) return 0; + + // 왼쪽과 오른쪽의 깊이를 재귀적으로 구합니다. + const left = depth(node.left); + const right = depth(node.right); + + // 가장 긴 경로를 찾습니다. + diameter = Math.max(diameter, left + right); + + return Math.max(left, right) + 1; + }; + + depth(root); + + return diameter; +}; From c92402b277970ed314fd7ed80b5c17281fd19c2e Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 20 Nov 2024 00:18:57 +0900 Subject: [PATCH 32/46] 102 / Binary Tree Level Order Traversal / Medium / 32m --- .../102_binary_tree_level_order_traversal.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 wjsdncl/102_binary_tree_level_order_traversal.js diff --git a/wjsdncl/102_binary_tree_level_order_traversal.js b/wjsdncl/102_binary_tree_level_order_traversal.js new file mode 100644 index 0000000..c99cbcb --- /dev/null +++ b/wjsdncl/102_binary_tree_level_order_traversal.js @@ -0,0 +1,35 @@ +/** + * 32m13s 소요 + * + * 시간복잡도: O(n) + * + * 이진 트리의 노드 값을 레벨 순서대로 반환하는 문제입니다. + * + * 큐를 이용하여 레벨 순서대로 노드 값을 저장하고, 결과를 반환합니다. + * 빈 트리인 경우 빈 배열을 반환합니다. + */ +var levelOrder = function (root) { + if (!root) return []; // 빈 트리인 경우 빈 배열 반환 + + const result = []; + const queue = [root]; // 루트 노드를 큐에 넣는다. + + while (queue.length) { + const level = []; + const len = queue.length; + + // 큐에 있는 노드를 모두 빼내서 level 배열에 넣는다. + for (let i = 0; i < len; i++) { + const node = queue.shift(); + level.push(node.val); + + // 왼쪽 자식 노드와 오른쪽 자식 노드를 큐에 넣는다. + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + + result.push(level); + } + + return result; +}; From baab35405191a7ffc73fbc5e0cedcad685fe4f90 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 20 Nov 2024 00:20:24 +0900 Subject: [PATCH 33/46] 572 / Subtree Of Another Tree / Easy / 23m --- wjsdncl/572_subtree_of_another_tree.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 wjsdncl/572_subtree_of_another_tree.js diff --git a/wjsdncl/572_subtree_of_another_tree.js b/wjsdncl/572_subtree_of_another_tree.js new file mode 100644 index 0000000..c55dbf3 --- /dev/null +++ b/wjsdncl/572_subtree_of_another_tree.js @@ -0,0 +1,24 @@ +/** + * 23m41s 소요 + * + * 시간복잡도: O(n) + * + * 두 이진 트리가 주어졌을 때, 두 번째 트리가 첫 번째 트리의 서브트리인지 확인하는 문제입니다. + * + * 빈 트리인 경우 false를 반환합니다. + * 두 트리가 동일한지 확인하는 함수로 두 트리가 동일하지 않은 경우 왼쪽과 오른쪽 자식을 재귀적으로 비교합니다. + * 두 트리가 동일한 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다. + */ +var isSubtree = function (root, subRoot) { + if (!root) return false; // 빈 트리인 경우 false 반환 + + // 두 트리가 동일한지 확인하는 함수 + const isSameTree = (R, S) => { + if (!R && !S) return true; // 둘 다 null인 경우 두 트리는 동일 + if (!R || !S || R.val !== S.val) return false; // 둘 중 하나가 null이거나 노드의 값이 다르면 트리는 동일하지 않음 + return isSameTree(R.left, S.left) && isSameTree(R.right, S.right); // 현재 노드가 같다면 왼쪽과 오른쪽 자식을 재귀적으로 비교 + }; + + if (isSameTree(root, subRoot)) return true; // 두 트리가 동일한 경우 true 반환 + return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot); // 두 트리가 동일하지 않은 경우 왼쪽과 오른쪽 자식을 재귀적으로 비교 +}; From 10080ca60077d1bd688a2e5757e097b1af9ff786 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 20 Nov 2024 00:21:09 +0900 Subject: [PATCH 34/46] 213 / House Robber II / Medium / 36m --- wjsdncl/213_house_robber_ii.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 wjsdncl/213_house_robber_ii.js diff --git a/wjsdncl/213_house_robber_ii.js b/wjsdncl/213_house_robber_ii.js new file mode 100644 index 0000000..ac892b5 --- /dev/null +++ b/wjsdncl/213_house_robber_ii.js @@ -0,0 +1,29 @@ +/** + * 36m21s 소요 + * + * 시간복잡도: O(n) + * + * 집이 원형으로 배치되어 있고 각 집에는 돈이 들어있습니다. 인접한 두 집을 털면 경보가 울립니다. 경보를 울리지 않고 가장 많은 돈을 훔치는 문제입니다. + * + * 어떻게 풀어야 할지 감이 잡히지 않아서 풀이를 참고했습니다. + */ +var rob = function (nums) { + if (nums.length === 1) return nums[0]; // 집이 한 개인 경우 집을 털고 종료 + + const dp = Array(nums.length).fill(0); // 첫 번째 집을 턴 경우 + const dp2 = Array(nums.length).fill(0); // 첫 번째 집을 털지 않은 경우 + + dp[0] = nums[0]; // 첫 번째 집을 털었을 때의 금액 + dp[1] = Math.max(nums[0], nums[1]); // 첫 번째 혹은 두 번째 집을 털었을 때 최대 금액 + + dp2[0] = 0; // 첫 번째 집을 털지 않으므로 0 + dp2[1] = nums[1]; // 두 번째 집을 털었을 때의 금액 + + for (let i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); // 첫 번째 집을 털었을 때의 최대 금액 + dp2[i] = Math.max(dp2[i - 1], dp2[i - 2] + nums[i]); // 첫 번째 집을 털지 않았을 때의 최대 금액 + } + + // 첫 번째 집을 털었을 때와 첫 번째 집을 털지 않았을 때 중 최대 금액을 반환 + return Math.max(dp[nums.length - 2], dp2[nums.length - 1]); +}; From f13f72f3541152e0e737e13cceb84ef2418e93c3 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 20 Nov 2024 00:25:13 +0900 Subject: [PATCH 35/46] 207 / Course Schedule / Medium / 39m --- wjsdncl/207_course_schedule.js | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 wjsdncl/207_course_schedule.js diff --git a/wjsdncl/207_course_schedule.js b/wjsdncl/207_course_schedule.js new file mode 100644 index 0000000..3263bbb --- /dev/null +++ b/wjsdncl/207_course_schedule.js @@ -0,0 +1,40 @@ +/** + * 39m12s 소요 + * + * 시간 복잡도: O(n + m) (n: numCourses, m: prerequisites.length) + * + * n개의 과목이 있을 때 모든 과목을 수강할 수 있는지 확인하는 문제입니다. + * + * 이것도 어떻게 풀어야 할지 전혀 모르겠어서 풀이랑 ChatGPT에게 도움을 받았습니다. + * 사이클이 존재하는지 확인하는 문제라고 합니다. 사이클이 있으면 모든 과목을 수강할 수 없습니다. + */ +var canFinish = function (numCourses, prerequisites) { + // 그래프 초기화 + const graph = Array.from({ length: numCourses }, () => []); + for (let [a, b] of prerequisites) { + graph[b].push(a); + } + + // 방문 상태 배열 (0: 방문 안 함, 1: 방문 중, 2: 방문 완료) + const visited = new Array(numCourses).fill(0); + + // DFS를 사용하여 사이클 탐지 + const hasCycle = (node) => { + if (visited[node] === 1) return true; // 사이클 발견 + if (visited[node] === 2) return false; // 이미 방문 완료된 노드 + + visited[node] = 1; // 현재 노드 방문 중 + for (let neighbor of graph[node]) { + if (hasCycle(neighbor)) return true; + } + visited[node] = 2; // 방문 완료 + return false; + }; + + // 모든 노드에 대해 DFS 수행 + for (let i = 0; i < numCourses; i++) { + if (hasCycle(i)) return false; // 사이클 발견 시 false 반환 + } + + return true; // 모든 노드 탐색 후 사이클이 없으면 true 반환 +}; From 18d2ae629eb3842a8daa078ac42d4d0094d0054c Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 20 Nov 2024 00:25:57 +0900 Subject: [PATCH 36/46] 150 / Evaluate Reverse Polish Notation / Medium / 19m --- .../150_evaluate_reverse_polish_notation.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 wjsdncl/150_evaluate_reverse_polish_notation.js diff --git a/wjsdncl/150_evaluate_reverse_polish_notation.js b/wjsdncl/150_evaluate_reverse_polish_notation.js new file mode 100644 index 0000000..ea15c9d --- /dev/null +++ b/wjsdncl/150_evaluate_reverse_polish_notation.js @@ -0,0 +1,43 @@ +/** + * 19m26s 소요 + * + * 시간복잡도 : O(n) + * + * 배열로 주어진 reverse polish notation을 계산하는 문제입니다. + * reverse polish notation를 보고 어려운 줄 알았는데 그냥 순서대로 계산만 하면 되는 문제였습니다. + * + * 숫자인 경우 스택에 push하고, 연산자인 경우 스택에서 두 숫자를 pop하여 연산한 결과를 다시 스택에 push합니다. + * 최종적으로 스택에 남아 있는 값을 반환합니다. + */ +var evalRPN = function (tokens) { + const stack = []; + + for (let token of tokens) { + if (!isNaN(token)) { + // 숫자인 경우 스택에 push + stack.push(Number(token)); + } else { + // 연산자인 경우 스택에서 두 숫자를 pop + const b = stack.pop(); + const a = stack.pop(); + + switch (token) { + case "+": + stack.push(a + b); + break; + case "-": + stack.push(a - b); + break; + case "*": + stack.push(a * b); + break; + case "/": + stack.push(parseInt(a / b)); + break; + } + } + } + + // 스택에 남아 있는 최종 결과 반환 + return stack.pop(); +}; From 1d12860ff667ed0f0eec7b8d54211dc38a47f54d Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 3 Dec 2024 20:06:02 +0900 Subject: [PATCH 37/46] 56 / Merge Intervals / medium / 21m --- wjsdncl/56_merge_intervals.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 wjsdncl/56_merge_intervals.js diff --git a/wjsdncl/56_merge_intervals.js b/wjsdncl/56_merge_intervals.js new file mode 100644 index 0000000..4f138f4 --- /dev/null +++ b/wjsdncl/56_merge_intervals.js @@ -0,0 +1,34 @@ +/** + * 21m41s 소요 + * + * 시간복잡도: O(n log n) + * + * 배열의 모든 겹치는 구간을 병합하고 겹치지 않는 구간은 그대로 반환하는 문제입니다. + * + * 시작점을 기준으로 구간을 정렬하고, 결과 배열에 첫 구간을 넣어둡니다. + * 그 다음 구간부터 시작점이 이전 구간의 끝점보다 작거나 같은 경우 두 구간을 병합하고, + * 그렇지 않은 경우 결과 배열에 추가합니다. + */ +var merge = function (intervals) { + if (intervals.length <= 1) return intervals; + + // 시작점 기준으로 구간 정렬 + intervals.sort((a, b) => a[0] - b[0]); + + const result = [intervals[0]]; // 초기 결과 배열 + + for (let i = 1; i < intervals.length; i++) { + const prev = result[result.length - 1]; // 결과 배열의 마지막 구간 + const curr = intervals[i]; // 현재 구간 + + if (prev[1] >= curr[0]) { + // 겹치는 경우 병합 + prev[1] = Math.max(prev[1], curr[1]); + } else { + // 겹치지 않는 경우 결과 배열에 추가 + result.push(curr); + } + } + + return result; +}; From 1a1c75b834a6f4a94ecf76adfed8aacb5b275db3 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 3 Dec 2024 20:07:44 +0900 Subject: [PATCH 38/46] 1584 / Min Cost to Connect All Points / Medium / 38m --- .../1584_min_cost_to_connect_all_points.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 wjsdncl/1584_min_cost_to_connect_all_points.js diff --git a/wjsdncl/1584_min_cost_to_connect_all_points.js b/wjsdncl/1584_min_cost_to_connect_all_points.js new file mode 100644 index 0000000..1f0aad6 --- /dev/null +++ b/wjsdncl/1584_min_cost_to_connect_all_points.js @@ -0,0 +1,50 @@ +/** + * 38m17s 소요 + * + * 시간 복잡도: O(N^2) + * + * 어떻게 풀어야 할지 감이 안와서 풀이와 GPT의 도움을 받았습니다. + * Prim's Algorithm 또는 Kruskal's Algorithm 중 하나를 사용하여 풀 수 있다고 합니다. + * + * 모든 점들을 연결하는 최소 비용을 구하는 문제입니다. + * + * 시작점을 0으로 설정하고, 시작점에서 가장 가까운 점을 선택하여 MST에 추가합니다. + * 이후 추가된 점에서 가장 가까운 점을 선택하여 MST에 추가합니다. + * 이를 반복하여 모든 점을 연결할 때까지 반복합니다. + * 최종적으로 MST에 추가된 비용의 합을 반환합니다. + */ +var minCostConnectPoints = function (points) { + const n = points.length; + const minCost = new Array(n).fill(Infinity); // 최소 비용 배열 + const visited = new Array(n).fill(false); // 방문 여부 + minCost[0] = 0; // 시작점의 비용은 0 + let totalCost = 0; + + // 모든 점을 연결할 때까지 반복 + for (let i = 0; i < n; i++) { + let currMin = Infinity; + let currPoint = -1; + + // 최소 비용으로 연결할 점 선택 + for (let j = 0; j < n; j++) { + if (!visited[j] && minCost[j] < currMin) { + currMin = minCost[j]; + currPoint = j; + } + } + + // 현재 점을 MST에 추가 + visited[currPoint] = true; + totalCost += currMin; + + // 다른 점들의 최소 비용 업데이트 + for (let j = 0; j < n; j++) { + if (!visited[j]) { + const dist = Math.abs(points[currPoint][0] - points[j][0]) + Math.abs(points[currPoint][1] - points[j][1]); + minCost[j] = Math.min(minCost[j], dist); + } + } + } + + return totalCost; +}; From 095541d6dc73aa74f42fc446b857faa5723a944c Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 3 Dec 2024 20:08:13 +0900 Subject: [PATCH 39/46] 235 / Lowest Common Ancestor of a Binary Search Tree / Medium / 28m --- ...common_ancestor_of_a_binary_search_tree.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 wjsdncl/235_lowest_common_ancestor_of_a_binary_search_tree.js diff --git a/wjsdncl/235_lowest_common_ancestor_of_a_binary_search_tree.js b/wjsdncl/235_lowest_common_ancestor_of_a_binary_search_tree.js new file mode 100644 index 0000000..c2aa1c5 --- /dev/null +++ b/wjsdncl/235_lowest_common_ancestor_of_a_binary_search_tree.js @@ -0,0 +1,32 @@ +/** + * 28m41s 소요 + * + * 시간복잡도: O(n) (n: 트리의 높이) + * + * 트리 문제라 어려울 줄 알았는데 생각보다 쉬웠습니다. + * + * 이진 탐색 트리에서 두 노드의 가장 가까운 공통 조상을 찾는 문제입니다. + * + * 현재 노드의 값이 두 노드보다 크면 왼쪽으로 이동하고, + * 현재 노드의 값이 두 노드보다 작으면 오른쪽으로 이동합니다. + * 현재 노드가 두 노드 사이에 있는 경우 현재 노드가 최소 공통 조상입니다. + * + * 최소 공통 조상을 찾지 못한 경우 null을 반환합니다. + */ +var lowestCommonAncestor = function (root, p, q) { + while (root) { + // 현재 노드 값이 두 노드보다 크면 왼쪽으로 이동 + if (root.val > p.val && root.val > q.val) { + root = root.left; + } + // 현재 노드 값이 두 노드보다 작으면 오른쪽으로 이동 + else if (root.val < p.val && root.val < q.val) { + root = root.right; + } + // 현재 노드가 LCA인 경우 + else { + return root; + } + } + return null; // 트리 순회가 끝난 경우 +}; From 04729be560cbf35d36560008844d413212df79d8 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 3 Dec 2024 20:08:43 +0900 Subject: [PATCH 40/46] 139 / Word Break / Medium / 13m --- wjsdncl/139_word_break.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 wjsdncl/139_word_break.js diff --git a/wjsdncl/139_word_break.js b/wjsdncl/139_word_break.js new file mode 100644 index 0000000..5dcb8c9 --- /dev/null +++ b/wjsdncl/139_word_break.js @@ -0,0 +1,33 @@ +/** + * 13m38s 소요 + * + * 시간복잡도: O(n^2) + * + * 단어 s가 주어지고, 단어 사전 wordDict가 주어집니다. + * 단어 s를 단어 사전을 이용하여 구성할 수 있는지 여부를 반환하는 문제입니다. + * + * dp를 사용하여 풀 수 있습니다. + * dp[i]는 s의 0부터 i까지의 부분 문자열이 단어 사전을 이용하여 구성 가능한지 여부를 나타냅니다. + * dp[0]은 빈 문자열이므로 true로 초기화합니다. + * dp[i]를 구하기 위해 dp[j]를 검사합니다. (0 <= j < i) + * dp[j]가 true이고, s의 j부터 i까지의 부분 문자열이 단어 사전에 있는 경우 dp[i]를 true로 설정합니다. + * dp[s.length]를 반환합니다. + */ +var wordBreak = function (s, wordDict) { + const wordSet = new Set(wordDict); // 빠른 검색을 위한 Set 생성 + const dp = new Array(s.length + 1).fill(false); + dp[0] = true; // 빈 문자열은 항상 구성 가능 + + // dp[i]를 구하기 위해 dp[j]를 검사 + for (let i = 1; i <= s.length; i++) { + // dp[j]가 true이고, s의 j부터 i까지의 부분 문자열이 단어 사전에 있는 경우 dp[i]를 true로 설정 + for (let j = 0; j < i; j++) { + if (dp[j] && wordSet.has(s.slice(j, i))) { + dp[i] = true; + break; // 가능한 경우를 찾으면 더 이상 탐색하지 않음 + } + } + } + + return dp[s.length]; +}; From 70b8187eca9e622697907ad545c9d9666cd462c7 Mon Sep 17 00:00:00 2001 From: jmj Date: Tue, 3 Dec 2024 20:09:29 +0900 Subject: [PATCH 41/46] 215 / Kth Largest Element in an Array / Medium / 13m --- .../215_kth_largest_element_in_an_array.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 wjsdncl/215_kth_largest_element_in_an_array.js diff --git a/wjsdncl/215_kth_largest_element_in_an_array.js b/wjsdncl/215_kth_largest_element_in_an_array.js new file mode 100644 index 0000000..a078ebe --- /dev/null +++ b/wjsdncl/215_kth_largest_element_in_an_array.js @@ -0,0 +1,27 @@ +/** + * 13m25s 소요 + * + * 시간복잡도: O(N log K) (N: nums의 길이, K: k) + * + * 배열에서 k번째로 큰 요소를 찾는 문제입니다. + * + * 최소 힙을 사용하여 풀 수 있습니다. + * 배열을 순회하며 힙에 요소를 삽입합니다. + * 힙 크기가 k를 초과하면 가장 작은 값을 제거합니다. + * 힙의 첫 번째 요소가 k번째로 큰 값입니다 + */ +var findKthLargest = function (nums, k) { + const minHeap = new MinPriorityQueue(); // 최소 힙 생성 + + for (let num of nums) { + minHeap.enqueue(num); // 힙에 삽입 + + // 힙 크기가 k를 초과하면 가장 작은 값 제거 + if (minHeap.size() > k) { + minHeap.dequeue(); + } + } + + // 힙의 첫 번째 요소가 k번째로 큰 값 + return minHeap.front().element; +}; From bce2f875cbba2c8caec1ec8b3fcc72a2277df5a3 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 4 Dec 2024 03:08:46 +0900 Subject: [PATCH 42/46] 7 / Reverse Interger / Medium / 14m --- wjsdncl/7_reverse_integer.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 wjsdncl/7_reverse_integer.js diff --git a/wjsdncl/7_reverse_integer.js b/wjsdncl/7_reverse_integer.js new file mode 100644 index 0000000..d80628a --- /dev/null +++ b/wjsdncl/7_reverse_integer.js @@ -0,0 +1,20 @@ +/** + * 14m51s 소요 + * + * 시간복잡도 : O(log n) (n: x의 길이) + * + * 32비트 정수 x를 뒤집어서 반환하는 문제입니다. + * + * 32비트 정수의 범위를 벗어나면 0을 반환합니다. + * 숫자를 문자열로 변환하여 뒤집은 뒤, 다시 숫자로 변환합니다. + * 32비트 정수 범위를 벗어나는지 확인합니다. + * 음수인 경우 부호를 복원합니다. + */ +var reverse = function (x) { + const isNegative = x < 0; // 음수 여부 확인 + const reversed = parseInt(Math.abs(x).toString().split("").reverse().join("")); // 절댓값 뒤집기 + + if (reversed > 2 ** 31 - 1) return 0; // 32비트 정수 범위 확인 + + return isNegative ? -reversed : reversed; // 부호 복원 +}; From 8334eb57ec502f31550110407e92b75206915c9a Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 4 Dec 2024 03:09:38 +0900 Subject: [PATCH 43/46] 743 / Network Delay Time / Medium / 37m --- wjsdncl/743_network_delay_time.js | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 wjsdncl/743_network_delay_time.js diff --git a/wjsdncl/743_network_delay_time.js b/wjsdncl/743_network_delay_time.js new file mode 100644 index 0000000..fdb152a --- /dev/null +++ b/wjsdncl/743_network_delay_time.js @@ -0,0 +1,58 @@ +/** + * 37m26s 소요 + * + * 시간복잡도: O(n^2) + * + * 그래프 형태로 주어진 배열에서 k번 노드에서 모든 노드로 가는 최단 시간을 구하는 문제입니다. + * + * 어떻게 풀어야 할지 감이 잘 안와서 풀이랑 GPT의 도움을 받았습니다. + * 다익스트라 알고리즘을 사용하면 풀 수 있다고 합니다. + * + * 다익스트라 알고리즘은 최단 경로를 찾는 알고리즘 중 하나입니다. + * 시작 노드에서 다른 노드로 가는 최단 경로를 찾을 때 사용합니다. + * + * 그래프를 초기화합니다. + * 다익스트라 알고리즘을 사용하여 최단 경로를 찾습니다. + * 최단 경로 배열 중 최대값을 찾아 반환합니다. + */ +var networkDelayTime = function (times, n, k) { + const graph = new Map(); // 그래프 생성 + + // 그래프 초기화 + for (let i = 1; i <= n; i++) { + graph.set(i, []); + } + for (let [u, v, w] of times) { + graph.get(u).push([v, w]); + } + + // 다익스트라 알고리즘 + const dist = Array(n + 1).fill(Infinity); + dist[k] = 0; + + const pq = new MinPriorityQueue({ priority: (x) => x[1] }); // [노드, 거리] + pq.enqueue([k, 0]); + + // 우선순위 큐에서 노드를 꺼내면서 최단 거리 갱신 + while (!pq.isEmpty()) { + const [current, time] = pq.dequeue().element; + + // 현재 노드의 거리가 최단 거리보다 크면 무시 + if (time > dist[current]) continue; + + // 인접 노드를 순회하며 최단 거리 갱신 + for (let [neighbor, weight] of graph.get(current)) { + const newTime = time + weight; + + // 최단 거리 갱신 + if (newTime < dist[neighbor]) { + dist[neighbor] = newTime; + pq.enqueue([neighbor, newTime]); + } + } + } + + // 최종 거리 배열에서 최대값 확인 + const result = Math.max(...dist.slice(1)); // 1번 노드부터 n번 노드까지 + return result === Infinity ? -1 : result; +}; From 06e5e5eef55cc263225de7943a74546a691b83e1 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 4 Dec 2024 03:10:06 +0900 Subject: [PATCH 44/46] 39 / Combination Sum / Medium / 29m --- wjsdncl/39_combination_sum.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 wjsdncl/39_combination_sum.js diff --git a/wjsdncl/39_combination_sum.js b/wjsdncl/39_combination_sum.js new file mode 100644 index 0000000..df8d073 --- /dev/null +++ b/wjsdncl/39_combination_sum.js @@ -0,0 +1,33 @@ +/** + * 29m41s 소요 + * + * 시간복잡도: O(2^n) + * + * 주어진 숫자 배열에서 합이 target이 되는 모든 조합을 구하는 문제입니다. + * + * 백트래킹을 사용하여 모든 조합을 구합니다. + * 현재 합이 target과 같으면 결과 배열에 추가합니다. + * 현재 합이 target보다 크면 더 이상 탐색하지 않습니다. + * 현재 합이 target보다 작으면 다음 인덱스부터 탐색합니다. + * 탐색이 끝나면 결과 배열을 반환합니다. + */ +var combinationSum = function (candidates, target) { + const result = []; + + const backtrack = (current, remain, start) => { + if (remain === 0) { + result.push([...current]); + return; + } + if (remain < 0) return; + + for (let i = start; i < candidates.length; i++) { + current.push(candidates[i]); + backtrack(current, remain - candidates[i], i); // 같은 위치에서 다시 시작 + current.pop(); // 탐색이 끝난 후 원래 상태로 복구 + } + }; + + backtrack([], target, 0); + return result; +}; From 3b9ae7d485a509ae8b200b55a9ab91125472d268 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 4 Dec 2024 03:10:34 +0900 Subject: [PATCH 45/46] 2013 / Detect Squares / Medium / 51m --- wjsdncl/2013_detect_squares.js | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 wjsdncl/2013_detect_squares.js diff --git a/wjsdncl/2013_detect_squares.js b/wjsdncl/2013_detect_squares.js new file mode 100644 index 0000000..cdf3471 --- /dev/null +++ b/wjsdncl/2013_detect_squares.js @@ -0,0 +1,51 @@ +/** + * 51m18s 소요 + * + * 시간복잡도: O(n) (n: 점의 개수) + * + * 주어진 점들로부터 정사각형을 만들 수 있는 경우의 수를 구하는 문제입니다. + * + * 어떻게 풀어야 할지 감이 전혀 안와서 풀이랑 GPT의 도움을 받았습니다. + * + * 점들의 빈도를 저장하기 위한 맵을 생성합니다. + * 새로운 점을 추가할 때마다 빈도를 증가시킵니다. + * 정사각형을 만들 수 있는 경우의 수를 구합니다. + * 대각선의 조건을 만족하는 점들을 찾습니다. + * 대각선 점을 기준으로 나머지 두 점의 빈도를 곱하여 더합니다. + * 결과를 반환합니다. + */ +var DetectSquares = function () { + // 점들의 빈도를 저장하기 위한 맵 + this.points = new Map(); +}; + +DetectSquares.prototype.add = function (point) { + const [x, y] = point; + const key = `${x},${y}`; + if (!this.points.has(key)) { + this.points.set(key, 0); + } + this.points.set(key, this.points.get(key) + 1); +}; + +DetectSquares.prototype.count = function (point) { + const [px, py] = point; + let count = 0; + + // 모든 점을 탐색하며 정사각형의 가능성을 확인 + for (let [key, freq] of this.points) { + const [x, y] = key.split(",").map(Number); + + // x나 y가 같으면 대각선이 될 수 없으므로 스킵 + if (x === px || y === py) continue; + + // 대각선의 조건: 두 점 간의 x, y 차이가 같아야 함 + if (Math.abs(x - px) === Math.abs(y - py)) { + // 대각선 점을 기준으로 나머지 두 점의 빈도를 곱함 + const point1 = `${x},${py}`; + const point2 = `${px},${y}`; + count += (this.points.get(point1) || 0) * (this.points.get(point2) || 0) * freq; + } + } + return count; +}; From ec2ec3caa49b156b7adeb032a378a8876702d579 Mon Sep 17 00:00:00 2001 From: jmj Date: Wed, 4 Dec 2024 03:10:51 +0900 Subject: [PATCH 46/46] 57 / Insert Interval / Medium / 32m --- wjsdncl/57_Insert_Interval.js | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 wjsdncl/57_Insert_Interval.js diff --git a/wjsdncl/57_Insert_Interval.js b/wjsdncl/57_Insert_Interval.js new file mode 100644 index 0000000..0910d30 --- /dev/null +++ b/wjsdncl/57_Insert_Interval.js @@ -0,0 +1,39 @@ +/** + * 32m31s 소요 + * + * 시간복잡도 : O(n) (n: intervals의 길이) + * + * 구간들의 배열과 새로운 구간이 주어질 때, 구간들을 병합한 결과를 반환하는 문제입니다. + * + * 구간들을 순회하면서 새로운 구간과 겹치는 구간을 병합합니다. + * 새로운 구간이 구간들보다 클 때까지 구간들을 병합합니다. + * 새로운 구간이 구간들보다 작을 때까지 구간들을 병합합니다. + * 나머지 구간들을 결과에 추가합니다. + * 결과를 반환합니다. + */ +var insert = function (intervals, newInterval) { + const result = []; + let i = 0; + + // newInterval 이전의 겹치지 않는 구간 추가 + while (i < intervals.length && intervals[i][1] < newInterval[0]) { + result.push(intervals[i]); + i++; + } + + // newInterval과 겹치는 구간 병합 + while (i < intervals.length && intervals[i][0] <= newInterval[1]) { + newInterval[0] = Math.min(newInterval[0], intervals[i][0]); + newInterval[1] = Math.max(newInterval[1], intervals[i][1]); + i++; + } + result.push(newInterval); // 병합된 구간 추가 + + // newInterval 이후의 겹치지 않는 구간 추가 + while (i < intervals.length) { + result.push(intervals[i]); + i++; + } + + return result; +};