Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[정현준] 4주차 답안 제출 #410

Merged
merged 3 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions longest-consecutive-sequence/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package leetcode_study

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import kotlin.math.max

class `longest-consecutive-sequence` {
fun longestConsecutive(nums: IntArray): Int {
if (nums.isEmpty()) return 0
return usingUnionFind(nums)
}

/**
* 1. 배열을 정렬하여 순서대로 순회하며 연속 수열 길이를 확인한다.
* TC: O(n * log(n)), SC: O(1)
*/
private fun usingSort(nums: IntArray): Int {
nums.sort()

var (length, maxLength) = 1 to 0
for (index in 0 until nums.size - 1) {
if (nums[index] == nums[index + 1]) {
continue
} else if (nums[index] + 1 == nums[index + 1]) {
length++
} else {
maxLength = max(length, maxLength)
length = 1
}
}
return max(length, maxLength)
}

/**
* 2. Set의 자료구조를 활용하여 가장 작은 값부터 while문을 통한 최대 증가 값을 반환한다.
* TC: O(n), SC: O(n)
*/
private fun usingSet(nums: IntArray): Int {
val numberSet = nums.toSet()
var maxLength = 0

for (number in nums) {
if (numberSet.contains(number - 1)) {
continue
}
var length = 1
while (numberSet.contains(number + length)) {
length++
}
maxLength = max(maxLength, length)
Comment on lines +46 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오! 이렇게 접근하는 방법이 있군요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

달래님 풀이를 참고했습니다 ㅎㅎ

}

return maxLength
}

/**
* 3. Union-Find
* TC: O(n), SC: O(n)
*/
private fun usingUnionFind(nums: IntArray): Int {
val nodes = mutableMapOf<Int, Int>()
val dsu = DSU(nums.size)

for ((i,n) in nums.withIndex()) {
if (n in nodes) continue

nodes[n - 1]?.let { dsu.union(i, it) }
nodes[n + 1]?.let { dsu.union(i, it) }

nodes[n] = i
}

return dsu.maxLength()
}

@Test
fun `입력받은 정수 배열의 최대 연속 수열 길이를 반환한다`() {
longestConsecutive(intArrayOf()) shouldBe 0
longestConsecutive(intArrayOf(100,4,200,1,3,2)) shouldBe 4
longestConsecutive(intArrayOf(11,23,12,13,14,21)) shouldBe 4
longestConsecutive(intArrayOf(0,3,7,2,5,8,4,6,0,1)) shouldBe 9
longestConsecutive(intArrayOf(11,64,43,12,13,10,9,8,7)) shouldBe 7
}
}

class DSU(val n: Int) {
private val parent = IntArray(n) { it }
private val size = IntArray(n) { 1 }

private fun find(x: Int): Int {
if (parent[x] != x)
parent[x] = find(parent[x])
return parent[x]
}

fun union(x: Int, y: Int) {
val root = find(x)
val child = find(y)
if(root != child) {
parent[child] = root
size[root] += size[child]
}
}

fun maxLength(): Int {
var res = 0
for (i in parent.indices) {
if (parent[i] == i)
res = maxOf(res, size[i])
}
return res
}
}
56 changes: 56 additions & 0 deletions maximum-product-subarray/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package leetcode_study

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import kotlin.math.max
import kotlin.math.min

class `maximum-product-subarray` {

fun maxProduct(nums: IntArray): Int {
return usingOptimizedDP(nums)
}

/**
* 현재의 값, 이전 위치의 최대 누적곱, 이전 위치의 최소 누적곱 이 세 개를 비교하여 한 번의 순회로 최대 값을 반환한다.
* 음수와 음수가 곱해져 최대 값이 도출될 수 있기에 DP 배열을 두 개 생성한다.
* TC: O(n), SC: O(n)
*/
private fun usingDP(nums: IntArray): Int {
val (min, max) = IntArray(nums.size) { 11 }.apply { this[0] = nums[0] } to IntArray(nums.size) { -11 }.apply { this[0] = nums[0] }
var result = nums[0]
for (index in 1 until nums.size) {
max[index] = max(max(nums[index], nums[index] * max[index - 1]), nums[index] * min[index - 1])
min[index] = min(min(nums[index], nums[index] * max[index - 1]), nums[index] * min[index - 1])
result = max(max(min[index], max[index]), result)
}

return result
}

/**
* DP 배열이 입력받는 정수의 배열만큼 생성할 필요가 없고, 이전 값과 현재 값만 기억하면 되므로 공간복잡도가 개선되었다.
* TC: O(n), SC: O(1)
*/
private fun usingOptimizedDP(nums: IntArray): Int {
var (min, max) = nums[0] to nums[0]
var result = nums[0]
for (index in 1 until nums.size) {
val (tmpMin, tmpMax) = min to max
max = max(max(nums[index], nums[index] * tmpMax), nums[index] * tmpMin)
min = min(min(nums[index], nums[index] * tmpMax), nums[index] * tmpMin)
result = max(max(min, max), result)
}

return result
}

@Test
fun `입력받은 정수 배열의 가장 큰 곱을 반환한다`() {
maxProduct(intArrayOf(2,3,-2,4)) shouldBe 6
maxProduct(intArrayOf(-2,0,-1)) shouldBe 0
maxProduct(intArrayOf(-10)) shouldBe -10
maxProduct(intArrayOf(-2,3,-4)) shouldBe 24
maxProduct(intArrayOf(-4,-3,-2)) shouldBe 12
}
}
41 changes: 41 additions & 0 deletions missing-number/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package leetcode_study

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test

