Skip to content

Commit 2c52d14

Browse files
Merge pull request #816 from EcoFriendlyAppleSu/main
[친환경사과] Week 4
2 parents 09cb2fa + 7b94450 commit 2c52d14

File tree

8 files changed

+374
-0
lines changed

8 files changed

+374
-0
lines changed

coin-change/EcoFriendlyAppleSu.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package leetcode_study
2+
3+
/*
4+
* 주어진 동전의 종류와 개수를 사용하여 주어진 금액을 만들 때, 중복을 허용한 최소 동전 개수를 구하는 문제
5+
* 너비 우선 탐색을 사용해 문제 해결
6+
* 시간 복잡도: O(n)
7+
* -> queue 자료구조에서 각 동전(n)을 꺼내고 목표 금액(amount == k)까지 도달하는 경우: O(n * k)
8+
* 공간 복잡도: O(k)
9+
* -> 동전 사용 횟수를 저장하는 board의 크기
10+
* */
11+
fun coinChange(coins: IntArray, amount: Int): Int {
12+
if (amount == 0) return 0
13+
if (coins.isEmpty() || coins.any { it <= 0 }) return -1
14+
15+
val board = IntArray(amount + 1) { -1 }
16+
val queue = ArrayDeque<Int>()
17+
18+
for (coin in coins) {
19+
if (coin <= amount) {
20+
queue.add(coin)
21+
board[coin] = 1 // 동전 하나로 구성 가능
22+
}
23+
}
24+
25+
while (queue.isNotEmpty()) {
26+
val currentPosition = queue.pollFirst()
27+
for (coin in coins) {
28+
val nextPosition = currentPosition + coin
29+
if (nextPosition in 1..amount) {
30+
// 아직 방문하지 않았거나 더 적은 동전으로 구성 가능하면 업데이트
31+
if (board[nextPosition] == -1 || board[nextPosition] > board[currentPosition] + 1) {
32+
queue.add(nextPosition)
33+
board[nextPosition] = board[currentPosition] + 1
34+
}
35+
}
36+
}
37+
}
38+
39+
return board[amount]
40+
}

combination-sum/EcoFriendlyAppleSu.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package leetcode_study
2+
3+
/*
4+
* 주어진 배열로 만들 수 있는 값(target)을 구성할 수 있는 모든 조합의 수를 구하는 문제
5+
* 재귀를 사용해 모든 경우의 수를 구한 후 구한 결과값에서 중복을 제거하는 방식으로 문제 해결
6+
* 시간 복잡도: O(2^n)
7+
* -> target 값을 0으로 만들기 위해 가능한 모든 조합을 찾는 과정
8+
* 공간 복잡도: O(2^n)
9+
* -> removeDuplicates는 중복을 제거하고 결과를 저장하는 데 사용됨. 중복을 제외하는 과정에서 O(2^n)개의 리스트 사용
10+
* */
11+
fun combinationSum(candidates: IntArray, target: Int): List<List<Int>> {
12+
val result = mutableListOf<List<Int>>()
13+
14+
fun combination(target: Int, current: List<Int>) {
15+
if (target == 0) {
16+
result.add(current)
17+
return
18+
}
19+
if (target < 0) return
20+
21+
for (candidate in candidates) {
22+
combination(target - candidate, current + candidate)
23+
}
24+
}
25+
combination(target, emptyList())
26+
27+
val removeDuplicates = mutableSetOf<List<Int>>()
28+
29+
for (i in result) {
30+
val temp = i.sorted()
31+
removeDuplicates.add(temp)
32+
}
33+
return removeDuplicates.toList()
34+
}
35+
36+
/*
37+
* 재귀를 사용하여 문제를 해결할 때, 재귀 작성 시 중복을 제거하는 방식으로 문제 해결
38+
* 시간 복잡도: O(2^n)
39+
* -> target 값을 0으로 만들기 위해 가능한 모든 조합을 찾는 과정
40+
* 공간 복잡도: O(n)
41+
* -> 재귀 호출 스택에서 사용하는 공간이 target 값에 비례하기 때문에, 재귀 깊이는 O(n)
42+
* */
43+
fun combinationSumUsingBackTracking(candidates: IntArray, target: Int): List<List<Int>> {
44+
val result = mutableListOf<List<Int>>()
45+
46+
fun combination(target: Int, current: MutableList<Int>, start: Int) {
47+
if (target == 0) {
48+
result.add(current.toList()) // 현재 조합을 결과에 추가
49+
return
50+
}
51+
if (target < 0) return
52+
53+
for (i in start until candidates.size) {
54+
current.add(candidates[i]) // 후보 추가
55+
combination(target - candidates[i], current, i) // 현재 후보를 다시 사용할 수 있음
56+
current.removeAt(current.lastIndex) // 백트래킹
57+
}
58+
}
59+
60+
combination(target, mutableListOf(), 0)
61+
return result
62+
}

