Skip to content

[정현준] 1주차 답안 제출 #298

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

Merged
merged 3 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions contains-duplicate/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package leetcode_study

import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test

class `contains-duplicate`{

fun containsDuplicate(nums: IntArray): Boolean {
return usingSet(nums)
}

// 1. 중첩 반복문 - 시간초과
// 시간복잡도: O(n^2), 공간복잡도: O(1)
private fun usingNestedLoop(nums: IntArray): Boolean {
nums.forEachIndexed { i, e1 ->
nums.forEachIndexed { j, e2 ->
if (i != j && e1 == e2) {
return true
}
}
}
return false
}

// 2. 정렬 후 순회
// 시간복잡도: O(n * log(n)), 공간복잡도: O(1)
private fun usingSort(nums: IntArray): Boolean {
nums.sort() // DualPivotQuicksort -> O(n log(n))
for (index in 1 until nums.size) {
val prev = nums[index - 1]
val curr = nums[index]
if (prev == curr) {
return true
}
}
return false
}

// 3. 자료구조 Set 사용
// 시간복잡도: O(n), 공간복잡도: O(n)
private fun usingSet(nums: IntArray): Boolean {
val set = nums.toSet()
return nums.size != set.size
}

@Test
fun 동일한_원소가_존재하면_true를_반환한다() {
val nums1 = intArrayOf(1, 2, 3, 1)
val nums2 = intArrayOf(1, 2, 3, 2)
val nums3 = intArrayOf(1, 1, 1, 1, 3, 3, 4, 3, 2, 4, 2)

containsDuplicate(nums1) shouldBeEqual true
containsDuplicate(nums2) shouldBeEqual true
containsDuplicate(nums3) shouldBeEqual true
}

@Test
fun 동일한_원소가_존재하지_않으면_false를_반환한다() {
val nums1 = intArrayOf(1, 2, 3, 4)
val nums2 = intArrayOf(1, 5, 7)

containsDuplicate(nums1) shouldBeEqual false
containsDuplicate(nums2) shouldBeEqual false
}
}
98 changes: 98 additions & 0 deletions kth-smallest-element-in-a-bst/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package leetcode_study

import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test
import java.util.PriorityQueue

class `kth-smallest-element-in-a-bst` {

fun kthSmallest(root: TreeNode?, k: Int): Int {
return inorderTraversal(root, k)
}

// 1. 재귀 호출로 모든 트리를 조회 후 정렬
// 시간복잡도: O(n * log(n)), 공간복잡도: O(n)
private fun recursionAndSort(root: TreeNode?, k: Int) = mutableSetOf<Int>().apply {
dfs(root, this)
}.sorted()[k - 1]

private fun dfs(node: TreeNode?, set: MutableSet<Int>) {
if (node == null) return

set.add(node.`val`)
dfs(node.left, set)
dfs(node.right, set)
}

// 2. 재귀 호출로 모든 트리의 값을 우선순위 큐에 삽입하고 작은 값으로 계속 큐를 갱신
// 시간복잡도: O(n * log(k)), 공간복잡도: O(n + k)
// 트리 순회 : O(n), 우선순위 큐 삽입: O(log k)
private fun usingPriorityQueue(node: TreeNode?, k: Int): Int {
fun dfs(node: TreeNode, k: Int, pq: PriorityQueue<Int>) {
pq.offer(node.`val`)

if (pq.size > k) {
pq.poll()
}
if (node.left != null) {
dfs(node.left!!, k, pq)
}
if (node.right != null) {
dfs(node.right!!, k, pq)
}
}

val pq = PriorityQueue { v1: Int, v2: Int -> v2 - v1 }
dfs(node!!, k, pq)
return pq.first()
}

// 3. 문제의 전제가 이진탐색트리이기에, 중위순회로 탐색하여 값을 누적하면 오름차순의 값이 된다.
// 시간복잡도: O(n), 공간복잡도: O(n)
private fun inorderTraversal(node: TreeNode?, k: Int): Int {
fun dfs(node: TreeNode, k: Int, list: MutableList<Int>) {
if (node.left != null) {
dfs(node.left!!, k, list)
}
list.add(node.`val`)
if (node.right != null) {
dfs(node.right!!, k, list)
}
}

return mutableListOf<Int>().apply {
dfs(node!!, k, this)
}[k - 1]
}

@Test
fun `루트와 정수 k가 주어지면 트리에 있는 모든 노드의 값 중 가장 작은 값을 반환한다`() {
kthSmallest(TreeNode.of(listOf(0, 3,1,4,null,2)), 1) shouldBeEqual 1
kthSmallest(TreeNode.of(listOf(0, 5,3,6,2,4,null,null,1)), 3) shouldBeEqual 3
}
}

