[Kotlin/프로그래머스] 23년 12월 2주차 코드카타정리

지혜·2023년 12월 11일

Code_Kata

목록 보기
4/16

✏20231211 월요일

📖푸드 파이트 대회

  • 배열의 첫번째(인덱스0번)는 물 한개. 이므로 1칼로리음식(인덱스1번)부터 계산할 수 있게 반복문 범위를 설정했다.
  • 각 칼로리 음식에 대하여 2명이 공평하게 나눌 수있도록 나누기2를 한 만큼 repeat()으로 반복될 수 있도록 했다.
  • 오른쪽 선수의 음식들에 더해서 가운데에 물(0칼로리)를 두고, 왼쪽 선수도 낮은칼로리의 음식부터 먹을 수 있도록 refversed()를 사용하여 반환해주었다.
class Solution {//나의 풀이
    fun solution(food: IntArray): String {
        var answer: String = ""
        
        for(i in 1 until food.size){
            answer += i.toString().repeat(food[i]/2)
        }
        
        return answer + "0" + answer.reversed()
    }
}
  • expression 'size' of type 'Int' cannot be invoked as a function. The function 'invoke()' is not found 오류
    =>size쓸때 아무생각없이 ( )괄호를 붙였다가 오류를 만났다. size는 ( )괄호없이 속성방식으로 사용해야 한다.
  • repeat(n:Int)함수는 int타입을 인자로 받지만, 내부적으로 StringBuilder를 사용하기때문에 문자열에만 사용할 수 있다. for문에서 숫자로 진행되는 i에 대해 toString()을 써서 문자열로 인식할 수 있도록 바꿔주어야 오류가 나지 않는다.
  • 배열에서 reversed( )를 쓰면 리스트로 반환하는데, 여기서 사용한 reversed( )는 String자체에 내장된 reversed( )메소드 이다. 그래서 문자열에서 사용해서 그대로 문자열을 반환해준다.

📖콜라 문제

  • val n 값을 받을, bottle을 변수로 선언했다. 개수에 맞게 교환하고 남는 나머지들의 값을 저장하기 위해 empty변수를 만들었다. 마지막으로 교환가능한 병 개수의 값을 저장하기 위한 change를 만들었다.
  • change(교환가능한 병의 전체 수)가 a(최소 교환 가능 개수) 보다 크거나 같을(교환가능) 동안 while문이 반복되도록 작성했다.
  • 반복문 안에서 받을 수 있는 콜라의 수를 bottle에 저장하고, answer에 총 교환 콜라 수를 누적 합산 하도록 해서, answer를 return했다.
class Solution {//나의 풀이
    fun solution(a: Int, b: Int, n: Int): Int {
        var answer: Int = 0
        var bottle = n
        var empty = 0
        var change = bottle + empty
        
        while(change >= a) {
            bottle = change/a * b
            empty = change % a
            change = bottle + empty
           
            answer += bottle
        }      
        return answer
    }
}
  • while반복문 쓸 때.. 자꾸.. 범위 설정하는걸 한번에 못한다..ㅎㅎ while(조건)을 만족하는 동안! 반복되는 것을 유의하자.
    +change >= a가 아니고 change > a 를 썼었는데, 어쨋든 a개수일 때도 교환은 가능하니까.. a개수일 때의 교환을 빼먹어서 값이 자꾸 다르게 나왔었다. 정확한 범위 조건에 대해 주의해야곘다.

✏20231212 화요일

📖명예의 전당 (1)

  • 명예의전당(Hall Of Fame)의 값을 저장하기 위해 hof 변수를 만들었다. removeFirst( )를 사용해야하기 때문에 mutableListOf<Int>로 선언해줬다.
  • 인덱스 0번부터 score의 크기만큼 반복될 수 있도록 for문을 만들었다.
  • 명예의전당 k만큼은 그냥 hor에 들어갈 수 있도록 if문을 작성하고, hof의 최소값이 answer의 배열로 들어갈 수 있도록 했다.
  • hof의 개수가 k보다 많아지면, 다시 if문을 사용해서 hof의 최소값과 해당 인덱스의 값을 비교했다.
  • hof의 최소값보다 크거나 같다면 hof를 오름차순 정렬하여, 제일 작은 첫번째 값을 삭제하고 해당인덱스의 값을 hof에 넣은 후의 최소값을 찾아서 answer에 넣어주었다.
  • hof의 최소값보다 작은 경우에도 hof배열의 최소값을 answer에 넣도록 작성해주었다.