class `missing-number` {

fun missingNumber(nums: IntArray): Int {
return usingSum(nums)
}

/**
* 1. 배열을 추가로 생성하여 존재하는 정수의 인덱스에 true를 저장하여, false인 인덱스를 반환한다.
* TC: O(n), SC: O(n)
*/
private fun usingIndex(nums: IntArray): Int {
val existed = BooleanArray(nums.size + 1)
nums.forEach { existed[it] = true }

existed.forEachIndexed { i, e ->
if (!e) return i
}
return -1
}

/**
* 2. 0부터 정수 배열의 사이즈만큼 정수를 합산하여 기대하는 합산과 뺀 결과를 반환한다.
* TC: O(n), SC: O(1)
*/
private fun usingSum(nums: IntArray): Int {
val size = nums.size
return nums.fold((size + 1) * size / 2) { acc , i -> acc - i }
}

@Test
fun `입력받은 정수 배열에서 비어있는 정수를 반환한다`() {
missingNumber(intArrayOf(3,2,1)) shouldBe 0
missingNumber(intArrayOf(3,0,1)) shouldBe 2
missingNumber(intArrayOf(9,6,4,2,3,5,7,0,1)) shouldBe 8
}
}
44 changes: 44 additions & 0 deletions valid-palindrome/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package leetcode_study

import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test

class `valid-palindrome` {

/**
* 입력받은 문자열의 양 끝부터 투 포인터를 이용하여 비교한다.
* TC: O(n), SC: O(1)
*/
fun isPalindrome(s: String): Boolean {
var (left, right) = 0 to s.length - 1
while (left < right) {
while (left < right && !isAlphabetOrDigit(s[left])) {
left++
}
while (left < right && !isAlphabetOrDigit(s[right])) {
right--
}
if (s[left].lowercaseChar() != s[right].lowercaseChar()) {
return false
}
left++
right--
}
return true
}

private fun isAlphabetOrDigit(c: Char): Boolean = c.isDigit() || (c in 'a' .. 'z') || (c in 'A' .. 'Z')

@Test
fun `입력받은 문자열의 영숫자가 팰린드롬 여부를 반환한다`() {
isPalindrome("A man, a plan, a canal: Panama") shouldBe true
isPalindrome("race a car") shouldBe false
isPalindrome("ㅁaㄹㅁb듐노+_c$#&$%#b*&@!!@a$") shouldBe true
}

@Test
fun `입력받은 문자열이 공백만 존재한다면 참을 반환한다`() {
isPalindrome(" ") shouldBe true
isPalindrome(" ") shouldBe true
}
}
95 changes: 95 additions & 0 deletions word-search/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package leetcode_study

import io.kotest.matchers.shouldBe
import leetcode_study.`word-search`.Position.Companion.MOVES
import org.junit.jupiter.api.Test

class `word-search` {

/**
* 격자에 존재하는 문자를 사용하여 word 를 만들 수 있는지 확인하기 위해 DFS를 통한 visited 배열 백트래킹을 사용하여 해결
* TC: O(너비 * 높이 * 4^word), SC: O(너비 * 높이 * 4^word)
*/
fun exist(board: Array<CharArray>, word: String): Boolean {
return usingBacktracking(board, word)
}

private fun usingBacktracking(board: Array<CharArray>, word: String): Boolean {
fun dfs(board: Array<CharArray>, visited: Array<BooleanArray>, word: String, position: Position, index: Int): Boolean {
if (index == word.length) return true
for (move in MOVES) {
val next = position + move
if (next.isNotOutOfIndexed(board) && !visited[next.x][next.y] && board[next.x][next.y] == word[index]) {
visited[next.x][next.y] = true
if (dfs(board, visited, word, next, index + 1)) return true
visited[next.x][next.y] = false
}
}
return false
}

val visited = Array(board.size) {
BooleanArray(board[0].size)
}

for (x in board.indices) {
for (y in board[x].indices) {
visited[x][y] = true
if (board[x][y] == word[0] && dfs(board, visited, word, Position(x,y), 1)) {
return true
}
visited[x][y] = false
}
}
return false
}

@Test
fun `문자로 구성된 2차원 배열에서 word 문자열 존재 유무를 반환한다`() {
exist(arrayOf(
charArrayOf('A','B','C','E'),
charArrayOf('S','F','C','S'),
charArrayOf('A','D','E','E')
), "ABCCED") shouldBe true
exist(arrayOf(
charArrayOf('A','B','C','E'),
charArrayOf('S','F','C','S'),
charArrayOf('A','D','E','E')
), "SEE") shouldBe true
exist(arrayOf(
charArrayOf('A','B','C','E'),
charArrayOf('S','F','C','S'),
charArrayOf('A','D','E','E')
), "SES") shouldBe false
exist(arrayOf(
charArrayOf('A','B','C','E'),
charArrayOf('S','F','E','S'),
charArrayOf('A','D','E','E')
), "ABCESEEEFS") shouldBe true
exist(arrayOf(
charArrayOf('C','A','A'),
charArrayOf('A','A','A'),
charArrayOf('B','C','D')
), "AAB") shouldBe true
}

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<CharArray>) =
this.x < board.size && this.x >= 0 && this.y < board[0].size && this.y >= 0

companion object {
val MOVES: List<Position> = listOf(
Position(-1, 0),
Position(0, 1),
Position(1, 0),
Position(0, -1),
)
}
}
}