일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 알고리즘
- SPA
- Spring
- SWEA
- 타입변수
- 파이프 옮기기
- 드래그방지
- 인공지능
- 리스트
- let과var차이
- BOJ17070
- 텐서플로우
- 주피터
- 리눅스
- 딥러닝
- 스프링
- 타입제한
- BFS
- 서버사이드랜더링
- Java
- 리스트구현
- 연결리스트구현
- 파이썬
- 고쳐야해!
- 백준
- 검색어최적화
- BOJ
- 머신러닝
- spa 라우팅
- jnut
- Today
- Total
林's
[STL] std::sort와 비교함수 오버라이딩 본문
C++의 STL에서는 자바의 Collection.sort 와 같은 std::sort라는 아주 막강한 정렬함수가 존재합니다.
함수의 형태는 다음과 같이 두가지가 있으며 algorithm 헤더파일 안에 정의되어 있습니다.
-
std::sort(시작주소, 끝주소)
- ex). sort(begin(vec), end(vec)); // vec은 벡터를 의미합니다.
-
std::sort(시작주소, 끝주소, 비교함수*)
- ex). sort(vec.begin(), vec.end(), compare);
- // vec.begin() 과 begin(vec)은 결과가 같습니다. compare는 사용자 정의 비교함수를 가리키는 포인터로 함수는 함수명 자체로 포인터이기 때문에 이름만 적어주셔야만 합니다.
- ex). sort(vec.begin(), vec.end(), compare);
1번과 2번을 나눈 이유는 무엇일까요?
cpp reference 의 말을 빌리자면, sort함수가 하는 일이
Sorts the elements in the range [first, last) in ascending order. [시작주소, 끝주소) 범위의 요소들을 오름차순 정렬합니다. |
라고 적혀있네요. sort 함수는 기본적으로 요소들을 오름차순 정렬하도록 구현되어 있나봐요.
그렇다면, 정수배열에 숫자를 막 담아본다음에 sort함수를 사용하여 정렬을 시켜보도록 하겠습니다.
[0]. 준비물
#include <string>
#include <iostream>
#include <algorithm>
using namespace std; 를 꼭 코드 상단에 입력해주고 실습을 진행해주세요~
[1]. built-in 타입의 오름차순 정렬
int arr[] = { 5, 4, 3, 1, 2 }; sort(begin(arr), end(arr), compare); for (size_t i = 0; i < sizeof(arr)/4; i++) { cout << arr[i] << " "; } cout << endl;
출력해보시면, 1, 2, 3, 4, 5 로 오름차 된 결과물을 얻으실 수 있으실 겁니다.
내림차순 정렬을 하기위해서는 지금부터 compare 함수를 우리가 직접 정의해줘야합니다.
(간단히 양의 정수만을 정렬시킨다면 -를 곱해서 넣은다음 sort 시키고 -를 붙여서 출력시에 -를 한 번더 곱해주면 되겠지만..., (다익스트라 알고리즘의 우선순위 큐에서 많이 사용하는 방법이죠? ))
객체의 경우에는 조금있다 다시 설명드리겠지만, '<' 연산자 오버로딩을 통해 compare 함수와 같은 기능을 수행하게 할 수 있습니다. 이 경우에는 첫 번째 함수를 사용하면 자동으로 적용이 됩니다.
아래와 같은 함수를 작성하고 함수의 이름을 두 번째 sort함수의 세번째 인자로 주고 실행해봅시다.
[2]. 비교함수 구현 규칙
bool compare(const int & a, const int & b) // 반환값이 true 이면, 왼쪽이 오른쪽보다 작다고 생각한다. { return a < b; }
int arr[] = { 5, 4, 3, 1, 2 }; sort(begin(arr), end(arr), compare); for (size_t i = 0; i < sizeof(arr)/4; i++) { cout << arr[i] << " "; } cout << endl;
이 경우엔 내림차순 정렬된 모습을 확인하실 수 있어요~
왜 그렇게 나오는 지에 대해선 sort함수의 구현 규칙을 이해해야합니다.
함수명과 매개변수 명은 자유입니다. 그러나 다음의 규칙을 따라주셔야합니다.
1). 반환타입: bool
2). 매개변수: 같은 타입의 매개변수 두개
3). return 값의 의미:
true 이면 sort함수는 왼쪽이 오른쪽보다 먼저 나오게 해줍니다.
따라서 내림차 정렬을 하기 위해서는 return 문의 비교연산자 방향을 반대로 해주시면 되겠습니다.
return a >= b 로 바꾸고 실행해보세요~
[3]. 객체를 정렬하는 방법
자, 여기까지 기본타입들에게만 적용되는 방법을 알아보았는데요.
실제로는 기본타입뿐만 아니라 컨테이너 안의 객체들을 정렬해야할 상황이 올 수도 있으므로
컨테이너의 객체들을 정렬하는 방법을 알아보겠습니다.
여기서부터는 사전지식이 필요합니다.
바로 연산자 오버로딩에 관한 내용인데요. 이를 먼저 학습하시고 오신다음 읽으시는 것을 추천드립니다.
방법은 총 2가지가 있습니다.
1). 클래스 안에서 < 연산자 오버로딩하기
2). 위에서 살펴본 것처럼 compare 함수를 만들어서 인자로 주기
저는 공부할 때 잘 와닿지 않았는데. 코드를 통해 확인해보니 이해가 갔으며, 둘 다 쓸 수 있게 연습해둬야 겠다는 생각을 했습니다. 같이 따라해봐요!
class User { public: pair<string, string> m_info; User(string name, string id) { m_info.first = name; m_info.second = id; } };
크게 복잡한 클래스는 아니죠? 페어객체가 달랑 하나 들어있는 User 라는 클래스일뿐입니다. 겁먹지 마세요!
info 라는 페어객체에는 사용자 이름과 아이디가 들어가 있습니다.
이제 이 친구를 벡터에 세개 정도 넣어서 이름을 사전순으로 정렬해서 출력해보려고 해요.
(string 클래스에는 < 비교 연산자가 오버로딩 되어 있습니다. 사전순 비교가 가능하니 C string에 익숙하신 분들은 오해없으시길 바랍니다 ㅎㅎ)
앞서 두 가지 방법이 있다고 했었는데. 두 번째 방법을 앞에서 사용해 봤으니 그걸로 먼저 해보도록 할까요?
이름이 같은 친구들도 존재할 수 있을 것이라 생각해서 id는 유일하니, id의 사전 순서가 더 빠른 친구를 먼저 나타나게 하려면 다음과 같이 프로그래밍을 할 수 있을 것 같아요.
bool compareObject(const User & a, const User & b) { if (a.m_info.first < b.m_info.first) return true; else { if (a.m_info.first == b.m_info.first) return a.m_info.second < b.m_info.second; else return false; } }
a와 b를 비교하는데, info.first 는 이름이었죠? a의 이름이 더 빨리 나오는 경우는 바로 true 라고 해주면 되지만,
a와 b의 이름이 동일한 경우에는 아이디가 더 빠른 친구가 앞으로 오게 해달라는 의도가 담긴게 두 번째 if문 입니다.
그렇지 않은 경우에는 더 뒤에 나오면 되니 false를 반환하면 되겠죠?
저는 다음과 같은 데이터를 가지고 실험을 해보았습니다. 출력도 함께 해보죠.
User a("백윤기","1"); User b("김태원", "3"); User c("김태원", "2"); vector<User> vec; vec.push_back(a); vec.push_back(b); vec.push_back(c);
sort(begin(vec), end(vec), compareObject); for (const auto & user : vec) { cout << user << " "; }
a,b,c 중에서 이름이 가장 빠른 친구는 김태원 친구이지만 동일한 인물이 존재하고 아이디가 더 빠른 사람이 앞으로 오겠죠? 그리고 백윤기라는 친구는 이름이 다른 친구들보다 뒤에 나오니 가장 마지막에 나타나게 됩니다.
그리고, User객체를 출력하기 위해서는 다음과 같이 << 연산자를 클래스 안에서 오버로딩 해야합니다.
friend ostream& operator << (ostream& out, const User & rhs) { out << rhs.m_info.first << "(" << rhs.m_info.second << ")"; return out; }
이제 마지막으로 연산자 오버로딩을 통한 비교함수 구현을 해보고 포스팅을 마무리 하도록 하겠습니다.
방법은 간단 합니다. 위에서 작성한 비교함수의 내용을 그대로 복사해주시면 되는데요. 변수명이 조금 바뀌는 것 빼고는 차이점이 없습니다.
bool operator < (User & rhs) { if (m_info.first < rhs.m_info.first) return true; else { if (m_info.first == rhs.m_info.first) return m_info.second < rhs.m_info.second; else return false; } }
연산자 오버로딩에 대한 이해가 부족하신 분은 미리 공부해 오신다면, 손 쉽게 이해할 수 있으실 겁니다.
이렇게 클래스 내부에서 < 연산자 오버로딩이 되면, 다음과 같이 sort 함수에서 세 번째 인자를 적어주지 않으셔도 됩니다.
sort(begin(vec), end(vec)); for (const auto & user : vec) { cout << user << " "; }
이상으로 sort 함수의 compare 함수를 오버로딩하여 컨테이너와 배열을 정렬하는 방법에 대해 살펴보았습니다.
연산자 오버로딩에 대한 이해가 부족하신 분들은 이번 포스팅이 많이 어려울 수 있을 것이라 생각합니다.
그런 분들을 위해서 유튜브 강좌 링크를 제공하오니, 도움이 되셨으면 좋겠습니다.
긴 글 읽어주셔서 감사드리고
질문과 피드백은 언제나 환영입니다.
즐거운 코딩 되세요 ^^
'프로그래밍 > 현대적 C++ 프로그래밍' 카테고리의 다른 글
프로그램의 구조 (0) | 2019.04.07 |
---|---|
[C++] 상속의 접근제한자 (0) | 2019.04.06 |
1. C언어와 C++의 입출력 차이 (0) | 2018.12.21 |