본문 바로가기

Problem Solving

99클럽 코테 스터디 3일차 TIL - 프로그래머스 문자열 내 마음대로 정렬하기

문제

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

 

문제 접근

설명

이 문제는 주어진 문자열 배열 strings를 정렬하는 문제입니다. 정렬 기준은 다음과 같습니다.

  1. 문자열 원소의 n번째 문자를 기준으로 오름차순
  2. n번째 문자가 동일한 경우 알파벳 순으로 오름차순

접근법

문자열의 정렬 기준을 정의할 Comparator를 구현하고, Arrays.sort 메서드 호출로 strings 배열을 정렬하면 됩니다.

 

문제 풀이

1. Comparator 구현

 다음은 Comparator를 직접 구현한 풀이입니다.

import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public String[] solution(String[] strings, int n) {
        
        Arrays.sort(strings, (s1, s2) -> {
            if(s1.charAt(n) == s2.charAt(n)) return s1.compareTo(s2); // 2) n번째 문자가 동일하다면 문자열 자체를 알파벳 순으로 정렬
            return Character.compare(s1.charAt(n), s2.charAt(n)); // 1) n번째 문자가 다르다면 해당 문자를 알파벳 순으로 정렬
        });
        
        return strings;
    }
}

 

두 개의 문자열 s1, s2에 대해 어떻게 정렬할 것인지 작성하면 됩니다.

 

첫 번째로 s1의 n번째 문자와 s2의 n번째 문자를 추출하여 비교합니다. 만약 두 문자가 같지 않다면 Character.compare를 호출해 문자(char)를 알파벳 순으로 정렬합니다.

Character.compare

 

Character.compare 메서드는 알파벳 순서 상 x가 y 보다 앞에 와야 할 경우 음수를 리턴합니다. 반대로 x가 y 보다 뒤에 와야 할 경우 양수를 리턴합니다.

 

두 번째로 s1의 n번째 문자와 s2의 n번째 문자가 같다면, s1과 s2 문자열(String) 자체를 비교합니다. String에는 기본적으로 compareTo 메서드가 오버라이드 되어있습니다. 이 메서드는 문자열을 알파벳 순으로 정렬합니다.

 

2. Comparator.comparing 활용

 Comparator를 직접 구현할 경우 두 개의 문자열에 대해 어떤 기준으로 어떻게 정렬할 것인지 방법을 일일히 작성해야 했습니다. 이때 Comparator.comparing을 활용하면 어떤 값을 기준으로 정렬할 것인지만 명시해주면 되어 더 편리합니다.

import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public String[] solution(String[] strings, int n) {        
        Arrays.sort(strings, Comparator
                    .comparing((String s) -> s.charAt(n)) // n번째 문자 비교
                    .thenComparing(s -> s)); // 문자열 자체 비교
        return strings;
    }
}

 

Comparator.comparing은 Function이라는 함수형 인터페이스를 인자로 받습니다.

Comparator.comparing

 

comparing 메서드는 비교 대상인 c1과 c2를 각각 keyExtractor.apply 메서드의 인자로 넣어 호출합니다. 

 

Function 함수형 인터페이스

 

Function 함수형 인터페이스는 apply 라는 메서드를 가집니다.

 

이제 Comparator.comparing 메서드와 Function 함수형 인터페이스인 keyExtractor의 동작 방식을 살펴보겠습니다.

  1. Comparator.comparing 메서드는 비교 대상인 c1과 c2를 각각 keyExtractor.apply 메서드의 인자로 넣어 호출합니다.
  2. apply 메서드는 c1과 c2의 특정 값을 반환합니다.
  3. 두 값은 Comparator.comparing 메서드 내부에서 compareTo로 비교됩니다.
Arrays.sort(strings, Comparator
            .comparing((String s) -> s.charAt(n)) // n번째 문자 비교
            .thenComparing(s -> s)); // 문자열 자체 비교

 

즉, (String s) -> s.charAt(n) 람다식과 s -> s 람다식은 비교 기준이자 keyExtractor의 apply 메서드를 구현한 것입니다. strings 배열 내부의 문자열은 Comparator.comparing에 의해 각각 keyExtractor.apply의 인자로 호출된 후, 추출된 값은 다시 compareTo에 의해 비교되어 정렬되는 것입니다.

 

3. 단순 비교

Arrays.sort로 알파벳 순으로 strings 배열을 정렬한 뒤, n번째 문자 기준 오름차순으로 단순 비교해 배열을 생성하는 방법도 있습니다. 이 방법은 Comparator를 사용하는 것 보다 훨씬 빨랐습니다.

왼쪽: Comparator / 오른쪽: 단순 비교

 

import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public String[] solution(String[] strings, int n) {
        String[] answer = new String[strings.length];
        int idx = 0;
        
        Arrays.sort(strings); // 1. 알파벳 순으로 정렬
        
        for(char c='a'; c<='z'; c++) // 2. n번째 문자 기준으로 정렬
            for(String str : strings)
                if(str.charAt(n) == c)
                    answer[idx++] = str;
        
        return answer;
    }
}

 

정리

 문자열의 정렬 기준을 Comparator로 재정의 하는 것이 관건인 문제였습니다. Comparator를 사용하면 어떤 값을 기준으로 어떻게 정렬할 것인지를 작성해야 했다면, Comparator.comparing을 활용하면, 어떤 값을 정렬 기준으로 사용할지만 작성하면 되어 편리했습니다.

 

위 문제에서는 오름차순 정렬이었기에 comparing을 활용할 때 별도의 처리를 하지 않았지만, 만약 내림차순 정렬이라면 reversed()로 체이닝을 해야 합니다.