class Solution {//나의 풀이
    fun solution(k: Int, score: IntArray): IntArray {
        var answer: IntArray = intArrayOf()
        var hof  = mutableListOf<Int>()
        
        for(i in 0 until score.size) {
            if(i < k) {
                hof += score[i]
                answer += hof.minOrNull()!!
            } else {
                if(score[i] >= hof.minOrNull()!!){
                    hof.sort()
                    hof.removeFirst()
                    hof += score[i]
            		answer += hof.minOrNull()!!
                } else {
                    answer += hof.minOrNull()!!
                }
            }
        }
        
        return answer
    }
}
  • 원래 hof를 만들때 intArrayOf( )로 선언해줬었는데, public fun <T> MutableList<TypeVariable(T)>.removeFirst() 오류가 났다. 생김새를 보듯이 <제네릭>도 빼먹으면 오류가 난다.
  • 위의 이유와 마찬자기로 hof.sort( ).removeFrist( )라고 바로 이어서 작성했더니 오류가 나서 나눠서 작성했다.
  • 이전에도 말했지만 min( )이 이젠 사용이 안되고 minOrNull( )이 사용이 되는데, !!를 지양해야하는 건 알지만 null인 경우를 받아들이지 않는 오류가 있어서 !!를 붙여서 간단하게 오류를 피했다.

📖명예의 전당 (1) 문제의 다른 사람 풀이 : PriorityQueue(우선순위 큐)를 사용한 방법

import java.util.*
class Solution {//다른 사람 풀이
    fun solution(k: Int, score: IntArray): IntArray {
        var answer = mutableListOf<Int>()
        val pq = PriorityQueue<Int>()
        for(item in score) {
            if(pq.size < k) {
                pq.add(item)
            }
            else {
                if(pq.peek() < item) {
                    pq.add(item)
                    pq.poll()
                }
            }
            answer.add(pq.peek())
        }
        return answer.toIntArray()
    }
}
  • Priority Queue( ) : 우선순위 큐. 이진트리의 형태로 값을 (오름차순)정렬하여 저장한다.
  • for문 안에서도 인덱스 값을 사용할 필요 없이 (값 in 배열) 형태를 사용했다. 더 깔끔해보이는 것 같다.
  • Queue에서 사용하는 function
    • peek( ) : Queue의 현재상태를 건드리지 않고, head의 값을 반환한다. Queue가 비어있는 경우 null을 반환한다.
    • element( ) : peek와 동일한 기능을 하지만, Queue가 비어있어도 null을 반환하지 않는다.
    • poll( ) : Queue의 head를 반환하는 동시에 제거한다. 제거된 후 남은 요소들은 한 칸 씩 당겨져 재정렬된다. Queue가 비어있는 경우 null을 반환한다.
    • remove( ) : poll과 동일한 기능을 하지만, Queue가 비어있어도 null을 반환하지 않는다.

✏20231213 수요일

📖2016년

  • 답을 출력할 요일 배열을 week 이름으로 만들었다. 일주일인 7로 나누어 계산을 할 때, 1월1일이 금요일이므로 첫날. 1일(%7 =1)이 금요일이니까 인덱스 1번이 FRI로 오도록 배열에 입력했다.
  • 각 달의 일수를 적은 month 배열을 만들고, day 변수에 주어진 a, b 값에 맞춰 총 일 수를 계산하여 저장한 다음
  • day%7을 인덱스로하는 week의 값이 반환되도록 만들었다.
class Solution {//나의 풀이
    fun solution(a: Int, b: Int): String {
        
        var week = arrayOf("THU","FRI", "SAT", "SUN","MON","TUE","WED" )
        var month = arrayOf(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
        
        var day = (0 until a-1).map{month[it]}.sum() + b
        
        return week[day % 7]
    }
}
  • 전체일수를 일주일로 나누어 해당하는 요일을 찾기만 하면 되는 간단한 문제였다.
  • 여기에서 범위에 map을 적용하여 계산하는 방법을 사용해 봤다. map 함수는 람다 조건식 안에서 원하는 조건으로 변환된 새로운 컬렉션을 만들어주는 함수이다. 자주 쓰니까 정의도 정확하게 기억하고 다양하게 사용하게 활용하는 방법을 생각해야겠다.

📖과일 장수

