Skip to content

Commit

Permalink
Merge pull request #816 from EcoFriendlyAppleSu/main
Browse files Browse the repository at this point in the history
[친환경사과]  Week 4
  • Loading branch information
EcoFriendlyAppleSu authored Jan 4, 2025
2 parents 09cb2fa + 7b94450 commit 2c52d14
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 0 deletions.
40 changes: 40 additions & 0 deletions coin-change/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package leetcode_study

/*
* 주어진 동전의 종류와 개수를 사용하여 주어진 금액을 만들 때, 중복을 허용한 최소 동전 개수를 구하는 문제
* 너비 우선 탐색을 사용해 문제 해결
* 시간 복잡도: O(n)
* -> queue 자료구조에서 각 동전(n)을 꺼내고 목표 금액(amount == k)까지 도달하는 경우: O(n * k)
* 공간 복잡도: O(k)
* -> 동전 사용 횟수를 저장하는 board의 크기
* */
fun coinChange(coins: IntArray, amount: Int): Int {
if (amount == 0) return 0
if (coins.isEmpty() || coins.any { it <= 0 }) return -1

val board = IntArray(amount + 1) { -1 }
val queue = ArrayDeque<Int>()

for (coin in coins) {
if (coin <= amount) {
queue.add(coin)
board[coin] = 1 // 동전 하나로 구성 가능
}
}

while (queue.isNotEmpty()) {
val currentPosition = queue.pollFirst()
for (coin in coins) {
val nextPosition = currentPosition + coin
if (nextPosition in 1..amount) {
// 아직 방문하지 않았거나 더 적은 동전으로 구성 가능하면 업데이트
if (board[nextPosition] == -1 || board[nextPosition] > board[currentPosition] + 1) {
queue.add(nextPosition)
board[nextPosition] = board[currentPosition] + 1
}
}
}
}

return board[amount]
}
62 changes: 62 additions & 0 deletions combination-sum/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package leetcode_study

/*
* 주어진 배열로 만들 수 있는 값(target)을 구성할 수 있는 모든 조합의 수를 구하는 문제
* 재귀를 사용해 모든 경우의 수를 구한 후 구한 결과값에서 중복을 제거하는 방식으로 문제 해결
* 시간 복잡도: O(2^n)
* -> target 값을 0으로 만들기 위해 가능한 모든 조합을 찾는 과정
* 공간 복잡도: O(2^n)
* -> removeDuplicates는 중복을 제거하고 결과를 저장하는 데 사용됨. 중복을 제외하는 과정에서 O(2^n)개의 리스트 사용
* */
fun combinationSum(candidates: IntArray, target: Int): List<List<Int>> {
val result = mutableListOf<List<Int>>()

fun combination(target: Int, current: List<Int>) {
if (target == 0) {
result.add(current)
return
}
if (target < 0) return

for (candidate in candidates) {
combination(target - candidate, current + candidate)
}
}
combination(target, emptyList())

val removeDuplicates = mutableSetOf<List<Int>>()

for (i in result) {
val temp = i.sorted()
removeDuplicates.add(temp)
}
return removeDuplicates.toList()
}

/*
* 재귀를 사용하여 문제를 해결할 때, 재귀 작성 시 중복을 제거하는 방식으로 문제 해결
* 시간 복잡도: O(2^n)
* -> target 값을 0으로 만들기 위해 가능한 모든 조합을 찾는 과정
* 공간 복잡도: O(n)
* -> 재귀 호출 스택에서 사용하는 공간이 target 값에 비례하기 때문에, 재귀 깊이는 O(n)
* */
fun combinationSumUsingBackTracking(candidates: IntArray, target: Int): List<List<Int>> {
val result = mutableListOf<List<Int>>()

fun combination(target: Int, current: MutableList<Int>, start: Int) {
if (target == 0) {
result.add(current.toList()) // 현재 조합을 결과에 추가
return
}
if (target < 0) return

for (i in start until candidates.size) {
current.add(candidates[i]) // 후보 추가
combination(target - candidates[i], current, i) // 현재 후보를 다시 사용할 수 있음
current.removeAt(current.lastIndex) // 백트래킹
}
}

combination(target, mutableListOf(), 0)
return result
}
83 changes: 83 additions & 0 deletions decode-ways/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package leetcode_study