class TreeNode(var `val`: Int) {
var left: TreeNode? = null
var right: TreeNode? = null

companion object {
fun of(numbers: List<Int?>): TreeNode? {
fun setChild(node: TreeNode?, nums: List<Int?>, index: Int): TreeNode? {
if (node == null) return null
val (leftIndex, rightIndex) = index * 2 to index * 2 + 1

if (leftIndex < nums.size && nums[leftIndex] != null) {
node.left = TreeNode(nums[leftIndex]!!)
setChild(node.left, nums, leftIndex)
}
if (rightIndex < nums.size && nums[rightIndex] != null) {
node.right = TreeNode(nums[rightIndex]!!)
setChild(node.right, nums, rightIndex)
}
return node
}
return setChild(TreeNode(numbers[1]!!), numbers, 1)
}
}
}
46 changes: 46 additions & 0 deletions number-of-1-bits/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package leetcode_study

import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test

class `number-of-1-bits` {

fun hammingWeight(n: Int): Int {
return bitOperation(n)
}

// 1. 직접 2진수를 구함
// 시간복잡도: O(log n), 공간복잡도: O(1)
private fun binary(n: Int): Int {
var calc = n
var count = 0
while(calc > 0) {
if (calc % 2 != 0) {
count++
}
calc /= 2
}
return count
}

// 2. 비트 논리 연산자 사용
// 시간복잡도: O(log n), 공간복잡도: O(1)
private fun bitOperation(n: Int): Int {
var calc = n
var count = 0
while (calc > 0) {
if (calc and 1 == 1) {
count ++
}
calc = calc shr 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코틀린은 >>shr 으로 표현하는군요
자바 유저라 처음보는데 재밌네요 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 비트 연산자는 처음 써봐서 ㅎㅎ 이번에 처음 배웠습니당

}
return count
}

@Test
fun `이진수에서_0이_아닌_성분의_개수를 반환한다`() {
hammingWeight(11) shouldBeEqual 3
hammingWeight(128) shouldBeEqual 1
hammingWeight(2147483645) shouldBeEqual 30
}
}
90 changes: 90 additions & 0 deletions palindromic-substrings/jdalma.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package leetcode_study

import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test