decode-ways/EcoFriendlyAppleSu.kt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package leetcode_study
2+
3+
/*
4+
* 디코딩할 수 있는 경우의 수를 구하는 문제
5+
* 아래 풀이는 구할 수 있는 모든 경우의 수를 구한 뒤 예외 상황을 제외해 답을 구하는 풀이
6+
* 주어진 문자열을 재귀를 통해 나눌 때, 중복의 경우를 모두 계산하기 때문에 "Memory Limit Exceeded" 발생
7+
* 예를 들어, 길이가 50인 문자열에 대해 2^{50} 에 가까운 경우의 수가 생성될 수 있음.
8+
* */
9+
fun numDecodings(s: String): Int {
10+
val alphabetList = mutableListOf<String>()
11+
var answer = 0
12+
for (i in 1..26) {
13+
alphabetList.add(i.toString())
14+
}
15+
val dividedList = lengthDivider(s)
16+
17+
for (dividedElement in dividedList) {
18+
var index = 0
19+
var value = 0
20+
for (each in dividedElement) {
21+
val subString = s.substring(index, index + each)
22+
if (subString.first() != '0' && alphabetList.contains(subString)) {
23+
index += each
24+
value += 1
25+
}
26+
}
27+
if (value == dividedElement.size) {
28+
answer += 1
29+
}
30+
}
31+
return answer
32+
}
33+
34+
fun lengthDivider(s: String): List<List<Int>> {
35+
val result = mutableListOf<List<Int>>()
36+
37+
fun divide(target: Int, current: List<Int>) {
38+
if (target == 0) {
39+
result.add(current)
40+
return
41+
}
42+
if (target < 0) return
43+
44+
divide(target - 1, current + 1)
45+
divide(target - 2, current + 2)
46+
}
47+
48+
divide(s.length, emptyList())
49+
return result
50+
}
51+
52+
/*
53+
* 메모이제이션을 사용한 문제 해결
54+
* 시간 복잡도: O(n)
55+
* -> 주어진 문자열의 길이 만큼 순회
56+
* 공간 복잡도: O(n)
57+
* -> 주어진 길이 만큼 기존 정보를 '메모'할 수 있는 공간
58+
* */
59+
fun numDecodings(s: String): Int {
60+
if (s.isEmpty() || s.first() == '0') return 0 // 빈 문자열 또는 '0'으로 시작하는 경우 디코딩 불가
61+
62+
val n = s.length
63+
val dp = IntArray(n + 1) // dp[i]는 s[0..i-1]까지 디코딩 가능한 경우의 수를 저장
64+
dp[0] = 1 // 빈 문자열은 하나의 방법으로 디코딩 가능
65+
dp[1] = if (s[0] != '0') 1 else 0 // 첫 문자가 '0'이 아니면 한 가지 방법 가능
66+
67+
for (i in 2..n) {
68+
val oneDigit = s.substring(i - 1, i).toInt()
69+
val twoDigits = s.substring(i - 2, i).toInt()
70+
71+
// 한 자리 숫자가 유효한 경우
72+
if (oneDigit in 1..9) {
73+
dp[i] += dp[i - 1]
74+
}
75+
76+
// 두 자리 숫자가 유효한 경우
77+
if (twoDigits in 10..26) {
78+
dp[i] += dp[i - 2]
79+
}
80+
}
81+
82+
return dp[n]
83+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package leetcode_study
2+
3+
/*
4+
* 주어진 숫자 배열에서 Subarray가 가장 큰 수를 구하는 문제
5+
* 시간 복잡도: O(n)
6+
* -> 주어진 배열만큼 계산
7+
* 공간 복잡도: O(n)
8+
* -> 가중치를 더하는 배열 필요
9+
* */
10+
fun maxSubArray(nums: IntArray): Int {
11+
val dp = IntArray(nums.size)
12+
dp[0] = nums[0]
13+
14+
for (i in 1 until nums.size) {
15+
if (dp[i - 1] + nums[i] >= 0 && dp[i-1] >= 0) {
16+
dp[i] = dp[i - 1] + nums[i]
17+
} else {
18+
dp[i] = nums[i]
19+
}
20+
}
21+
return dp.max()
22+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package leetcode_study
2+
3+
/*
4+
* 오름차순으로 정렬된 두 노드 리스트를 크기 순서대로 병합하는 문제
5+
* 기준 노드와 다음 노드를 가리키는 포인터 노드를 상용해 문제 해결
6+
* 시간 복잡도: O(m + n)
7+
* -> 주어진 두 노드 숫자만큼 순회
8+
* 공간 복잡도: O(1)
9+
* */
10+
fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? {
11+
val resultNode = ListNode(0)
12+
var currentNode = resultNode
13+
14+
var firstCurrentNode = list1
15+
var secondCurrentNode = list2
16+
17+
while (firstCurrentNode != null && secondCurrentNode != null) {
18+
if (firstCurrentNode.value <= secondCurrentNode.value) {
19+
currentNode.next = ListNode(firstCurrentNode.value)
20+
firstCurrentNode = firstCurrentNode.next
21+
} else {
22+
currentNode.next = ListNode(secondCurrentNode.value)
23+
secondCurrentNode = secondCurrentNode.next
24+
}
25+
currentNode = currentNode.next!!
26+
}
27+
28+
if (firstCurrentNode != null) {
29+
currentNode.next = firstCurrentNode
30+
} else if(secondCurrentNode != null) {
31+
currentNode.next = secondCurrentNode
32+
}
33+
return resultNode.next
34+
}
35+
36+
37+
class ListNode(
38+
var value: Int,
39+
) {
40+
var next: ListNode? = null
41+
}

missing-number/EcoFriendlyAppleSu.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package leetcode_study
2+
3+
/*
4+
* 주어진 숫자 배열에서 존재하지 않은 수 판별하는 문제
5+
* 0부터 n의 범위에서 모든 수는 유일하게 존재
6+
* 시간 복잡도: O(n)
7+
* -> nums 배열을 순회하며, 각 요소에 대해 board 배열에 값을 설정: O(n)
8+
* -> board 배열을 순회하며, 값이 0인 인덱스 검색: O(n)
9+
* --> O(n) + O(n) = O(n)
10+
* 공간 복잡도: O(n)
11+
* -> 입력 배열 nums의 크기가 n일 때, board 배열의 크기 n + 1: O(n)
12+
* */
13+
fun missingNumber(nums: IntArray): Int {
14+
val size = nums.size
15+
val board = IntArray(size+1)
16+
for (index in nums.indices) {
17+
board[nums[index]] = 1
18+
}
19+
20+
for (i in board.indices) {
21+
if (board[i] == 0) {
22+
return i
23+
}
24+
}
25+
return 0
26+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package leetcode_study
2+
3+
/*
4+
* 주어진 문자열에서 자른 개별 문자열이 회문일 경우를 판단하는 문제
5+
* 시간 복잡도: O(n^3)
6+
* -> 주어진 문자열을 자를 때 이중 반복문 수행: O(n^2)
7+
* -> subString() 함수는 내부적으로 주어진 k 만큼 복사된 문자열 객체를 만들기 때문에 O(n) 소요
8+
* --> O(n) * O(n^2) = O(n^3)
9+
* 공간 복잡도: O(n)
10+
* -> subString() 함수가 호출될 때마다 길이 k의 새로운 문자열 객체가 생성되기 때문에 subString 최대 길이인 O(n)을 가짐.
11+
* */
12+
fun countSubstrings(s: String): Int {
13+
var count = 0
14+
15+
val maxLength = s.length
16+
for (startIndex in 0 until maxLength) {
17+
for (endIndex in startIndex + 1..maxLength) {
18+
val temp = s.substring(startIndex, endIndex)
19+
if (temp == temp.reversed()) {
20+
count++
21+
}
22+
}
23+
}
24+
return count
25+
}
26+
27+
/*
28+
* 자른 문자열을 개별적으로 회문인지 판단해야하는데 주어진 문자열에서 포함하는 것으로 문제를 해석하고
29+
* 해결했기 때문에 통과하지 못함
30+
* */
31+
fun countSubstrings01(s: String): Int {
32+
val result = mutableListOf<String>()
33+
val maxLength = s.length
34+
var startIndex = 0
35+
while (startIndex < maxLength) {
36+
val endIndex = startIndex + 1
37+
for (i in endIndex until maxLength + 1) {
38+
val temp = s.substring(startIndex, i)
39+
if (s.contains(temp.reversed())) {
40+
result.add(temp)
41+
}
42+
}
43+
startIndex += 1
44+
}
45+
return result.size
46+
}

word-search/EcoFriendlyAppleSu.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package leetcode_study
2+
3+
4+
/*
5+
* dfs로 문제 해결
6+
* 정해진 순서의 단어를 처리하는 부분에서 대부분의 시간 사용
7+
* 시간 복잡도: O(m *n)
8+
* -> 주어진 배열에서 첫 번째 글자를 찾는 반복문: O(m *n)
9+
* -> 최악의 경우 모든 칸을 방문 처리(dfs): O(m *n)
10+
* --> O(m *n) + O(m *n) == O(m *n)
11+
* 공간 복잡도: O(m * n * l)
12+
* -> 방문을 체크하기 위한 공간: O(m * n)
13+
* -> DFS는 단어의 길이 l 만큼 최대 깊이로 호출되므로, 재귀 스택의 공간 복잡도: O(l)
14+
* */
15+
val dx = listOf(0, 1, -1, 0)
16+
val dy = listOf(1, 0, 0, -1)
17+
18+
fun exist(board: Array<CharArray>, word: String): Boolean {
19+
val rowSize = board.size
20+
val colSize = board[0].size
21+
val visited = Array(rowSize) { BooleanArray(colSize) }
22+
23+
// 모든 위치에서 DFS 탐색 시작
24+
for (i in board.indices) {
25+
for (j in board[0].indices) {
26+
if (board[i][j] == word[0] && dfs(board, word, i, j, 0, visited)) {
27+
return true
28+
}
29+
}
30+
}
31+
return false
32+
}
33+
34+
fun dfs(board: Array<CharArray>, word: String, x: Int, y: Int, index: Int, visited: Array<BooleanArray>): Boolean {
35+
// 탐색 종료 조건
36+
if (index == word.length) return true
37+
if (x !in board.indices || y !in board[0].indices || visited[x][y] || board[x][y] != word[index]) return false
38+
39+
// 현재 위치 방문 처리
40+
visited[x][y] = true
41+
42+
// 4방향으로 탐색
43+
for (i in 0..3) {
44+
val nx = x + dx[i]
45+
val ny = y + dy[i]
46+
if (dfs(board, word, nx, ny, index + 1, visited)) {
47+
return true
48+
}
49+
}
50+
51+
// 방문 복구
52+
visited[x][y] = false
53+
return false
54+
}

0 commit comments

Comments
 (0)