본문 바로가기
기술 기록/Python,Django

파이썬) 객체 리스트의 다중 정렬 (백준 10825)

by Fola 2022. 3. 27.
students.sort(key=lambda x: (-x.native_lang, x.english, -x.math, x.name))

 

객체 리스트 정렬 방법에 관한 정보만을 바로 보려면 아래 3번 문단으로

 

 

클래스로 찍어낸 객체들을 배열한 리스트에서,

객체의 여러 속성을 기준 삼아 정렬하는 알고리즘을 익히기 좋은 문제가 있어 정리.

(물론 문제만 해결하자면 객체를 만들지 않고 2차원 배열을 이용하는 방법이 더 간단하다) 

 

https://www.acmicpc.net/problem/10825

 

10825번: 국영수

첫째 줄에 도현이네 반의 학생의 수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 한 줄에 하나씩 각 학생의 이름, 국어, 영어, 수학 점수가 공백으로 구분해 주어진다. 점수는 1보다 크거나 같고, 1

www.acmicpc.net

 

 

백준 10825 번 문제의 입출력 예시 (캡쳐 이미지)
위 입력을 객체 단위로 저장하고 제시하는 다중 기준에 맞춰 정렬하려고 한다.

 

 

 

 

1. 객체를 담을 클래스는 다음과 같다

 

class Student:
    def __init__(self, name, native_lang, english, math):
        self.name = name
        self.native_lang = native_lang
        self.english = english
        self.math = math

    def __repr__(self):
        return repr((self.name, self.native_lang, self.english, self.math))

 

클래스 이름 : Student 

__init__ 생성자를 통해 이름(name)과 국어(native_lang), 영어(english), 수학(math)의 점수 value를 저장할 필드를 만든다.

__repr__() 함수는 객체 확인을 위해 편의상 작성.

 

 

 

2. 입력 값을 받아 객체를 생성하고 리스트에 담는다.

 

num = int(sys.stdin.readline())
students = []

for _ in range(num):
    input_list = list(map(str, sys.stdin.readline().split()))
    stu_1 = Student(input_list[0], int(input_list[1]), int(input_list[2]), int(input_list[3]))
    students.append(stu_1)

 

객체를 담을 객체 리스트의 이름은 students이다.

입력 케이스(num) 만큼 반복문을 돌며 stu_1 이란 객체를 생성하고 리스트에 추가한다.

 

 

 

3. 객체 리스트의 정렬

 

students.sort(key=lambda x: (-x.native_lang, x.english, -x.math, x.name))

 

파이썬의 sort 함수에는 key 값을 함수 인자로 받아 정렬 기준을 정할 수 있다.

x 는 람다식의 변수로 다른 이름으로 선언하여 사용해도 된다.

x: 다음에 오는 () 안의 코드가 정렬 기준과 순서이다.

 

부호는 오름차순/내림차순을 결정한다. -가 내림차순이다.

 

앞쪽에 위치한 필드가 우선 정렬 기준이 된다.

국어(native_lang)를 기준으로 내림차순 정렬을 한다.

만약 국어 점수의 값이 같을 경우 다음 기준 영어(english)로 넘어간다.

 

반드시 모든 필드를 key 값으로 넣을 필요는 없다.

한 가지 key값을 넣어도 동작한다.

 

그러나 key 값을 넣지 않을 경우. sort() 코드에서 TypeError 가 발생한다.

정렬하는 리스트가 다중 필드를 가지고 있어 비교할 기준이 없기 때문이다.

 

students.sort()
TypeError: '<' not supported between instances of 'Student' and 'Student'

 

 

 

4. 응용

동일한 방법으로 객체 배열뿐 아니라 튜플 리스트, 딕셔너리, 2차원 배열도 원하는 기준으로 정렬이 가능하다.

 

coordinate = []

for i in range(0, num):
    x, y = sys.stdin.readline().split()
    coordinate.append((int(x), int(y)))

coordinate.sort(key=lambda sort: (sort[1], sort[0]))

for i in coordinate:
    print(i[0], i[1])

 

x, y 좌표를 튜플 형태로 입력받은 후

y 축 오름차순 우선 정렬하고

y축 좌표가 같을 경우 x축 오름차순으로 정렬하는 코드이다.

 

람다식의 키값은 서브스트링 문법으로 접근한다.

튜플의 형태가 (x, y) 이기 때문에 s[1] 은 y를, s[0] 는 x를 의미한다.

 

만약 튜플이 (x, y, z)이고 x, y, z 순서로 정렬하고 싶다면

s[0], s[1], s[2] 순으로 key 값을 입력하면 된다.

 

 

딕셔너리의 정렬도 똑같다.

딕셔너리의 key (주의: 람다식의 key 아님)는 s[0], value는 s[1] 으로 지정한다.

 

# 딕셔너리의 value를 기준으로 정렬
dic.sort(key=lambda s: s[1])

 

 

 

5. 정리

자바에서의 comparable나 comparator를 이용한 정렬과 유사한 방법이다.

아이디어는 같지만 표현하는 문법이 크게 다르다. 

자바의 형식미와 파이썬의 단순미.

언어가 추구하는 철학을 잘 보여주는 예시라고 생각한다. 꽤 재밌는 포인트.

그리고 사실 나는 파이썬의 문법이 더 좋다.

 

 

 

 

(백준 10825번의 전체 코드)

 

# 백준 q10825

import sys


class Student:
    def __init__(self, name, native_lang, english, math):
        self.name = name
        self.native_lang = native_lang
        self.english = english
        self.math = math

    def __repr__(self):
        return repr((self.name, self.native_lang, self.english, self.math))


num = int(sys.stdin.readline())
students = []

for _ in range(num):
    input_list = list(map(str, sys.stdin.readline().split()))
    stu_1 = Student(input_list[0], int(input_list[1]), int(input_list[2]), int(input_list[3]))
    students.append(stu_1)

students.sort(key=lambda x: (-x.native_lang, x.english, -x.math, x.name))

for i in students:
    print(i.name)

 

댓글