class `palindromic-substrings` {

fun countSubstrings(s: String): Int {
return dp(s)
}

// 1. 브루트포스 3중 반복문
// 시간복잡도: O(n^3), 공간복잡도: O(1)
private fun bruteForce(s: String): Int {

fun isPalindrome(text: String, left: Int, right: Int): Boolean {
var (start, end) = left to right
while (start < end) {
if (text[start] != text[end]) {
return false
}
start++
end--
}
return true
}

var count = 0
s.indices.forEachIndexed { left, _ ->
(left until s.length).forEach { right ->
if (isPalindrome(s, left, right)) {
count++
}
}
}
return count
}

// 2. 문자열의 길이가 홀수,짝수를 감안하여 특정 지점부터 두 개의 포인터 left , right를 비교한다.
// 시간복잡도: O(n^2), 공간복잡도: O(1)
private fun twoPointer(s: String): Int {

fun palindromeCount(text: String, left: Int, right: Int): Int {
var count = 0
var (l , r) = left to right
while (l >= 0 && r < text.length && text[l--] == text[r++]) {
count++
}
return count
}

var result = 0
s.indices.forEachIndexed { index, e ->
val even = palindromeCount(s, index, index + 1)
val odd = palindromeCount(s, index - 1, index + 1)
result += even + odd + 1
}

return result
}

// 3. DP : 이전에 비교했던 결과를 기억하여 (현재 주어진 start, end 비교) && (이전 start, end 비교)
// 시간복잡도: O(n^2), 공간복잡도: O(n^2)
private fun dp(s: String): Int {
val len = s.length
val dp = Array(len) { BooleanArray(len) { false } }

for (end in 0 until len) {
for (start in end downTo 0) {
if (start == end) {
dp[start][end] = true
} else if (start + 1 == end) {
dp[start][end] = s[start] == s[end]
} else {
dp[start][end] = s[start] == s[end] && dp[start + 1][end - 1]
}
}
}

return dp.sumOf { row -> row.count { it } }
}

@Test
fun `주어진 문자열의 회문인 부분 문자열의 개수를 반환한다`() {
countSubstrings("abc") shouldBeEqual 3
countSubstrings("aaa") shouldBeEqual 6
countSubstrings("ababa") shouldBeEqual 9
countSubstrings("abcda") shouldBeEqual 5
}
}
82 changes: 82 additions & 0 deletions top-k-frequent-elements/jdalma.kt
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
import java.util.PriorityQueue

class `top-k-frequent-elements` {

fun topKFrequent(nums: IntArray, k: Int): IntArray {
return third(nums, k)
}

// 1. Map 정렬
// 시간복잡도: O(n * log(n)), 공간복잡도: O(n)
private fun first(nums: IntArray, k: Int): IntArray {
val map = mutableMapOf<Int, Int>()

nums.forEach {
map.compute(it) { _, oldValue ->
if (oldValue == null) 1
else oldValue + 1
}
}

return map.entries.sortedByDescending { it.value }
.map { it.key }
.slice(0 until k)
.toIntArray()
}

// 2. 우선순위 큐
// 시간복잡도: O(n * log(k)), 공간복잡도: O(n + k)
private fun second(nums: IntArray, k: Int): IntArray {
val map = mutableMapOf<Int, Int>()

nums.forEach { map.put(it, map.getOrDefault(it, 0) + 1) }

val heap: PriorityQueue<Map.Entry<Int, Int>> = PriorityQueue<Map.Entry<Int, Int>> {
v1, v2 -> v2.value.compareTo(v1.value)
}.apply {
this.addAll(map.entries)
}

return (0 until k).map { heap.poll().key }.toIntArray()
}

// 3. 이차원배열로 빈번도 저장
// 시간복잡도: O(n), 공간복잡도: O(n)
private fun third(nums: IntArray, k: Int): IntArray {
val map = mutableMapOf<Int, Int>()

nums.forEach { map.put(it, map.getOrDefault(it, 0) + 1) }

val freq = Array<MutableList<Int>>(nums.size + 1) { mutableListOf() }
map.entries.forEach {
val frequency = it.value
freq[frequency].add(it.key)
}

val result = IntArray(k)
var index = 0
(freq.size - 1 downTo 0).forEach { i ->
freq[i].forEach {
result[index++] = it
if (index == k) {
return result
}
}
}

return IntArray(0)
}

@Test
fun `배열에서_가장_빈도가_높은_K개의_원소를_출력한다`() {
topKFrequent(intArrayOf(1,1,1,2,2,3), 2) shouldBe intArrayOf(1,2)
topKFrequent(intArrayOf(1,1,1,2,2,3,3,4), 3) shouldBe intArrayOf(1,2,3)
topKFrequent(intArrayOf(2,2,3,3,1,1,4), 3) shouldBe intArrayOf(2,3,1)
topKFrequent(intArrayOf(4,1,-1,2,-1,2,3), 2) shouldBe intArrayOf(-1,2)

}
}