diff --git a/maximum-subarray/jdalma.kt b/maximum-subarray/jdalma.kt new file mode 100644 index 000000000..1d9517cbc --- /dev/null +++ b/maximum-subarray/jdalma.kt @@ -0,0 +1,48 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max + +class `maximum-subarray` { + + fun maxSubArray(nums: IntArray): Int { + return usingDP(nums) + } + + /** + * TC: O(n), SC: O(n) + */ + private fun usingDP(nums: IntArray): Int { + val dp = IntArray(nums.size).apply { + this[0] = nums[0] + } + + for (index in 1 until nums.size) { + dp[index] = max(nums[index], nums[index] + dp[index - 1]) + } + + return dp.max() + } + + /** + * TC: O(n), SC: O(1) + */ + private fun usingKadane(nums: IntArray): Int { + var (current, max) = nums[0] to nums[0] + + for (index in 1 until nums.size) { + current = max(nums[index], current + nums[index]) + max = max(max, current) + } + + return max + } + + @Test + fun `정수 배열의 하위 배열 중 가장 큰 합을 반환한다`() { + maxSubArray(intArrayOf(-2,1,-3,4,-1,2,1,-5,4)) shouldBe 6 + maxSubArray(intArrayOf(1)) shouldBe 1 + maxSubArray(intArrayOf(5,4,-1,7,8)) shouldBe 23 + } +} diff --git a/number-of-connected-components-in-an-undirected-graph/jdalma.kt b/number-of-connected-components-in-an-undirected-graph/jdalma.kt new file mode 100644 index 000000000..1a113424f --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/jdalma.kt @@ -0,0 +1,63 @@ +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +public class CountConnectedComponents { + + public int countComponents(int n, int[][] edges) { + final int[] relation = new int[n]; + for (int i = 0; i < n ; i++) { + relation[i] = i; + } + + int result = n; + for (int[] edge : edges) { + if (union(relation, edge) == 1) { + result--; + } + } + + return result; + } + + private int union(int[] relation, int[] edge) { + int parent1 = find(relation, edge[0]); + int parent2 = find(relation, edge[1]); + + if (parent1 == parent2) { + return 0; + } + + if (parent1 < parent2) { + relation[parent2] = parent1; + } else { + relation[parent1] = parent2; + } + return 1; + } + + private int find(int[] relation, int node) { + int result = node; + while (relation[result] != result) { + relation[result] = relation[relation[result]]; + result = relation[result]; + } + return result; + } + + @Test + @DisplayName("입력받은 노드와 간선을 통해 그래프의 개수를 반환한다") + void graphCount() { + int actual = countComponents(3, new int[][]{ {0,1}, {0,2} }); + Assertions.assertThat(actual).isEqualTo(1); + + int actual1 = countComponents(6, new int[][]{ {0,1}, {1,2}, {2,3}, {4,5} }); + Assertions.assertThat(actual1).isEqualTo(2); + + int actual2 = countComponents(6, new int[][]{ {0,1}, {2,3}, {4,5}, {1,2}, {3,4} }); + Assertions.assertThat(actual2).isEqualTo(1); + } +} diff --git a/remove-nth-node-from-end-of-list/jdalma.kt b/remove-nth-node-from-end-of-list/jdalma.kt new file mode 100644 index 000000000..533be8797 --- /dev/null +++ b/remove-nth-node-from-end-of-list/jdalma.kt @@ -0,0 +1,82 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `remove-nth-node-from-end-of-list` { + + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + if (head == null || (head.next == null && n == 1)) { + return null + } + return usingTwoPointers(head, n) + } + + /** + * TC: O(n), SC: O(n) + */ + private fun usingExtraList(head: ListNode, n: Int): ListNode { + + fun toList(node: ListNode): List { + val list = mutableListOf() + var tmp: ListNode? = node + while (tmp != null) { + list.add(tmp) + tmp = tmp.next + } + return list + } + + val list = toList(head) + var root = head + if (list.size == n) { + root = list[1] + } else { + list[list.size - n - 1].next = list[list.size - n].next + } + return root + } + + /** + * fast를 n만큼 먼저 이동 시킨 후 fast.next가 null일 때까지 fast와 slow를 다음 노드로 이동시킨다. + * fast를 n만큼 이동시키면 결국 slow는 n의 이전 노드에 도달하게 되기에 해당 slow 노드의 next를 변경하면 된다. + * TC: O(n), SC: O(1) + */ + private fun usingTwoPointers(head: ListNode, n: Int): ListNode? { + var fast: ListNode? = head + var slow: ListNode? = head + + repeat(n) { + fast = fast?.next + } + if (fast == null) return head.next + + while (fast?.next != null) { + fast = fast?.next + slow = slow?.next + } + slow?.next = slow?.next?.next + return head + } + + @Test + fun `링크된 목록의 헤드가 주어지면 목록의 끝에서 n번째 노드를 제거하고 그 헤드를 반환합니다`() { + removeNthFromEnd(ListNode.of(1), 1).also { + it shouldBe null + } + + removeNthFromEnd(ListNode.of(1, 2), 2).also { + it shouldBe ListNode(2) + } + + removeNthFromEnd(ListNode.of(1, 2), 1)!!.also { + it shouldBe ListNode(1) + it.next shouldBe null + } + + removeNthFromEnd(ListNode.of(1, 2, 3, 4, 5), 2)!!.also { + it.next!!.next!!.`val` shouldBe 3 + it.next!!.next!!.next!!.`val` shouldBe 5 + } + } +} diff --git a/same-tree/jdalma.kt b/same-tree/jdalma.kt new file mode 100644 index 000000000..7ad05907b --- /dev/null +++ b/same-tree/jdalma.kt @@ -0,0 +1,35 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `same-tree` { + + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + return if (p == null && q == null) { + true + } else if (p == null || q == null || p.`val` != q.`val`) { + false + } else { + isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } + } + + @Test + fun `두 개의 트리의 동등성을 반환한다`() { + isSameTree( + TreeNode.of(1,1,2), + TreeNode.of(1,1,2) + ) shouldBe true + + isSameTree( + TreeNode.of(1,1,2), + TreeNode.of(1,1,2,3) + ) shouldBe false + + isSameTree( + TreeNode.of(1,1,2), + TreeNode.of(1,1,3) + ) shouldBe false + } +} diff --git a/serialize-and-deserialize-binary-tree/jdalma.kt b/serialize-and-deserialize-binary-tree/jdalma.kt new file mode 100644 index 000000000..b07f63ec9 --- /dev/null +++ b/serialize-and-deserialize-binary-tree/jdalma.kt @@ -0,0 +1,69 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import java.util.StringJoiner + +/** + * 문제의 핵심은 트리를 탐색하는 순서 기준으로 직렬화된 문자열을 반대로 풀어내는 것 + * 직렬화를 DFS로 해결하면 역직렬화시 선입선출 방식으로 해결하여야 하고, + * 직렬화를 BFS로 해결하면 역직렬화시 힙의 인덱스 규칙을 이용하여 해결할 수 있을 듯 + */ +class `serialize-and-deserialize-binary-tree` { + + private val empty = "X" + private val delimiter = "|" + + /** + * DFS로 탐색하면서 노드의 값을 누적한다. + * TC: O(n), SC: O(n) + */ + fun serialize(root: TreeNode?): String { + if (root == null) return "" + + val joiner = StringJoiner("|") + nodeToString(root, joiner) + return joiner.toString() + } + + private fun nodeToString(node: TreeNode?, joiner: StringJoiner) { + if (node == null) { + joiner.add(empty) + } else { + joiner.add(node.`val`.toString()) + nodeToString(node.left, joiner) + nodeToString(node.right, joiner) + } + } + + /** + * 깊이 탐색으로 누적된 문자열을 선입선출로 꺼내어 노드를 생성한다. + * TC: O(n), SC: O(n) + */ + fun deserialize(data: String): TreeNode? { + if (data.isEmpty()) return null + return stringToNode(ArrayDeque(data.split(delimiter))) + } + + private fun stringToNode(queue: ArrayDeque): TreeNode? { + val value = queue.removeFirst() + return if (value == empty) { + null + } else { + val node = TreeNode(value.toInt()) + node.left = stringToNode(queue) + node.right = stringToNode(queue) + node + } + } + + @Test + fun `트리 노드를 직렬화한다`() { + serialize(TreeNode.of(1,2,3,null,null,4,5)) shouldBe "1|2|X|X|3|4|X|X|5|X|X" + } + + @Test + fun `문자열을 트리 노드로 역직렬화한다`() { + deserialize("1|2|X|X|3|4|X|X|5|X|X") shouldBe TreeNode.of(1,2,3,null,null,4,5) + } +}