  • 최대이익을 반환해야하기 때문에, 큰 점수들 부터 활용될 수 있도록 내림차순 정렬을 해줬다.
  • for문 에서 포장해야하는 m개수 만큼 반복될 수 있도록 범위를 주었다. 이때, 한 번 포장한 것들은 재사용될 수 없으므로 step으로 사용한 m개는 건너뛸 수 있도록 해줬다.
  • subList를 사용하여 포장할만큼 배열을 끊고, 포장된 packaging배열의 최소점수에 포장개수m을 곱하여 answer에 누적합산 될 수 있도록 했다.
class Solution {// 나의 풀이
    fun solution(k: Int, m: Int, score: IntArray): Int {
        var answer: Int = 0     
        var descendingScore = score.sortedDescending()
        
        for(i in 0 .. score.size-m step m) {
            var packaging = descendingScore.subList(i, i+m)
            answer += packaging.minOrNull()!! * m    
        }

        return answer
    }
}

📖과일 장수 문제의 다른 사람 풀이

class Solution {//다른 사람 풀이
    fun solution(k: Int, m: Int, score: IntArray): Int {
        score.sortDescending()
        var answer = 0
        for (i in (m - 1)..score.lastIndex step m) {
            answer += score[i]
        }
        return answer * m
    }
}
  • 내 풀이와 비슷한 풀이인데 훨씬 간결해서 가져와봤다.
  • 우선, 내림차순 정렬한 것을 굳이 변수에 집어넣지 않아도 된다는 것을 알게 되었다.
  • 그리고 나는 subList를 사용해서 끊어줬는데, 그냥 바로 마지막인덱스 값만 골라서 바로 answer에 더해주는게 충격이었다. 어짜피 정렬해줬으니까 최소값을 쓸 필요가 없었던건데..! 뭔가 반성하게 되는 코드였다.

✏20231214 목요일

📖소수 만들기

  • 숫자 3개를 합해야하므로, 3중 반복문을 통해 nums 배열의 숫자를 뽑았다.
  • 변수 sum에 뽑은 숫자들의 합을 담고, if문으로 약수가 있는지 판별한다. (있으면1 없으면0)
  • 약수가 존재한다면 소수가 아니므로 소수가 0일때, answer에 하나씩 증가하도록 증감연사자를 썼다.
class Solution {//나의 풀이
    fun solution(nums: IntArray): Int {
        var answer = 0
        
        for(i in 0 until nums.size) {
            for(j in i+1 until nums.size) {
                for(k in j+1 until nums.size) {
                    var sum = nums[i] + nums[j] + nums[k]
                    var divisor = 0
                    for(n in 2 .. sum-1 ) {
                        if(sum % n == 0) {
                            divisor = 1
                        }
                    }
                    if(divisor == 0) {
                        answer++
                    }
                }
            }
        }
        
        return answer
    }
}
  • 다른사람의 풀이를 보니까, divisor=1 밑에 break를 넣었다. 생각해보면, 소수인지 아닌지가 중요한 사항이기 때문에, 한번 발견될 때 그냥 break로 빠져나오는게 필요없는 연산도 줄이고 좋은 방법인 것 같다.
    =>찾아보니까, 바깥 반복문은 계속 작동하고, 중첩반복문을 빠져나오고 싶을 때 쓰는 방법 중 하나로 flag를 세우는 방법이 있다고 한다. 다른 사람풀이에서는 flag = 1로 되어있는 의문이 풀렸다..
  • 다른사람들의 풀이 중 하나로, 아예 소수를 판별하는 펑션을 따로 작성한 후, solution에서 활용하는 방법이 있었는데, 따로 펑션을 세우는 거에도 낯설어하지말고 도전해봐야겠다.

📖덧칠하기

  • section의 부분이 다 칠해지면 되는 문제이기 때문에, 앞에서부터 차근차근 칠할 수있도록 반복문의 범위를 section으로 잡았다.
  • 한 번 칠했으면 answer이 한번씩 증가할 수 있도록 증감연산자를 사용하고, m의 길이 동안 section에서 포함되는 부분이 있을 수 있기 때문에, 다음에 칠하기 시작할 부분 next를 변수로 선언하여 step m 의 기능을 할 수 있도록 if문을 선언해줬다.
class Solution {//나의 풀이
    fun solution(n: Int, m: Int, section: IntArray): Int {
        var answer: Int = 0
        var next = 0
        
        for(i in section){
            if(i >= next) {
                answer++
                next = i + m 
            }
        }
        
        return answer
    }
}
  • 맨 처음에 솔직히 for(i in section step m)이라고 썼다.. 오류가 나서야 음~ 바보같았다고 깨달았다..ㅎ 어쨋든 section의 다음 값과 칠하기 시작할 시작점을 비교해서 중복으로 칠하지 못하도록 if문을 선언해줬다.
  • 겹치는 부분에 대해 어떻게 건너뛰어야 하는지 좀 헤맸었는데, 다른 사람의 풀이 중에 칠하는 범위에 대해 section[0] until section[0] + m 형태로 range를 선언해서, range안에 section 값이 포함된다면 continue로 건너뛰는 방법을 사용한 사람도 있었는데, 어쨋든 원리는 똑같은 거 같고 범위를 설정하는 것도 일이라고 생각해서 가져오지는 않았지만, continue를 활용하는 방법도 있었는데..! 하고 새삼스럽게 깨달아서 적어둔다.

✏20231215 금요일

📖기사단원의 무기

