-
Notifications
You must be signed in to change notification settings - Fork 126
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 #816 from EcoFriendlyAppleSu/main
[친환경사과] Week 4
- Loading branch information
Showing
8 changed files
with
374 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,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] | ||
} |
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,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 | ||
} |
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,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] | ||
} |
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,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() | ||
} |
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 | ||
|
||
/* | ||
* 오름차순으로 정렬된 두 노드 리스트를 크기 순서대로 병합하는 문제 | ||
* 기준 노드와 다음 노드를 가리키는 포인터 노드를 상용해 문제 해결 | ||
* 시간 복잡도: 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 | ||
} |
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,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 | ||
} |
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,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 | ||
} |
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,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 | ||
} |