/*
* 디코딩할 수 있는 경우의 수를 구하는 문제
* 아래 풀이는 구할 수 있는 모든 경우의 수를 구한 뒤 예외 상황을 제외해 답을 구하는 풀이
* 주어진 문자열을 재귀를 통해 나눌 때, 중복의 경우를 모두 계산하기 때문에 "Memory Limit Exceeded" 발생
* 예를 들어, 길이가 50인 문자열에 대해 2^{50} 에 가까운 경우의 수가 생성될 수 있음.
* */
fun numDecodings(s: String): Int {
val alphabetList = mutableListOf<String>()
var answer = 0
for (i in 1..26) {
alphabetList.add(i.toString())
}
val dividedList = lengthDivider(s)

for (dividedElement in dividedList) {
var index = 0
var value = 0
for (each in dividedElement) {
val subString = s.substring(index, index + each)
if (subString.first() != '0' && alphabetList.contains(subString)) {
index += each
value += 1
}
}
if (value == dividedElement.size) {
answer += 1
}
}
return answer
}

fun lengthDivider(s: String): List<List<Int>> {
val result = mutableListOf<List<Int>>()

fun divide(target: Int, current: List<Int>) {
if (target == 0) {
result.add(current)
return
}
if (target < 0) return

divide(target - 1, current + 1)
divide(target - 2, current + 2)
}

divide(s.length, emptyList())
return result
}

/*
* 메모이제이션을 사용한 문제 해결
* 시간 복잡도: O(n)
* -> 주어진 문자열의 길이 만큼 순회
* 공간 복잡도: O(n)
* -> 주어진 길이 만큼 기존 정보를 '메모'할 수 있는 공간
* */
fun numDecodings(s: String): Int {
if (s.isEmpty() || s.first() == '0') return 0 // 빈 문자열 또는 '0'으로 시작하는 경우 디코딩 불가

val n = s.length
val dp = IntArray(n + 1) // dp[i]는 s[0..i-1]까지 디코딩 가능한 경우의 수를 저장
dp[0] = 1 // 빈 문자열은 하나의 방법으로 디코딩 가능
dp[1] = if (s[0] != '0') 1 else 0 // 첫 문자가 '0'이 아니면 한 가지 방법 가능

for (i in 2..n) {
val oneDigit = s.substring(i - 1, i).toInt()
val twoDigits = s.substring(i - 2, i).toInt()

// 한 자리 숫자가 유효한 경우
if (oneDigit in 1..9) {
dp[i] += dp[i - 1]
}

// 두 자리 숫자가 유효한 경우
if (twoDigits in 10..26) {
dp[i] += dp[i - 2]
}
}

return dp[n]
}
22 changes: 22 additions & 0 deletions maximum-subarray/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package leetcode_study

/*
* 주어진 숫자 배열에서 Subarray가 가장 큰 수를 구하는 문제
* 시간 복잡도: O(n)
* -> 주어진 배열만큼 계산
* 공간 복잡도: O(n)
* -> 가중치를 더하는 배열 필요
* */
fun maxSubArray(nums: IntArray): Int {
val dp = IntArray(nums.size)
dp[0] = nums[0]

for (i in 1 until nums.size) {
if (dp[i - 1] + nums[i] >= 0 && dp[i-1] >= 0) {
dp[i] = dp[i - 1] + nums[i]
} else {
dp[i] = nums[i]
}
}
return dp.max()
}
41 changes: 41 additions & 0 deletions merge-two-sorted-lists/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package leetcode_study

/*
* 오름차순으로 정렬된 두 노드 리스트를 크기 순서대로 병합하는 문제
* 기준 노드와 다음 노드를 가리키는 포인터 노드를 상용해 문제 해결
* 시간 복잡도: O(m + n)
* -> 주어진 두 노드 숫자만큼 순회
* 공간 복잡도: O(1)
* */
fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? {
val resultNode = ListNode(0)
var currentNode = resultNode

var firstCurrentNode = list1
var secondCurrentNode = list2

while (firstCurrentNode != null && secondCurrentNode != null) {
if (firstCurrentNode.value <= secondCurrentNode.value) {
currentNode.next = ListNode(firstCurrentNode.value)
firstCurrentNode = firstCurrentNode.next
} else {
currentNode.next = ListNode(secondCurrentNode.value)
secondCurrentNode = secondCurrentNode.next
}
currentNode = currentNode.next!!
}

if (firstCurrentNode != null) {
currentNode.next = firstCurrentNode
} else if(secondCurrentNode != null) {
currentNode.next = secondCurrentNode
}
return resultNode.next
}


class ListNode(
var value: Int,
) {
var next: ListNode? = null
}
26 changes: 26 additions & 0 deletions missing-number/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package leetcode_study

