diff --git a/longest-substring-without-repeating-characters/jdalma.kt b/longest-substring-without-repeating-characters/jdalma.kt new file mode 100644 index 000000000..4a2837e96 --- /dev/null +++ b/longest-substring-without-repeating-characters/jdalma.kt @@ -0,0 +1,46 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max + +class `longest-substring-without-repeating-characters` { + + fun lengthOfLongestSubstring(s: String): Int { + if (s.length <= 1) return s.length + return usingSet(s) + } + + /** + * 1. 사용한 문자를 Set에 저장하여 확인하고, 중복된다면 해당 문자의 위치까지 모든 문자를 제거한다. + * TC: O(n), SC: O(n) + */ + private fun usingSet(s: String): Int { + var left = 0 + val used = mutableSetOf() + var maxLength = 0 + + for (right in s.indices) { + if (!used.contains(s[right])) { + maxLength = max(right - left + 1, maxLength) + used.add(s[right]) + } else { + while (used.contains(s[right])) { + used.remove(s[left]) + left++ + } + used.add(s[right]) + } + } + + return maxLength + } + + @Test + fun `입력받은 문자열의 반복되는 문자가 없는 가장 긴 부분 문자열의 길이를 반환한다`() { + lengthOfLongestSubstring("ababc") shouldBe 3 + lengthOfLongestSubstring("bbbbb") shouldBe 1 + lengthOfLongestSubstring("abcabcbb") shouldBe 3 + lengthOfLongestSubstring("pwwkew") shouldBe 3 + } +} diff --git a/number-of-islands/jdalma.kt b/number-of-islands/jdalma.kt new file mode 100644 index 000000000..48f95f81e --- /dev/null +++ b/number-of-islands/jdalma.kt @@ -0,0 +1,83 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `number-of-islands` { + + private data class Position( + val x: Int, + val y: Int + ) { + + operator fun plus(other: Position) = Position(this.x + other.x, this.y + other.y) + + fun isNotOutOfIndexed(board: Array) = + this.x < board.size && this.x >= 0 && this.y < board[0].size && this.y >= 0 + + companion object { + val MOVES: List = listOf( + Position(-1, 0), + Position(0, 1), + Position(1, 0), + Position(0, -1), + ) + } + } + + /** + * BFS를 사용한 탐색 + * TC: O(n * m), SC: O(n * m) + */ + fun numIslands(grid: Array): Int { + val (row, col) = grid.size to grid.first().size + val visited = Array(row) { BooleanArray(col) } + var result = 0 + + for (x in 0 until row) { + for (y in 0 until col) { + if (!visited[x][y] && grid[x][y] == '1') { + visited[x][y] = true + bfs(x, y, grid, visited) + result++ + } + } + } + return result + } + + private fun bfs(x: Int, y: Int, grid: Array, visited: Array) { + val queue = ArrayDeque().apply { + this.add(Position(x, y)) + } + + while (queue.isNotEmpty()) { + val now = queue.removeFirst() + for (move in Position.MOVES) { + val moved = now + move + if (moved.isNotOutOfIndexed(grid) && grid[moved.x][moved.y] == '1' && !visited[moved.x][moved.y]) { + visited[moved.x][moved.y] = true + queue.add(moved) + } + } + } + } + + + @Test + fun `문자 이차원배열을 입력받으면 1로 이루어진 영역의 수를 반환한다`() { + numIslands(arrayOf( + charArrayOf('1','1','1','1','0'), + charArrayOf('1','1','0','1','0'), + charArrayOf('1','1','0','0','0'), + charArrayOf('0','0','0','0','0') + )) shouldBe 1 + + numIslands(arrayOf( + charArrayOf('1','1','0','0','0'), + charArrayOf('1','1','0','0','0'), + charArrayOf('0','0','1','0','0'), + charArrayOf('0','0','0','1','1') + )) shouldBe 3 + } +} diff --git a/reverse-linked-list/jdalma.kt b/reverse-linked-list/jdalma.kt new file mode 100644 index 000000000..b61e3aad1 --- /dev/null +++ b/reverse-linked-list/jdalma.kt @@ -0,0 +1,36 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `reverse-linked-list` { + + /** + * TC : O(n), SC: O(1) + */ + fun reverseList(head: ListNode?): ListNode? { + var prev: ListNode? = null + var next: ListNode? = null + var curr: ListNode? = head + + while (curr != null) { + next = curr.next + curr.next = prev + prev = curr + curr = next + } + + return prev + } + + @Test + fun `입력 받은 단일 리스트를 반전하고 반전한 목록을 반환한다`() { + reverseList(ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))) + shouldBe(ListNode(5, ListNode(4, ListNode(3, ListNode(2, ListNode(1)))))) + } +} + +class ListNode( + var `val`: Int, + var next: ListNode? = null +) diff --git a/set-matrix-zeroes/jdalma.kt b/set-matrix-zeroes/jdalma.kt new file mode 100644 index 000000000..477969ccc --- /dev/null +++ b/set-matrix-zeroes/jdalma.kt @@ -0,0 +1,111 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import kotlin.math.max + +class `set-matrix-zeroes` { + + private data class Position( + val x: Int, + val y: Int + ) + + fun setZeroes(matrix: Array): Unit { + usingFlag(matrix) + } + + /** + * 0으로 변경해야 할 열과 행을 Set에 담아 처리한다. + * TC: O(n * m) SC: O(n + m) + */ + private fun usingSet(matrix: Array) { + val zeroRows = mutableSetOf() + val zeroCols = mutableSetOf() + for (i in matrix.indices) { + for (j in matrix.first().indices) { + if (matrix[i][j] == 0) { + zeroRows.add(i) + zeroCols.add(j) + } + } + } + + for (row in zeroRows) { + for (col in matrix.first().indices) { + matrix[row][col] = 0 + } + } + + for (col in zeroCols) { + for (row in matrix.indices) { + matrix[row][col] = 0 + } + } + } + + /** + * 0으로 변경해야 할 열과 행을 matrix 0번째 행과 0번째 열 그리고 두 개의 flag로 처리하여 공간복잡도를 개선한다. + * TC: O(n * m) SC: O(1) + */ + private fun usingFlag(matrix: Array) { + var (rowFlag, colFlag) = false to false + for (i in matrix.indices) { + for (j in matrix.first().indices) { + if (matrix[i][j] == 0) { + if (i == 0) rowFlag = true + if (j == 0) colFlag = true + matrix[0][j] = 0 + matrix[i][0] = 0 + } + } + } + + for (i in 1 until matrix.size) { + for (j in 1 until matrix.first().size) { + if (matrix[i][0] == 0 || matrix[0][j] == 0) { + matrix[i][j] = 0 + } + } + } + + if (rowFlag) { + for (i in matrix.first().indices) { + matrix[0][i] = 0 + } + } + + if (colFlag) { + for (element in matrix) { + element[0] = 0 + } + } + } + + @Test + fun `원소가 0이라면 해당 행과 열을 모두 0으로 수정한다`() { + val actual1 = arrayOf( + intArrayOf(1,1,1), + intArrayOf(1,0,1), + intArrayOf(1,1,1) + ) + setZeroes(actual1) + actual1 shouldBe arrayOf( + intArrayOf(1,0,1), + intArrayOf(0,0,0), + intArrayOf(1,0,1) + ) + + val actual2 = arrayOf( + intArrayOf(0,1,2,0), + intArrayOf(3,4,5,2), + intArrayOf(1,3,1,5) + ) + setZeroes(actual2) + actual2 shouldBe arrayOf( + intArrayOf(0,0,0,0), + intArrayOf(0,4,5,0), + intArrayOf(0,3,1,0) + ) + } +} diff --git a/unique-paths/jdalma.kt b/unique-paths/jdalma.kt new file mode 100644 index 000000000..53ecda265 --- /dev/null +++ b/unique-paths/jdalma.kt @@ -0,0 +1,58 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +/** + * 0,0 에서 아래 또는 오른쪽으로만 이동 가능하다. + */ +class `unique-paths` { + + fun uniquePaths(row: Int, col: Int): Int { + return if (row <= 1 || col <= 1) 1 + else usingArray(row, col) + } + + /** + * TC: O(n * m), SC: O(n * m) + */ + private fun usingGrid(row: Int, col: Int): Int { + val grid = Array(row) { IntArray(col) } + (0 until row).forEach { grid[it][0] = 1 } + (0 until col).forEach { grid[0][it] = 1 } + + for (i in 1 until row) { + for (j in 1 until col) { + grid[i][j] = grid[i - 1][j] + grid[i][j - 1] + } + } + + return grid[row - 1][col - 1] + } + + /** + * 이전 라인의 배열만 기억하여도 되므로 공간 복잡도를 아래와 같이 줄일 수 있다. + * TC: O(n * m), SC: O(m) + */ + private fun usingArray(row: Int, col: Int): Int { + var dp = IntArray(col) + + for (i in 0 until row) { + val tmp = IntArray(col) + for (j in 0 until col) { + if (i == 0 && j == 0) tmp[j] = 1 + else if (j > 0) tmp[j] = dp[j] + tmp[j - 1] + else tmp[j] = dp[j] + } + dp = tmp + } + + return dp.last() + } + + @Test + fun `왼쪽 상단 모서리에서 오른쪽 상단 모서리로 도달할 수 있는 고유 경로의 수를 반환한다`() { + uniquePaths(3, 7) shouldBe 28 + uniquePaths(3, 2) shouldBe 3 + } +}