-
Notifications
You must be signed in to change notification settings - Fork 126
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
[정현준] 5주차 답안 제출 #449
[정현준] 5주차 답안 제출 #449
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package leetcode_study | ||
|
||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder | ||
import org.junit.jupiter.api.Test | ||
|
||
class `3sum` { | ||
|
||
fun threeSum(nums: IntArray): List<List<Int>> { | ||
return usingTwoPointer(nums) | ||
} | ||
|
||
/** | ||
* 1. 정수 배열을 순회하며 모두 확인한다. (시간초과) | ||
* TC: O(n^3), SC: O(n) | ||
*/ | ||
private fun usingIterative(nums: IntArray): List<List<Int>> { | ||
val result = mutableSetOf<List<Int>>() | ||
for (first in nums.indices) { | ||
for (second in first + 1 until nums.size) { | ||
for (third in second + 1 until nums.size) { | ||
if (nums[first] + nums[second] + nums[third] == 0) { | ||
result.add(listOf(nums[first], nums[second], nums[third]).sorted()) | ||
} | ||
} | ||
} | ||
} | ||
return result.toList() | ||
} | ||
|
||
/** | ||
* 2. 입력받은 정수 배열을 정렬하여 순회하면서 원소를 합산하여 0과 비교한 결과를 기준으로 투 포인터의 값을 조작한다. | ||
* TC: O(n * log(n) + n^2), SC: O(n) | ||
*/ | ||
private fun usingTwoPointer(nums: IntArray): List<List<Int>> { | ||
val sortedNumbers = nums.sorted() | ||
val result = mutableSetOf<List<Int>>() | ||
for (index in nums.indices) { | ||
var left = index + 1 | ||
var right = nums.size - 1 | ||
while (left < right) { | ||
val sum = sortedNumbers[index] + sortedNumbers[left] + sortedNumbers[right] | ||
if (sum == 0) { | ||
result.add(listOf(sortedNumbers[index], sortedNumbers[left], sortedNumbers[right])) | ||
left++ | ||
right-- | ||
} else if (sum < 0) { | ||
left++ | ||
} else { | ||
right-- | ||
} | ||
} | ||
} | ||
return result.toList() | ||
} | ||
|
||
@Test | ||
fun `입력받은 정수 배열의 세 개의 원소의 합이 0이 되는 리스트를 반환한다`() { | ||
threeSum(intArrayOf(-1,0,1,2,-1,-4)) shouldContainExactlyInAnyOrder listOf( | ||
listOf(-1,-1,2), | ||
listOf(-1,0,1) | ||
) | ||
threeSum(intArrayOf(0,0,0)) shouldContainExactlyInAnyOrder listOf( | ||
listOf(0,0,0) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package leetcode_study | ||
|
||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
import kotlin.math.max | ||
|
||
class `best-time-to-buy-and-sell-stock` { | ||
|
||
fun maxProfit(prices: IntArray): Int { | ||
if (prices.size == 1) return 0 | ||
return usingKadaneAlgorithm(prices) | ||
} | ||
|
||
/** | ||
* 1. 방향이 존재하기 때문에 투 포인터를 활용하여 주식을 팔 수 있는 경우라면 최대 값을 계산하고 만약 산 가격보다 싼 가격을 만나면 다시 산다 | ||
* TC: O(n), SC: O(1) | ||
*/ | ||
private fun usingTwoPointer(prices: IntArray): Int { | ||
var (left, right) = 0 to 1 | ||
var maxProfit = 0 | ||
|
||
while (right < prices.size) { | ||
if (prices[left] < prices[right]) { | ||
maxProfit = max(prices[right] - prices[left], maxProfit) | ||
right++ | ||
} else if (prices[left] >= prices[right]) { | ||
left = right | ||
right++ | ||
} | ||
} | ||
|
||
return maxProfit | ||
} | ||
|
||
/** | ||
* 2. 카데인 알고리즘의 변형된 버전으로 가장 싼 경우를 buy에 저장하고 현재 최대 수익을 초과하면 업데이트한다 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 방식을 카데인 알고리즘으로 부르는군요! 배웠습니다! |
||
* TC: O(n), SC: O(1) | ||
*/ | ||
private fun usingKadaneAlgorithm(prices: IntArray): Int { | ||
var buy = prices[0] | ||
var maxProfit = 0 | ||
|
||
for (index in 1 until prices.size) { | ||
if (prices[index] < buy) { | ||
buy = prices[index] | ||
} else if (prices[index] - buy > maxProfit) { | ||
maxProfit = prices[index] - buy | ||
} | ||
} | ||
return maxProfit | ||
} | ||
|
||
@Test | ||
fun `주어진 가격 배열을 통해 최대의 수익을 반환한다`() { | ||
maxProfit(intArrayOf(3,3)) shouldBe 0 | ||
maxProfit(intArrayOf(7,6,5,4,3,2,1,0)) shouldBe 0 | ||
maxProfit(intArrayOf(7,1,5,3,6,4)) shouldBe 5 | ||
maxProfit(intArrayOf(1,2,4,2,5,7,2,4,9,0,9)) shouldBe 9 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package leetcode_study | ||
|
||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder | ||
import org.junit.jupiter.api.Test | ||
|
||
class `group-anagrams` { | ||
|
||
fun groupAnagrams(strs: Array<String>): List<List<String>> { | ||
return usingArray(strs) | ||
} | ||
|
||
/** | ||
* 1. 입력받은 문자열들을 문자 배열로 변환하여 정렬된 결과를 map 의 키로 정하여 키 기준으로 문자열들을 그룹화한다. | ||
* TC: O(n * k log(k)), SC: O(n) | ||
*/ | ||
private fun usingSort(strs: Array<String>): List<List<String>> { | ||
val map = strs.groupBy { it.toCharArray().sorted() } | ||
return map.values.toList() | ||
} | ||
|
||
/** | ||
* 2. 입력받은 문자열들을 순회하며 문자열의 문자 갯수를 카운트하여 애너그램인지 구별한다. | ||
* TC: O(n), SC: O(n) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 배열을 사용한 방법도 있었군요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 k처럼, 이것도 O(n*k)일 것 같습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 그렇네요 ㅎㅎ 감사합니당 |
||
*/ | ||
private fun usingArray(strs: Array<String>): List<List<String>> { | ||
val map = strs.groupBy { it -> | ||
val counter = IntArray(26) | ||
for (ch in it) { | ||
counter[ch - 'a']++ | ||
} | ||
counter.joinToString(",") // 구분자를 넣지 않으면 arrayOf("bdddddddddd","bbbbbbbbbbc") 테케를 실패함 | ||
} | ||
|
||
return map.values.toList() | ||
} | ||
|
||
@Test | ||
fun `입력받은 문자열들을 애너그램 기준 그룹별로 반환한다`() { | ||
groupAnagrams(arrayOf("eat","tea","tan","ate","nat","bat")) shouldContainExactlyInAnyOrder listOf( | ||
listOf("tan","nat"), | ||
listOf("bat"), | ||
listOf("eat","tea","ate"), | ||
) | ||
groupAnagrams(arrayOf("cab","tin","pew","duh","may","ill","buy","bar","max","doc")) shouldContainExactlyInAnyOrder listOf( | ||
listOf("max"),listOf("buy"),listOf("doc"),listOf("may"),listOf("ill"), | ||
listOf("duh"),listOf("tin"),listOf("bar"),listOf("pew"),listOf("cab") | ||
) | ||
groupAnagrams(arrayOf("bdddddddddd","bbbbbbbbbbc")) shouldContainExactlyInAnyOrder listOf( | ||
listOf("bbbbbbbbbbc"), | ||
listOf("bdddddddddd") | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package leetcode_study | ||
|
||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
|
||
class `implement-trie-prefix-tree` { | ||
|
||
/** | ||
* 영어 소문자만 입력된다. | ||
*/ | ||
class Trie { | ||
|
||
private val node = Node() | ||
|
||
/** | ||
* TC: O(n), SC: O(n) | ||
*/ | ||
fun insert(word: String) { | ||
var now = node | ||
|
||
for (char in word) { | ||
val index = char - 'a' | ||
if (now.next[index] == null) { | ||
now.next[index] = Node() | ||
} | ||
now.next[index]?.apply { now = this } | ||
} | ||
now.isEnd = true | ||
} | ||
|
||
/** | ||
* TC: O(n), SC: O(1) | ||
*/ | ||
fun search(word: String): Boolean { | ||
var now = node | ||
|
||
for (char in word) { | ||
val index = char - 'a' | ||
if (now.next[index] == null) { | ||
return false | ||
} | ||
now.next[index]?.apply { now = this } | ||
} | ||
|
||
return now.isEnd | ||
} | ||
|
||
/** | ||
* TC: O(n), SC: O(1) | ||
*/ | ||
fun startsWith(prefix: String): Boolean { | ||
var now = node | ||
|
||
for (char in prefix) { | ||
val index = char - 'a' | ||
if (now.next[index] == null) { | ||
return false | ||
} | ||
now.next[index]?.apply { now = this } | ||
} | ||
|
||
return true | ||
} | ||
|
||
} | ||
|
||
@Test | ||
fun `접두사 트리를 구현하라`() { | ||
val trie = Trie() | ||
trie.insert("apple") | ||
trie.search("apple") shouldBe true | ||
trie.search("app") shouldBe false | ||
trie.startsWith("app") shouldBe true | ||
trie.insert("app") | ||
trie.search("app") shouldBe true | ||
} | ||
} | ||
|
||
private class Node { | ||
val next = Array<Node?>(26) { null } | ||
var isEnd = false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package leetcode_study | ||
|
||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
|
||
class `word-break` { | ||
|
||
fun wordBreak(s: String, wordDict: List<String>): Boolean { | ||
return usingDP(s, wordDict) | ||
} | ||
|
||
/** | ||
* 1. DFS 사용 (시간초과) | ||
* TC: O(s^w * wordDict 단어의 길이), SC: O(s) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 최악의 경우, 각 인덱스에서 w번 모두 선택할 수 있다고 생각해서 w * w * w *.... = w^s라고 이해했는데요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 맞네요 ㅎㅎ 말씀하신 |
||
*/ | ||
private fun usingDFS(s: String, wordDict: List<String>): Boolean { | ||
|
||
fun recursion(s: String, wordDict: List<String>, index: Int): Boolean = | ||
if (index == s.length) true | ||
else { | ||
wordDict.map { word -> | ||
var result = false | ||
if (index + word.length < s.length + 1 && s.substring(index, index + word.length) == word) { | ||
result = recursion(s, wordDict, index + word.length) | ||
} | ||
result | ||
}.find { it } ?: false | ||
} | ||
|
||
return recursion(s, wordDict, 0) | ||
} | ||
|
||
/** | ||
* 2, 사전에 담겨있는 문자열들을 기준으로 인덱스를 증가시키면서 문자열을 완성시킨다. 한 번 탐색하여 문자열을 완성시키지 못한 인덱스를 저장하여 해당 인덱스는 다시 탐색하지 않도록 하여 성능을 개선한다. | ||
* TC: O(s * w * wordDict 단어의 길이), SC: O(s) | ||
*/ | ||
private fun usingMemoizationDFS(s: String, wordDict: List<String>): Boolean{ | ||
|
||
fun dfs(s: String, wordDict: List<String>, index: Int, memo: MutableSet<Int>): Boolean { | ||
val len = s.length | ||
if(index == len) return true | ||
else if(memo.contains(index)) return false | ||
|
||
for (word in wordDict) { | ||
if (index + word.length < s.length + 1 && | ||
s.substring(index, index + word.length) == word && | ||
dfs(s, wordDict, index + word.length, memo)) { | ||
return true | ||
} | ||
} | ||
memo.add(index) | ||
return false | ||
} | ||
|
||
if(s.isEmpty()) return false | ||
return dfs(s, wordDict, 0, mutableSetOf()) | ||
} | ||
|
||
/** | ||
* 3. 문자열의 끝부터 0까지 순회하면서 순회하는 범위의 문자열을 만들 수 있다면 해당 인덱스를 true로 변환하여 이전에 사용한 연산의 결과를 재활용한다. | ||
* TC: O(s * w * wordDict 단어의 길이) TC: O(s) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SC 오타 있습니다! |
||
*/ | ||
private fun usingDP(s: String, wordDict: List<String>): Boolean { | ||
val dp = BooleanArray(s.length + 1).apply { | ||
this[s.length] = true | ||
} | ||
|
||
for (index in s.length - 1 downTo 0) { | ||
for (word in wordDict) { | ||
if (dp[index]) break | ||
else if (index + word.length <= s.length && s.substring(index, index + word.length) == word) { | ||
dp[index] = dp[index + word.length] | ||
} | ||
} | ||
} | ||
return dp[0] | ||
} | ||
|
||
@Test | ||
fun `문자열과 문자열 사전이 주어졌을 때 문자열 사전을 이용하여 문자열을 완성할 수 있으면 참을 반환한다`() { | ||
wordBreak("applepenapple", listOf("apple", "pen")) shouldBe true | ||
wordBreak("leetcode", listOf("leet", "co", "de")) shouldBe true | ||
wordBreak("abcd", listOf("a","abc","b","cd")) shouldBe true | ||
wordBreak("cars", listOf("car","ca","rs")) shouldBe true | ||
} | ||
|
||
@Test | ||
fun `문자열과 문자열 사전이 주어졌을 때 문자열 사전을 이용하여 문자열을 완성할 수 없다면 거짓을 반환한다`() { | ||
wordBreak("catsandog", listOf("cats", "dog", "sand", "and", "cat")) shouldBe false | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n
이 커지면n^2
항이n log n
항보다 훨씬 더 빠르게 증가하고n log n
영향력이 줄기 때문에,이런 경우 TC 를
O(n^2)
으로 적는 게 더 일반적인 것 같습니다!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 그렇군요 ㅎㅎ 이렇게 시간 복잡도를 나열하는 것이 맞나 생각했었는데 가장 영향력이 큰 것만 골라적어야하는군요 ㅎㅎ 감사합니당