https://school.programmers.co.kr/learn/courses/30/lessons/134240

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

class Solution {
    fun solution(food: IntArray): String {
        var answer: String = ""
        var numArr = food.slice(1..food.size-1).map{it/2}
        var foodList = mutableListOf<Int>()
        
        for(i in numArr.indices) {
            for(j in 0 until numArr[i]) {
                if(numArr[i] != 0) {
                    foodList.add(i+1)
                }
            }
        }
        
        answer = foodList.joinToString("") + '0' + foodList.reversed().joinToString("")
        return answer
    }
}

 

코드를 먼저 보자.

 

우선 , 물을 제외하고 음식 부분의 배열만 먼저 slice로 가져온 뒤 2로 나눠 정수 부분으로 가져왔다.

 

그 후 정수 숫자 만큼 인덱스 숫자를 찍어주고 나온 리스트를 '0'과 그 역순의 리스트와 합쳐줘서 반환해준다.

 

별로 어렵지 않은 문제였다.

 

다만 코틀린스럽게 풀지는 못했다...

내가 제출한 코드

class Solution {
    fun solution(numbers: IntArray): IntArray {
        var answer: IntArray = intArrayOf()
        var intset = mutableSetOf<Int>()
        
        for(i in 0 until numbers.size - 1) {
            for(j in i+1 until numbers.size) {
                intset.add(numbers[i] + numbers[j])
            }
        }
        answer = intset.sorted().toIntArray()
        return answer
    }
}

 

이중for문을 이용해서 더해주었고 원소가 중복되면 안되기에 Set를 사용했다.

 

내 생각 코틀린스럽게 푼 사람 (사실 코틀린스럽다는 말을 정확히 몰라 틀릴 수 있음,,)

뭔가 코틀린 함수 쓴게 코틀린스러운건가,,ㅎㅎ

다른 분의 코드

class Solution {
    fun solution(numbers: IntArray): IntArray {
        val list = numbers.toList()
        return list.withIndex().flatMap { i -> list.withIndex().map { j -> i to j } }
            .filter { it.first.index != it.second.index }
            .map { it.first.value + it.second.value }
            .toSortedSet()
            .toIntArray()
    }
}

 

분석해보면 공부에 도움이 될거 같아 분석한번 해보겠습니다.

 

분석에 앞서 필요한 확장함수를 조금 알아보고 가자.

.withIndex() -> 객체의 원소를 (index, value) 형태로 접근할 수 있게 해준다.

예) list = listOf("a", "b", "c")

      indexedList = list.withIndex() 

      print(indexedList)

[IndexedValue(index=0, value=a), **IndexedValue(index=1, value=b)**, IndexedValue(index=2, value=c)]

 

이런식으로 출력된다.

 

.flatMap -> 컬렉션 안에 컬렉션이 있을 경우 내부 컬렉션을 펼쳐주는 연산이며 .map과 다르게 1대1매핑이 아닌 1대 多 매핑이 가능하다. 즉 모든 요소들의 생성값을 다룰 수 있다. 

 

.filter -> 간단히 필터 조건문안에 조건을 만족하는 원소들만 받아서 가져오겠다는거다.

 

이제 코드를 하나하나 뜯어보자. (너무 깊숙이 뜯진 못한다,,)

 

val list = numbers.toList()

numbers를 리스트 형태로 바꾸어 list에 담아준다.

 

list.withIndex().flatMap { i -> list.withIndex().map { j -> i to j } }

 

이 부분이 이해하기 굉장히 어려웠던 부분이었다. 사실 완벽하게 이해한 것인지는 모르나 틀렸을 경우 댓글부탁드립니다.

우선 문제의 첫 번째 입출력 예인 [2,1,3,4,1] 을 입력값으로 해서 예시로 사용해보겠다.

 

withIndex() 를 통해 [(0,2) (1,1) (2,3) (3,4) (4,1)] 이런식의 원소의 인덱스와 실제 value가 포함된리스트가 반환된다.

 

flatMap() 을 통해 이 안에 원소들을 1대 다 매핑이 가능하게 만들어줌과 동시에 마지막엔 싱글리스트로 반환해준다.

.map{} 안에 "i to j"는 i와 j를 pair시켜주는 역할이다. 

 

[(0,2) (1,1) (2,3) (3,4) (4,1)] 이 리스트의 i로 돌아가는 반복문과 list.withIndex().map의 j 인덱스로 돌아가는 반복문으로 

j가 i를 기준으로 각 원소를 순회하며 i to j를 실행한다. 가독성을 위해 많은 띄어쓰기를 넣었습니다...

 

[(0,2) (0,2)    ,     (0,2) (1,1)    ,    (0,2) (2,3)   ,   (0,2) (3,4)    ,  (0,2) (4,1)]

 

마찬가지로 다음 i 원소인 (1,1)을 기준으로 실행

[ (1,1) (0,2)   ,     (1,1)  (1,1)    ,   (1,1) (2,3)   ,   (1,1) (3,4)    ,    (1,1)  (4,1)]

 

이런식으로 하면 반복하면 

