From 03b09536500ee113b3e5b32fc7e3f8e8179b8d2f Mon Sep 17 00:00:00 2001 From: jeongdalma Date: Sun, 11 Aug 2024 18:22:59 +0900 Subject: [PATCH 1/3] =?UTF-8?q?1=EC=A3=BC=EC=B0=A8=203=EB=AC=B8=EC=A0=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contains-duplicate/jdalma.kt | 60 ++++++++++++++++++++++++ number-of-1-bits/jdalma.kt | 42 +++++++++++++++++ top-k-frequent-elements/jdalma.kt | 78 +++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 contains-duplicate/jdalma.kt create mode 100644 number-of-1-bits/jdalma.kt create mode 100644 top-k-frequent-elements/jdalma.kt diff --git a/contains-duplicate/jdalma.kt b/contains-duplicate/jdalma.kt new file mode 100644 index 000000000..01073148e --- /dev/null +++ b/contains-duplicate/jdalma.kt @@ -0,0 +1,60 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test + +class `contains-duplicate`{ + + fun containsDuplicate(nums: IntArray): Boolean { + return third(nums) + } + + // 시간초과 + private fun first(nums: IntArray): Boolean { + nums.forEachIndexed { i, e1 -> + nums.forEachIndexed { j, e2 -> + if (i != j && e1 == e2) { + return true + } + } + } + return false + } + + private fun second(nums: IntArray): Boolean { + nums.sort() // DualPivotQuicksort + for (index in 1 until nums.size) { + val prev = nums[index - 1] + val curr = nums[index] + if (prev == curr) { + return true + } + } + return false + } + + private fun third(nums: IntArray): Boolean { + val set = nums.toSet() + return nums.size != set.size + } + + @Test + fun 동일한_원소가_존재하면_true를_반환한다() { + val nums1 = intArrayOf(1, 2, 3, 1) + val nums2 = intArrayOf(1, 2, 3, 2) + val nums3 = intArrayOf(1, 1, 1, 1, 3, 3, 4, 3, 2, 4, 2) + + containsDuplicate(nums1) shouldBeEqual true + containsDuplicate(nums2) shouldBeEqual true + containsDuplicate(nums3) shouldBeEqual true + } + + @Test + fun 동일한_원소가_존재하지_않으면_false를_반환한다() { + val nums1 = intArrayOf(1, 2, 3, 4) + val nums2 = intArrayOf(1, 5, 7) + + containsDuplicate(nums1) shouldBeEqual false + containsDuplicate(nums2) shouldBeEqual false + } +} diff --git a/number-of-1-bits/jdalma.kt b/number-of-1-bits/jdalma.kt new file mode 100644 index 000000000..9e4d09c49 --- /dev/null +++ b/number-of-1-bits/jdalma.kt @@ -0,0 +1,42 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test + +class `number-of-1-bits` { + + fun hammingWeight(n: Int): Int { + return second(n) + } + + private fun first(n: Int): Int { + var calc = n + var count = 0 + while(calc > 0) { + if (calc % 2 != 0) { + count++ + } + calc /= 2 + } + return count + } + + private fun second(n: Int): Int { + var calc = n + var count = 0 + while (calc > 0) { + if (calc and 1 == 1) { + count ++ + } + calc = calc shr 1 + } + return count + } + + @Test + fun `이진수에서_0이_아닌_성분의_개수를 반환한다`() { + hammingWeight(11) shouldBeEqual 3 + hammingWeight(128) shouldBeEqual 1 + hammingWeight(2147483645) shouldBeEqual 30 + } +} diff --git a/top-k-frequent-elements/jdalma.kt b/top-k-frequent-elements/jdalma.kt new file mode 100644 index 000000000..0bac77d10 --- /dev/null +++ b/top-k-frequent-elements/jdalma.kt @@ -0,0 +1,78 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import java.util.PriorityQueue + +class `top-k-frequent-elements` { + + fun topKFrequent(nums: IntArray, k: Int): IntArray { + return third(nums, k) + } + + // Map 정렬 + private fun first(nums: IntArray, k: Int): IntArray { + val map = mutableMapOf() + + nums.forEach { + map.compute(it) { _, oldValue -> + if (oldValue == null) 1 + else oldValue + 1 + } + } + + return map.entries.sortedByDescending { it.value } + .map { it.key } + .slice(0 until k) + .toIntArray() + } + + // 우선순위 큐 사용 + private fun second(nums: IntArray, k: Int): IntArray { + val map = mutableMapOf() + + nums.forEach { map.put(it, map.getOrDefault(it, 0) + 1) } + + val heap: PriorityQueue> = PriorityQueue> { + v1, v2 -> v2.value.compareTo(v1.value) + }.apply { + this.addAll(map.entries) + } + + return (0 until k).map { heap.poll().key }.toIntArray() + } + + // 이차원배열로 빈번도 저장 + private fun third(nums: IntArray, k: Int): IntArray { + val map = mutableMapOf() + + nums.forEach { map.put(it, map.getOrDefault(it, 0) + 1) } + + val freq = Array>(nums.size + 1) { mutableListOf() } + map.entries.forEach { + val frequency = it.value + freq[frequency].add(it.key) + } + + val result = IntArray(k) + var index = 0 + (freq.size - 1 downTo 0).forEach { i -> + freq[i].forEach { + result[index++] = it + if (index == k) { + return result + } + } + } + + return IntArray(0) + } + + @Test + fun `배열에서_가장_빈도가_높은_K개의_원소를_출력한다`() { + topKFrequent(intArrayOf(1,1,1,2,2,3), 2) shouldBe intArrayOf(1,2) + topKFrequent(intArrayOf(1,1,1,2,2,3,3,4), 3) shouldBe intArrayOf(1,2,3) + topKFrequent(intArrayOf(2,2,3,3,1,1,4), 3) shouldBe intArrayOf(2,3,1) + topKFrequent(intArrayOf(4,1,-1,2,-1,2,3), 2) shouldBe intArrayOf(-1,2) + } +} From 982696632b9f3cc218f837a809e19313e51f0ecd Mon Sep 17 00:00:00 2001 From: jeongdalma Date: Mon, 12 Aug 2024 19:44:11 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EB=B3=B5=EC=9E=A1=EB=8F=84=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=201=EC=A3=BC=EC=B0=A8=204=EB=B2=88=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contains-duplicate/jdalma.kt | 5 +- kth-smallest-element-in-a-bst/jdalma.kt | 98 +++++++++++++++++++++++++ number-of-1-bits/jdalma.kt | 2 + top-k-frequent-elements/jdalma.kt | 6 +- 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 kth-smallest-element-in-a-bst/jdalma.kt diff --git a/contains-duplicate/jdalma.kt b/contains-duplicate/jdalma.kt index 01073148e..2a6b5b1ee 100644 --- a/contains-duplicate/jdalma.kt +++ b/contains-duplicate/jdalma.kt @@ -10,6 +10,7 @@ class `contains-duplicate`{ } // 시간초과 + // 시간복잡도: O(n^2), 공간복잡도: O(1) private fun first(nums: IntArray): Boolean { nums.forEachIndexed { i, e1 -> nums.forEachIndexed { j, e2 -> @@ -21,8 +22,9 @@ class `contains-duplicate`{ return false } + // 시간복잡도: O(n * log(n)), 공간복잡도: O(1) private fun second(nums: IntArray): Boolean { - nums.sort() // DualPivotQuicksort + nums.sort() // DualPivotQuicksort -> O(n log(n)) for (index in 1 until nums.size) { val prev = nums[index - 1] val curr = nums[index] @@ -33,6 +35,7 @@ class `contains-duplicate`{ return false } + // 시간복잡도: O(n), 공간복잡도: O(n) private fun third(nums: IntArray): Boolean { val set = nums.toSet() return nums.size != set.size diff --git a/kth-smallest-element-in-a-bst/jdalma.kt b/kth-smallest-element-in-a-bst/jdalma.kt new file mode 100644 index 000000000..ddc76f37c --- /dev/null +++ b/kth-smallest-element-in-a-bst/jdalma.kt @@ -0,0 +1,98 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test +import java.util.PriorityQueue + +class `kth-smallest-element-in-a-bst` { + + fun kthSmallest(root: TreeNode?, k: Int): Int { + return inorderTraversal(root, k) + } + + // 1. 재귀 호출로 모든 트리를 조회 후 정렬 + // 시간복잡도: O(n * log(n)), 공간복잡도: O(n) + private fun recursionAndSort(root: TreeNode?, k: Int) = mutableSetOf().apply { + dfs(root, this) + }.sorted()[k - 1] + + private fun dfs(node: TreeNode?, set: MutableSet) { + if (node == null) return + + set.add(node.`val`) + dfs(node.left, set) + dfs(node.right, set) + } + + // 2. 재귀 호출로 모든 트리의 값을 우선순위 큐에 삽입하고 작은 값으로 계속 큐를 갱신 + // 시간복잡도: O(n * log(k)), 공간복잡도: O(n + k) + // 트리 순회 : O(n), 우선순위 큐 삽입: O(log k) + private fun usingPriorityQueue(node: TreeNode?, k: Int): Int { + fun dfs(node: TreeNode, k: Int, pq: PriorityQueue) { + pq.offer(node.`val`) + + if (pq.size > k) { + pq.poll() + } + if (node.left != null) { + dfs(node.left!!, k, pq) + } + if (node.right != null) { + dfs(node.right!!, k, pq) + } + } + + val pq = PriorityQueue { v1: Int, v2: Int -> v2 - v1 } + dfs(node!!, k, pq) + return pq.first() + } + + // 3. 문제의 전제가 이진탐색트리이기에, 중위순회로 탐색하여 값을 누적하면 오름차순의 값이 된다. + // 시간복잡도: O(n), 공간복잡도: O(n) + private fun inorderTraversal(node: TreeNode?, k: Int): Int { + fun dfs(node: TreeNode, k: Int, list: MutableList) { + if (node.left != null) { + dfs(node.left!!, k, list) + } + list.add(node.`val`) + if (node.right != null) { + dfs(node.right!!, k, list) + } + } + + return mutableListOf().apply { + dfs(node!!, k, this) + }[k - 1] + } + + @Test + fun `루트와 정수 k가 주어지면 트리에 있는 모든 노드의 값 중 가장 작은 값을 반환한다`() { + kthSmallest(TreeNode.of(listOf(0, 3,1,4,null,2)), 1) shouldBeEqual 1 + kthSmallest(TreeNode.of(listOf(0, 5,3,6,2,4,null,null,1)), 3) shouldBeEqual 3 + } +} + +class TreeNode(var `val`: Int) { + var left: TreeNode? = null + var right: TreeNode? = null + + companion object { + fun of(numbers: List): TreeNode? { + fun setChild(node: TreeNode?, nums: List, index: Int): TreeNode? { + if (node == null) return null + val (leftIndex, rightIndex) = index * 2 to index * 2 + 1 + + if (leftIndex < nums.size && nums[leftIndex] != null) { + node.left = TreeNode(nums[leftIndex]!!) + setChild(node.left, nums, leftIndex) + } + if (rightIndex < nums.size && nums[rightIndex] != null) { + node.right = TreeNode(nums[rightIndex]!!) + setChild(node.right, nums, rightIndex) + } + return node + } + return setChild(TreeNode(numbers[1]!!), numbers, 1) + } + } +} diff --git a/number-of-1-bits/jdalma.kt b/number-of-1-bits/jdalma.kt index 9e4d09c49..22a96ee81 100644 --- a/number-of-1-bits/jdalma.kt +++ b/number-of-1-bits/jdalma.kt @@ -9,6 +9,7 @@ class `number-of-1-bits` { return second(n) } + // 시간복잡도: O(log n), 공간복잡도: O(1) private fun first(n: Int): Int { var calc = n var count = 0 @@ -21,6 +22,7 @@ class `number-of-1-bits` { return count } + // 시간복잡도: O(log n), 공간복잡도: O(1) private fun second(n: Int): Int { var calc = n var count = 0 diff --git a/top-k-frequent-elements/jdalma.kt b/top-k-frequent-elements/jdalma.kt index 0bac77d10..dbf508841 100644 --- a/top-k-frequent-elements/jdalma.kt +++ b/top-k-frequent-elements/jdalma.kt @@ -11,6 +11,7 @@ class `top-k-frequent-elements` { } // Map 정렬 + // 시간복잡도: O(n * log(n)), 공간복잡도: O(n) private fun first(nums: IntArray, k: Int): IntArray { val map = mutableMapOf() @@ -27,7 +28,8 @@ class `top-k-frequent-elements` { .toIntArray() } - // 우선순위 큐 사용 + // 우선순위 큐 + // 시간복잡도: O(n * log(k)), 공간복잡도: O(n + k) private fun second(nums: IntArray, k: Int): IntArray { val map = mutableMapOf() @@ -43,6 +45,7 @@ class `top-k-frequent-elements` { } // 이차원배열로 빈번도 저장 + // 시간복잡도: O(n), 공간복잡도: O(n) private fun third(nums: IntArray, k: Int): IntArray { val map = mutableMapOf() @@ -74,5 +77,6 @@ class `top-k-frequent-elements` { topKFrequent(intArrayOf(1,1,1,2,2,3,3,4), 3) shouldBe intArrayOf(1,2,3) topKFrequent(intArrayOf(2,2,3,3,1,1,4), 3) shouldBe intArrayOf(2,3,1) topKFrequent(intArrayOf(4,1,-1,2,-1,2,3), 2) shouldBe intArrayOf(-1,2) + } } From 4fc89864253f459b21f2ecd27d1d5ab1190fcf57 Mon Sep 17 00:00:00 2001 From: jeongdalma Date: Tue, 13 Aug 2024 20:57:11 +0900 Subject: [PATCH 3/3] =?UTF-8?q?5=EB=B2=88=20=EB=AC=B8=EC=A0=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contains-duplicate/jdalma.kt | 12 +++-- number-of-1-bits/jdalma.kt | 8 +-- palindromic-substrings/jdalma.kt | 90 +++++++++++++++++++++++++++++++ top-k-frequent-elements/jdalma.kt | 6 +-- 4 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 palindromic-substrings/jdalma.kt diff --git a/contains-duplicate/jdalma.kt b/contains-duplicate/jdalma.kt index 2a6b5b1ee..d4684df3f 100644 --- a/contains-duplicate/jdalma.kt +++ b/contains-duplicate/jdalma.kt @@ -6,12 +6,12 @@ import org.junit.jupiter.api.Test class `contains-duplicate`{ fun containsDuplicate(nums: IntArray): Boolean { - return third(nums) + return usingSet(nums) } - // 시간초과 + // 1. 중첩 반복문 - 시간초과 // 시간복잡도: O(n^2), 공간복잡도: O(1) - private fun first(nums: IntArray): Boolean { + private fun usingNestedLoop(nums: IntArray): Boolean { nums.forEachIndexed { i, e1 -> nums.forEachIndexed { j, e2 -> if (i != j && e1 == e2) { @@ -22,8 +22,9 @@ class `contains-duplicate`{ return false } + // 2. 정렬 후 순회 // 시간복잡도: O(n * log(n)), 공간복잡도: O(1) - private fun second(nums: IntArray): Boolean { + private fun usingSort(nums: IntArray): Boolean { nums.sort() // DualPivotQuicksort -> O(n log(n)) for (index in 1 until nums.size) { val prev = nums[index - 1] @@ -35,8 +36,9 @@ class `contains-duplicate`{ return false } + // 3. 자료구조 Set 사용 // 시간복잡도: O(n), 공간복잡도: O(n) - private fun third(nums: IntArray): Boolean { + private fun usingSet(nums: IntArray): Boolean { val set = nums.toSet() return nums.size != set.size } diff --git a/number-of-1-bits/jdalma.kt b/number-of-1-bits/jdalma.kt index 22a96ee81..eea9462fb 100644 --- a/number-of-1-bits/jdalma.kt +++ b/number-of-1-bits/jdalma.kt @@ -6,11 +6,12 @@ import org.junit.jupiter.api.Test class `number-of-1-bits` { fun hammingWeight(n: Int): Int { - return second(n) + return bitOperation(n) } + // 1. 직접 2진수를 구함 // 시간복잡도: O(log n), 공간복잡도: O(1) - private fun first(n: Int): Int { + private fun binary(n: Int): Int { var calc = n var count = 0 while(calc > 0) { @@ -22,8 +23,9 @@ class `number-of-1-bits` { return count } + // 2. 비트 논리 연산자 사용 // 시간복잡도: O(log n), 공간복잡도: O(1) - private fun second(n: Int): Int { + private fun bitOperation(n: Int): Int { var calc = n var count = 0 while (calc > 0) { diff --git a/palindromic-substrings/jdalma.kt b/palindromic-substrings/jdalma.kt new file mode 100644 index 000000000..6aa5200ff --- /dev/null +++ b/palindromic-substrings/jdalma.kt @@ -0,0 +1,90 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test + +class `palindromic-substrings` { + + fun countSubstrings(s: String): Int { + return dp(s) + } + + // 1. 브루트포스 3중 반복문 + // 시간복잡도: O(n^3), 공간복잡도: O(1) + private fun bruteForce(s: String): Int { + + fun isPalindrome(text: String, left: Int, right: Int): Boolean { + var (start, end) = left to right + while (start < end) { + if (text[start] != text[end]) { + return false + } + start++ + end-- + } + return true + } + + var count = 0 + s.indices.forEachIndexed { left, _ -> + (left until s.length).forEach { right -> + if (isPalindrome(s, left, right)) { + count++ + } + } + } + return count + } + + // 2. 문자열의 길이가 홀수,짝수를 감안하여 특정 지점부터 두 개의 포인터 left , right를 비교한다. + // 시간복잡도: O(n^2), 공간복잡도: O(1) + private fun twoPointer(s: String): Int { + + fun palindromeCount(text: String, left: Int, right: Int): Int { + var count = 0 + var (l , r) = left to right + while (l >= 0 && r < text.length && text[l--] == text[r++]) { + count++ + } + return count + } + + var result = 0 + s.indices.forEachIndexed { index, e -> + val even = palindromeCount(s, index, index + 1) + val odd = palindromeCount(s, index - 1, index + 1) + result += even + odd + 1 + } + + return result + } + + // 3. DP : 이전에 비교했던 결과를 기억하여 (현재 주어진 start, end 비교) && (이전 start, end 비교) + // 시간복잡도: O(n^2), 공간복잡도: O(n^2) + private fun dp(s: String): Int { + val len = s.length + val dp = Array(len) { BooleanArray(len) { false } } + + for (end in 0 until len) { + for (start in end downTo 0) { + if (start == end) { + dp[start][end] = true + } else if (start + 1 == end) { + dp[start][end] = s[start] == s[end] + } else { + dp[start][end] = s[start] == s[end] && dp[start + 1][end - 1] + } + } + } + + return dp.sumOf { row -> row.count { it } } + } + + @Test + fun `주어진 문자열의 회문인 부분 문자열의 개수를 반환한다`() { + countSubstrings("abc") shouldBeEqual 3 + countSubstrings("aaa") shouldBeEqual 6 + countSubstrings("ababa") shouldBeEqual 9 + countSubstrings("abcda") shouldBeEqual 5 + } +} diff --git a/top-k-frequent-elements/jdalma.kt b/top-k-frequent-elements/jdalma.kt index dbf508841..4f1e05d49 100644 --- a/top-k-frequent-elements/jdalma.kt +++ b/top-k-frequent-elements/jdalma.kt @@ -10,7 +10,7 @@ class `top-k-frequent-elements` { return third(nums, k) } - // Map 정렬 + // 1. Map 정렬 // 시간복잡도: O(n * log(n)), 공간복잡도: O(n) private fun first(nums: IntArray, k: Int): IntArray { val map = mutableMapOf() @@ -28,7 +28,7 @@ class `top-k-frequent-elements` { .toIntArray() } - // 우선순위 큐 + // 2. 우선순위 큐 // 시간복잡도: O(n * log(k)), 공간복잡도: O(n + k) private fun second(nums: IntArray, k: Int): IntArray { val map = mutableMapOf() @@ -44,7 +44,7 @@ class `top-k-frequent-elements` { return (0 until k).map { heap.poll().key }.toIntArray() } - // 이차원배열로 빈번도 저장 + // 3. 이차원배열로 빈번도 저장 // 시간복잡도: O(n), 공간복잡도: O(n) private fun third(nums: IntArray, k: Int): IntArray { val map = mutableMapOf()