-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #410 from jdalma/main
[정현준] 4주차 답안 제출
- Loading branch information
Showing
5 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
|
||
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
) | ||
} | ||
} | ||
} |