[(0,2) (0,2)    ,     (0,2) (1,1)    ,    (0,2) (2,3)   ,   (0,2) (3,4)    ,  (0,2) (4,1)

(1,1) (0,2)   ,      (1,1)  (1,1)   ,    (1,1) (2,3)   ,   (1,1) (3,4)    ,  (1,1)  (4,1)

(2,3) (0,2)   ,      (2,3)   (1,1)   ,   (2,3)  (2,3)  ,   (2,3) (3,4)    ,  (2,3)   (4,1)

(3,4) (0,2)   ,      (3,4)   (1,1)   ,   (3,4)  (2,3)   ,  (3,4) (3,4)    ,  (3,4)   (4,1)

(4,1) (0,2)   ,      (4,1)   (1,1)   ,   (4,1)  (2,3)   ,   (4,1) (3,4)    ,  (4,1)   (4,1)]

 

이런식의 리스트가 반환됩니다.

 

.filter { it.first.index != it.second.index }

 

서로 다른 인덱스의 있는 두 수를 더해주기 위해서는 이 리스트에서 filter를 통해 (0,2)(0,2) , (1,1)(1,1) , (2,3)(2,3) ,  (3,4) (3,4)  , (4,1)(4,1) 과 같은 인덱스 값이 같은 원소들을 제외하고 인덱스 값이 다른 원소들만 걸러줍니다.

 

[                  ,     (0,2) (1,1)    ,    (0,2) (2,3)   ,   (0,2) (3,4)    ,  (0,2) (4,1)

(1,1) (0,2)   ,                           ,    (1,1) (2,3)   ,   (1,1) (3,4)    ,  (1,1)  (4,1)

(2,3) (0,2)   ,      (2,3)   (1,1)   ,                        ,   (2,3) (3,4)    ,  (2,3)   (4,1)

(3,4) (0,2)   ,      (3,4)   (1,1)   ,   (3,4)  (2,3)   ,                        ,  (3,4)   (4,1)

(4,1) (0,2)   ,      (4,1)   (1,1)   ,   (4,1)  (2,3)   ,   (4,1) (3,4)    ,                      ]

 

.map { it.first.value + it.second.value }

 

원소들의 value 를 합해준 리스트를 반환합니다.

[3, 5, 6, 3, 

3, 4, 5, 2,

5, 4, 7, 4,

6, 5, 7, 5,

3, 2, 4, 5] 

 

.toSortedSet()

 

Set 컬렉션의 가장 큰 특징인게 중복된 원소가 없는 없다는것입니다.

toSortedSet() 으로 set 컬렉션형태로 바꾸고 오름차순으로 정렬해줍니다.

 

그리고 마지막은 반환형태에 맞게 toIntArray()로 맞춰줍니다.

 

끄읕

 

c 코드로 짜면 이중 for문이 들어가게 짤 텐데.. 코틀린은,,

class Solution {
    fun solution(array: IntArray, commands: Array<IntArray>): IntArray {
        var answer = intArrayOf()
        answer = commands.map{ array.slice(it[0]-1 until it[1]).sorted()[it[2]-1] }.toIntArray()
        return answer
    }
}

 

한 줄로 코틀린스럽게,,,

 

우선 map으로 원소 하나 하나에 접근을 해주고 slice로 배열의 원하는 만큼 짤라서 정렬시켜주고 그 배열의 특정 인덱스 값 원소를 가져와서 IntArray로 바꿔서 리턴해준다.

 

 

 

처음 이 문제를 생각했을 땐 굉장히 복잡하게 느껴졌다.

 

mapIndexed로 접근해서 인덱스 값으로 접근을 해볼까 이런식으로,,

정렬을 보면 sort를 떠올려야하는데 아직 많이 부족하다..

 

한참을 헤매다가 결국 검색을 했다.. 

 

검색결과 다중 정렬이라는 아주 멋지고 간단한 방법을 발견했다.

 

하나 이상의 기준을 가지고 정렬을 해야할 때 사용하면 정말 쉽게 구현가능하다.

 

바로 sortedWith() 함수와 compareBy를 comparator로 사용하는 것이다.

 

코드를 먼저 보자.

 

class Solution {
    fun solution(strings: Array<String>, n: Int): Array<String> {
        var answer = arrayOf<String>()
        
        answer = strings.sortedWith(compareBy({it[n]}, {it})).toTypedArray()
        
        return answer
    }
}

 

우선 sortedWith 함수는 원본 리스트를 바꾸지 않고 인자로 comparator를 받아 여러개의 정렬 기준으로 새로운 리스트를 반환한다.

 

여기서는 compareBy라는 comparator를 사용하여 두 개의 정렬 기준에서 우선 순위를 부여하여 첫번째로 it[n]을 기준으로 정렬하고 그 후에 그 기준을 적용시킨 채로 두 번째 정렬 기준인 기본 오름차순 정렬을 해준것이다.

(tip: sortedBy는 정렬 기준이 하나 일 때 사용^^)

 

compareBy는 기본적으로 오름차순 정렬이며 내림차순으로 하고 싶을 때는 compareByDescending()을 써주면 된다.

 

+ Recent posts