/*
* 주어진 숫자 배열에서 존재하지 않은 수 판별하는 문제
* 0부터 n의 범위에서 모든 수는 유일하게 존재
* 시간 복잡도: O(n)
* -> nums 배열을 순회하며, 각 요소에 대해 board 배열에 값을 설정: O(n)
* -> board 배열을 순회하며, 값이 0인 인덱스 검색: O(n)
* --> O(n) + O(n) = O(n)
* 공간 복잡도: O(n)
* -> 입력 배열 nums의 크기가 n일 때, board 배열의 크기 n + 1: O(n)
* */
fun missingNumber(nums: IntArray): Int {
val size = nums.size
val board = IntArray(size+1)
for (index in nums.indices) {
board[nums[index]] = 1
}

for (i in board.indices) {
if (board[i] == 0) {
return i
}
}
return 0
}
46 changes: 46 additions & 0 deletions palindromic-substrings/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package leetcode_study

/*
* 주어진 문자열에서 자른 개별 문자열이 회문일 경우를 판단하는 문제
* 시간 복잡도: O(n^3)
* -> 주어진 문자열을 자를 때 이중 반복문 수행: O(n^2)
* -> subString() 함수는 내부적으로 주어진 k 만큼 복사된 문자열 객체를 만들기 때문에 O(n) 소요
* --> O(n) * O(n^2) = O(n^3)
* 공간 복잡도: O(n)
* -> subString() 함수가 호출될 때마다 길이 k의 새로운 문자열 객체가 생성되기 때문에 subString 최대 길이인 O(n)을 가짐.
* */
fun countSubstrings(s: String): Int {
var count = 0

val maxLength = s.length
for (startIndex in 0 until maxLength) {
for (endIndex in startIndex + 1..maxLength) {
val temp = s.substring(startIndex, endIndex)
if (temp == temp.reversed()) {
count++
}
}
}
return count
}

/*
* 자른 문자열을 개별적으로 회문인지 판단해야하는데 주어진 문자열에서 포함하는 것으로 문제를 해석하고
* 해결했기 때문에 통과하지 못함
* */
fun countSubstrings01(s: String): Int {
val result = mutableListOf<String>()
val maxLength = s.length
var startIndex = 0
while (startIndex < maxLength) {
val endIndex = startIndex + 1
for (i in endIndex until maxLength + 1) {
val temp = s.substring(startIndex, i)
if (s.contains(temp.reversed())) {
result.add(temp)
}
}
startIndex += 1
}
return result.size
}
54 changes: 54 additions & 0 deletions word-search/EcoFriendlyAppleSu.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package leetcode_study


/*
* dfs로 문제 해결
* 정해진 순서의 단어를 처리하는 부분에서 대부분의 시간 사용
* 시간 복잡도: O(m *n)
* -> 주어진 배열에서 첫 번째 글자를 찾는 반복문: O(m *n)
* -> 최악의 경우 모든 칸을 방문 처리(dfs): O(m *n)
* --> O(m *n) + O(m *n) == O(m *n)
* 공간 복잡도: O(m * n * l)
* -> 방문을 체크하기 위한 공간: O(m * n)
* -> DFS는 단어의 길이 l 만큼 최대 깊이로 호출되므로, 재귀 스택의 공간 복잡도: O(l)
* */
val dx = listOf(0, 1, -1, 0)
val dy = listOf(1, 0, 0, -1)

fun exist(board: Array<CharArray>, word: String): Boolean {
val rowSize = board.size
val colSize = board[0].size
val visited = Array(rowSize) { BooleanArray(colSize) }

// 모든 위치에서 DFS 탐색 시작
for (i in board.indices) {
for (j in board[0].indices) {
if (board[i][j] == word[0] && dfs(board, word, i, j, 0, visited)) {
return true
}
}
}
return false
}

fun dfs(board: Array<CharArray>, word: String, x: Int, y: Int, index: Int, visited: Array<BooleanArray>): Boolean {
// 탐색 종료 조건
if (index == word.length) return true
if (x !in board.indices || y !in board[0].indices || visited[x][y] || board[x][y] != word[index]) return false

// 현재 위치 방문 처리
visited[x][y] = true

// 4방향으로 탐색
for (i in 0..3) {
val nx = x + dx[i]
val ny = y + dy[i]
if (dfs(board, word, nx, ny, index + 1, visited)) {
return true
}
}

// 방문 복구
visited[x][y] = false
return false
}

0 comments on commit 2c52d14

Please sign in to comment.