  • sqrt를 활용할 것이기 때문에, kotlin.math.*를 임포트해주었다.
  • 약수의 개수를 세야하기 때문에 count 변수와, 개수를 담을 배열 divisor를 만들었다.
  • number까지 각 각의 약수의 개수를 구해야하기때문에 for문을 만들고, 그 안에 이중 for문으로 약수의 개수를 구하고, 구한 값을 divisor 배열에 넣은 다음, count 값을 초기화해준다.
  • 그렇게 구해진 divisor배열에 map을 통해 limit보다 값이 큰 개수들은 power로 대체하고, 값이 작거나 같으면 그대로의 값을 가지도록 배열을 재정비하고 그 합계를 반환한다.
import kotlin.math.*
class Solution {//나의 풀이
    fun solution(number: Int, limit: Int, power: Int): Int {
        var count = 0
        var divisor = arrayOf<Int>()
        
        for(i in 1.. number){
            for(j in 1..sqrt(i.toDouble()).toInt()) {
                if( j * j == i) count++
                else if (i % j == 0 ) count += 2
            } 
            divisor += count
            count = 0
        }  

      return divisor.map{i -> if(i>limit) power else i}.sum()

    }
}
  • 맨 처음에는 그냥 if( i % 2 == 0 )방식을 사용해서 문제를 풀었다. 약수를 구하는 건 꽤 해봤으니까, for문 말고 var divisor = (1..number).map{ }을 사용해서 약수의 개수를 구하고, divisor를 또 새로운 변수에 담아 limit에 따른 조건으로 map을 걸어서 2-3줄 정도로 뚝딱 작성해가지고, 오~ 완전 잘한것같은데 ㅎㅎ 하고 .. 채점을 돌렸더니.. 시간 초과가 나왔다. ㅎㅎ
  • 여기까지는 뭐 그래. 내가 너무 map만 썼나? 확장함수보다는 그냥 for를 쓰는게 더 빠르다고 들은 것 같으니까 map으로 작성한 약수 개수 구하는 식을 for문으로 풀고, divisor를 그냥 배열로 선언했으니까, 새롭게 담아줄 필요 없이 바로 divisor.map{i -> if(i>limit) power else i}.sum()을 반환하게 바꿨는데....... 그래도 시간 초과가 나왔다.ㅎ
  • 그제서야 약수의 개수를 구하는 다른 방법을 검색해보기 시작했는데, 제곱근을 통해서 구하는게 훨씬 효율적으로 구하는 방법이라고 이미 많은 포스팅들이 있었다.. ㅎㅎ 어쨋든, 숫자 하나가 아니고, 1번부터~number까지 각각의 숫자에 대한 약수의 개수를 구하는거라 for문을 겉에 한번 더 싸야했지만.. 그래서 시간단축이 잘된건지 잘모르겠지만 정상작동하여 통과 받았다.!

📖 제곱근을 통해 약수의 개수를 효율적으로 구하기

import kotlin.math.sqrt
.
.
for(i in 1..sqrt(num.toDouble()).toInt()) {
    if( i * i == num) count++
    else if (num % i == 0 ) count += 2
} 
  • n = a*b 일떄, n/b를 하면 a가 나오고, n/a를 하면 b가 나온다. 즉, a가 약수일 때 b가 보장된다는 뜻이다. 이런 경우에는 a인 경우, b인 경우를 전부 계산하면 시간적으로 손해다.
  • 따라서, 제곱근을 사용하여 검사 수를 절반으로 줄이고, 다만 제곱근인 경우에는 같은 수를 곱해서 나오는 것이기 때문에 +1을 해주고, 나머지 중 나눠서 딱 떨어지는 숫자가 있을 때 +2를 해주면 된다.

📖 개수 말고 약수 자체를 구하는 방법

import kotlin.math.sqrt
.
.
val result = arrayListOf<Int>()
for (i in 1 .. sqrt(num.toDouble()).toInt()) {
     if (num % i == 0) {
         result.add(i)
         if (num / i != i) result.add(num / i)
     }
}
 println("${result.sorted()}")//약수들을 오름순으로 정렬.
profile
파이팅!

0개의 댓글