F.R.I.D.A.Y.

프로그램에 일상을 더하다: 여러 항목 정렬하기 본문

DEV/C C++

프로그램에 일상을 더하다: 여러 항목 정렬하기

F.R.I.D.A.Y. 2020. 3. 19. 22:55
반응형

https://pixabay.com/photos/rain-rain-drop-glass-surface-4714068/

 프로그램에 일상을 더하는 첫 번째 시간, 배열을 사용한 여러 항목 정렬하기입니다.


정렬하기

 정렬이란 무엇을 뜻할까요? 네이버 사전을 찾아보면 아래처럼 알려주네요.

 특정 기준으로 줄 세우기를 하는 것입니다.

한 가지 항목 정렬하기

 우리는 먼저 한 가지 항목을 정렬해 볼 겁니다. 들어오는 값은 아래와 같습니다.

5 7 9 3 1 2 8 6 4 10
더보기

# 텍스트 데이터

5 7 9 3 1 2 8 6 4 10

 오름차순[# 뒤로 갈수록 값이 커집니다. 그래프로 그리면 점차 올라가는 모양이기 때문에 오름차순입니다. 내림차순은 그 반대이죠.]으로 정렬을 진행해보겠습니다. 값을 입력받는 코드는 아래와 같습니다.

#include <stdio.h>

int main(void){
	int arr[10];
	
	for(int i = 0 ; i< 10; ++i){
		scanf("%d", arr +i);
	}
	
	return 0;
}

 선택 정렬을 이용해볼 겁니다. 좋은 알고리즘이라 할 수는 없지만 정렬 알고리즘 자체를 공부하는 건 아니니까요.

for(int i = 0; i < 9; ++i){
    int maxIndex = i;
    for(int k = i+1; k < 10; ++k){
        if(arr[maxIndex] > arr[k]) maxIndex = k;
    }
    int temp = arr[i];
    arr[i] = arr[maxIndex];
    arr[maxIndex] = temp;
}

 앞선 값이 더 크면 이후의 값과 교환하도록 부등호를 >로 정해주었습니다.

 이 코드를 사용하면 1부터 10까지 차례로 나올 것입니다.

 잘 나오네요. 한 가지 정렬 기준으로 한 가지 항목을 정렬하는 것은 쉽습니다. 문제는 이제부터입니다.

하나의 정렬 기준, 여러 항목 정렬하기

 정렬하는 문제는 학생을 성적순으로 등수를 매기는 경우가 많습니다. 학생의 성적은 단일 과목에 대한 성적도 있겠지만, 여러 과목에 대한 종합적인 성적도 존재합니다. 단일 과목은 위의 한 가지 항목 정렬 부분에서 다루었으니 넘어가고, 여러 과목에 대한 종합 성적을 처리해야 한다고 보겠습니다.

 다섯 명의 학생이 국, 영, 수, 사, 과학까지 총 다섯 개의 과목을 시험 봤습니다. 각 학생의 점수는 다음과 같습니다.

학생(번호) 국어 영어 수학 사회 과학
A(1) 90 85 72 83 96
B(2) 91 78 90 60 79
C(3) 92 90 83 75 84
D(4) 83 60 70 93 65
E(5) 70 100 74 80 91
더보기

# 학생에 대한 텍스트 데이터

1 90 85 72 83 96

2 91 78 90 60 79

3 92 90 83 75 84

4 83 60 70 93 65

5 70 100 74 80 91

 우리는 수학을 기준으로 학생의 등수를 매길 것입니다. 그리고 한 가지 더, 우리는 배열만을 이용할 겁니다. 기본 입력은 아래와 같겠네요.

#include <stdio.h>

int main(void){
    int student[5], korean[5], english[5], math[5], society[5], science[5];
    
    for(int i = 0 ; i < 5; ++i){
        scanf("%d %d %d %d %d %d", student+i, korean+i, english+i, math+i, society+i, science+i);
    }
    
    /* sorting code */
    
    printf("\n");
    for(int i = 0 ; i < 5; ++i){
        printf("%c %d %d %d %d %d\n", student[i]/* +64 */, korean[i], 
                                    english[i], math[i], society[i], science[i]);
    }
    
    return 0;
}

 각 과목의 점수만 입력으로 받는 것이 아니라 학생을 구분 지을 수 있도록 학생의 번호를 입력[# 문자로 입력받지 않은 이유는 코드를 줄이기 위함입니다. 궁금하다면 student의 자료형을 char로, 그리고 %d가 아닌 %c로 받도록 코드를 수정하고 프로그램을 실행해보세요.] 받을 수 있도록 student 변수를 추가로 생성했습니다.

 이제 하나의 과목을 정렬했던 것처럼 정렬을 진행해보겠습니다. 성적은 더 높은 값이 앞서야 합니다. 내림차순으로 정렬해야겠죠. 이번에도 선택 정렬을 이용합니다. 과학 점수를 이용해 정렬을 진행해보겠습니다.

한셀(엑셀)에서의 과학 점수를 기준으로 한 내림차순 정렬 결과

 프로그램을 잘 작성했다면 그림과 같이 정렬되어 나올 것입니다. 그럼 위의 <한 가지 항목 정렬하기>에서 작성했던 대로 만들어 보겠습니다.

for(int i = 0; i < 4; ++i){
    int maxIndex = i;
    for(int k = i+1; k < 5; ++k){
        if(science[maxIndex] < science[k]) maxIndex = k;
    }
    int temp = science[i];
    science[i] = science[maxIndex];
    science[maxIndex] = temp;
}

 이 코드를 적용해 정렬을 진행해보도록 하지요.

현재 코드로 정렬한 결과(좌)와 그 결과를 토대로 만든 표(우)

 과학 부분은 똑같지만, 다른 부분은 정렬이 되지 않았습니다. 여기서 중요한 것이 있습니다.

핵심

 우리가 어떤 대상을 기준으로 정렬을 진행합니다. 가령 가족 중 세 번째로 나이가 많은[# 대부분 자식 중 첫째가 되겠네요.] 구성원을 기준으로 가족을 정렬한다고 칩시다. 그렇다면 기준 대상이 되는 세 번째 구성원만 이동할까요? 아닙니다. 가족 전체가 세 번째 구성원이 이동하는 곳으로 함께 이동하겠죠. 이때, 가족의 다른 구성원이 이동하는 시점은[# 다른 구성원이 이미 가야 할 자리를 알고 있는 경우는 제외한다면] 세 번째 구성원이 이동하는 시점이 될 것입니다.
 프로그램도 마찬가지입니다. 결국 기준이 되는 데이터로 여러 항목을 정렬해야 한다면, 기준 데이터를 정렬할 때 함께 정렬하면 됩니다.

for(int i = 0; i < 4; ++i){
    int maxIndex = i;
    for(int k = i+1; k < 5; ++k){
        if(science[maxIndex] < science[k]) maxIndex = k;
    }
    int temp = science[i];
    science[i] = science[maxIndex];
    science[maxIndex] = temp;
}

 이 정렬 코드 중에서 기준이 될 데이터를 교환하는 부분은 어디일까요?

 이 세 줄입니다. 따라서 이 부분에 순차적으로 다른 데이터도 함께 교환해주면 되겠죠.

기준 데이터를 기준으로 모든 항목을 정렬한 결과(좌)와 결과를 토대로 만든 표(우)

 이제 엑셀에서 자동으로 정렬해준 표와 동일함을 알 수 있습니다. 일상생활에서는 기준으로 그룹을 이동시킬 때 해당 그룹 자체를 이동시키는 것은 당연한 것이기에 이렇게 하나하나 값을 옮겨주어야 하는 경우는 생각하기 힘들 수 있습니다. 프로그램은 한 번에 하나의 작업만 가능하기 때문에 이런 식으로 배열을 이용해 값을 관리할 때는 여러 데이터를 동시에 옮긴다는 생각보다는 하나씩 옮긴다는 생각으로 임해주기 바랍니다.

 물론 배열이 아닌 구조체를 이용했다면 이렇게 여러 번에 걸쳐 교환해야 하는 문제가 없지만요.

더보기

# 최종 코드

#include <stdio.h>

int main(void){
    int student[5], korean[5], english[5], math[5], society[5], science[5];
    
    for(int i = 0 ; i < 5; ++i){
        scanf("%d %d %d %d %d %d", student+i, korean+i, english+i, math+i, society+i, science+i);
    }
    
    for(int i = 0; i < 4; ++i){
        int maxIndex = i;
        for(int k = i+1; k < 5; ++k){
            if(science[maxIndex] < science[k]) maxIndex = k;
        }
        int temp = science[i];
        science[i] = science[maxIndex];
        science[maxIndex] = temp;
        
        temp = korean[i];
        korean[i] = korean[maxIndex];
        korean[maxIndex] = temp;
        
        temp = english[i];
        english[i] = english[maxIndex];
        english[maxIndex] = temp;

        temp = math[i];
        math[i] = math[maxIndex];
        math[maxIndex] = temp;

        temp = society[i];
        society[i] = society[maxIndex];
        society[maxIndex] = temp;
        
        temp = student[i];
        student[i] = student[maxIndex];
        student[maxIndex] = temp;
        
    }
    
    printf("\n");
    for(int i = 0 ; i < 5; ++i){
        printf("%c %d %d %d %d %d\n", student[i] /*+64*/, korean[i], 
                                    english[i], math[i], society[i], science[i]);
    }
    
    return 0;
}

 여러 번 같은 작업을 반복하는 경우라면 함수나 전처리기를 이용한 매크로 함수를 이용하면 편리합니다.


오늘의 생각하기

하나의 작업을 더 작은 작업으로 쪼개 보기

# index

728x90
반응형

'DEV > C C++' 카테고리의 다른 글

C: volatile 키워드  (0) 2020.08.22
C: 숫자 지그재그로 출력하기  (0) 2020.07.24
XOR: 배타적 논리합  (0) 2020.02.28
부동소수점  (2) 2020.02.22
포인터(pointer) part4. 함수 포인터 배열  (0) 2020.01.30
Comments