diff --git a/binary-tree-maximum-path-sum/jdalma.kt b/binary-tree-maximum-path-sum/jdalma.kt new file mode 100644 index 000000000..da537bceb --- /dev/null +++ b/binary-tree-maximum-path-sum/jdalma.kt @@ -0,0 +1,35 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max + +class `binary-tree-maximum-path-sum` { + + /** + * TC: O(n), SC: O(log n) + */ + fun maxPathSum(root: TreeNode?): Int { + if (root == null) return 0 + var max = root.`val` // 부모 노드와 2개의 자식 노드의 합을 전역 변수로 갱신한다. + + fun dfs(node: TreeNode?): Int { + if (node == null) return 0 + + val left = max(dfs(node.left), 0) + val right = max(dfs(node.right), 0) + + max = max(node.`val` + left + right, max) + return node.`val` + max(left, right) // 현재 노드와 2개의 자식 노드 중 최대의 값을 반환한다. + } + + dfs(root) + return max + } + + @Test + fun `이진 트리의 최대 경로 합을 반환한다`() { + maxPathSum(TreeNode.of(-10,9,20,null,null,15,7)) shouldBe 42 + maxPathSum(TreeNode.of(1,9,20,null,null,15,7)) shouldBe 45 + } +} diff --git a/graph-valid-tree/jdalma.kt b/graph-valid-tree/jdalma.kt new file mode 100644 index 000000000..bec0fb853 --- /dev/null +++ b/graph-valid-tree/jdalma.kt @@ -0,0 +1,61 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `graph-valid-tree` { + + /** + * TC: O(n), SC: O(n) + */ + fun validTree(nodeSize: Int, edges: Array): Boolean { + if (nodeSize - 1 != edges.size) { + return false + } + + val visited = mutableSetOf() + val adj = List(nodeSize) { mutableListOf() } + for (e in edges) { + adj[e[0]].add(e[1]) + adj[e[1]].add(e[0]) + } + + val queue = ArrayDeque().apply { + this.add(0) + } + + while (queue.isNotEmpty()) { + val now = queue.removeFirst() + visited.add(now) + for (next in adj[now]) { + if (!visited.contains(next)) { + queue.add(next) + } + } + } + + return nodeSize == visited.size + } + + @Test + fun `노드가 트리의 조건을 만족하는지 여부를 반환한다`() { + validTree(5, + arrayOf( + intArrayOf(0,1), + intArrayOf(0,2), + intArrayOf(0,3), + intArrayOf(1,4) + ) + ) shouldBe true + + validTree(5, + arrayOf( + intArrayOf(0,1), + intArrayOf(0,2), + intArrayOf(0,3), + intArrayOf(1,4), + intArrayOf(2,3), + ) + ) shouldBe false + } +} diff --git a/insert-interval/jdalma.kt b/insert-interval/jdalma.kt new file mode 100644 index 000000000..59797d598 --- /dev/null +++ b/insert-interval/jdalma.kt @@ -0,0 +1,59 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max +import kotlin.math.min + +class `insert-interval` { + + /** + * TC: O(n), SC: O(n) + */ + fun insert(intervals: Array, newInterval: IntArray): Array { + if (intervals.isEmpty()) return arrayOf(newInterval) + return justIterate(intervals, newInterval) + } + + private fun justIterate(intervals: Array, newInterval: IntArray): Array { + val result = mutableListOf() + var new: IntArray? = newInterval + + for (interval in intervals) { + if (new != null) { + if (new[1] < interval[0]) { + // new 범위가 더 앞에 있다 + result.add(new) + result.add(interval) + new = null + } else if (new[0] > interval[1]) { + // new 범위가 더 뒤에 있어 다른 범위에 포함될 가능성이 있음 + result.add(interval) + } else { + new[0] = min(new[0], interval[0]) + new[1] = max(new[1], interval[1]) + } + } else { + result.add(interval) + } + } + if (new != null) { + result.add(new) + } + return result.toTypedArray() + } + + @Test + fun name() { + insert( + arrayOf( + intArrayOf(1,2), + intArrayOf(3,5), + intArrayOf(6,7), + intArrayOf(8,10), + intArrayOf(12,16) + ), + intArrayOf(4,8) + ) shouldBe arrayOf(intArrayOf(1,2), intArrayOf(3,10), intArrayOf(12,16)) + } +} diff --git a/maximum-depth-of-binary-tree/jdalma.kt b/maximum-depth-of-binary-tree/jdalma.kt new file mode 100644 index 000000000..e3f1a9695 --- /dev/null +++ b/maximum-depth-of-binary-tree/jdalma.kt @@ -0,0 +1,22 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max + +class `maximum-depth-of-binary-tree` { + + /** + * 이진 트리이기에 스택의 깊이가 차지하는 공간 복잡도는 log일 것 + * TC: O(n), SC: O(log n) + */ + fun maxDepth(root: TreeNode?): Int { + return if (root == null) 0 + else max(maxDepth(root.left) + 1, maxDepth(root.right) + 1) + } + + @Test + fun `노드의 최대 깊이를 반환한다`() { + maxDepth(TreeNode.of(3,9,20,null,null,15,7)) shouldBe 3 + } +} diff --git a/reorder-list/jdalma.kt b/reorder-list/jdalma.kt new file mode 100644 index 000000000..5cb8c7e63 --- /dev/null +++ b/reorder-list/jdalma.kt @@ -0,0 +1,98 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `reorder-list` { + + fun reorderList(root: ListNode?) { + if (root == null) return + usingTwoPointers(root) + } + + /** + * TC: O(n), SC: O(n) + */ + private fun usingStack(input: ListNode) { + val tail = ArrayDeque().apply { + var node: ListNode? = input + while (node != null) { + this.add(node) + node = node.next + } + } + + val dummy = ListNode(-1) + var node: ListNode = dummy + var head: ListNode = input + for (i in 0 until tail.size) { + if (i % 2 != 0) { + node.next = tail.removeLast() + } else { + node.next = head + head.next?.let { head = it } + } + node.next?.let { node = it } + } + node.next = null + } + + /** + * TC: O(n), SC: O(1) + */ + private fun usingTwoPointers(input: ListNode) { + if (input.next == null) return + + var slow: ListNode? = input + var fast: ListNode? = input + while (fast?.next != null && fast.next?.next != null) { + slow = slow?.next + fast = fast.next?.next + } + + val firstHalfEnd = slow + var secondHalfStart = slow?.next + firstHalfEnd?.next = null + + secondHalfStart = reverse(secondHalfStart) + + var node1: ListNode? = input + var node2: ListNode? = secondHalfStart + while (node2 != null) { + val (next1, next2) = (node1?.next to node2?.next) + node1?.next = node2 + node2.next = next1 + node1 = next1 + node2 = next2 + } + } + + private fun reverse(head: ListNode?): ListNode? { + var prev: ListNode? = null + var current = head + while (current != null) { + val next = current.next + current.next = prev + prev = current + current = next + } + return prev + } + + @Test + fun `정렬된 리스트 노드의 참조 체이닝을 특정 순서로 재정렬한다`() { + val actual = ListNode.of(1,2,3,4,5).apply { + reorderList(this) + } + actual.`val` shouldBe 1 + actual.next!!.`val` shouldBe 5 + actual.next!!.next!!.`val` shouldBe 2 + actual.next!!.next!!.next!!.`val` shouldBe 4 + actual.next!!.next!!.next!!.next!!.`val` shouldBe 3 + actual.next!!.next!!.next!!.next!!.next shouldBe null + + ListNode.of(1,2,3,4,5,6).apply { + reorderList(this) + } + } +}