일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 메타테이블
- 영화
- more effective c++
- Vector
- 예외
- 영화 리뷰
- c++
- reference
- 오블완
- Smart Pointer
- virtual function
- 티스토리챌린지
- effective stl
- exception
- Effective c++
- UE4
- 언리얼
- 비교 함수 객체
- lua
- resource management class
- 참조자
- 반복자
- 루아
- 게임
- 상속
- 스마트 포인터
- implicit conversion
- 암시적 변환
- 다형성
- operator new
Archives
- Today
- Total
스토리텔링 개발자
[Effective STL] 31. 상황별 정렬 선택하기 본문
728x90
항목 31. 정렬시의 선택 사항들을 제대로 파악해 놓자
- sort 알고리즘은 정말 좋은 알고리즘이지만.. 늘 최적은 아니다.
- 상황별 정렬 알고리즘을 알아보자.
partial_sort
- 항상 전체 데이터의 정렬이 필요하진 않다.
- 예컨대 vector<Widget>에서 가장 좋은 20개의 Widget만을 골라야 하는 경우라면,
- 상위 20위까지의 Widget만을 정렬하면 된다.
- 이럴 때 partial sort를 사용한다.
bool qualityCompare(const Widget& lhs, const Widget& rhs)
{
// lhs의 품질이 rhs의 품질보다 좋으면 true를 반환
}
// 가장 뛰어난 20개의 요소를 앞에서부터 순서대로 정렬
partial_sort(Widgets.begin(), widgets.begin() + 20, widgets.end(), qualityCompare);
nth_element
- 상위 20개가 필요하긴 한데, 순서까지는 필요 없다면 고려할만한 알고리즘이다.
- 자신이 처리할 범위 내의 객체를 정렬하되
- n번 위치(사용자가 지정한 위치)에 있는 요소만 전체 정렬된 상태에 있게 한다.
- 즉, n번 위치의 정렬만은 확실히 보장되는 알고리즘
- 위치 n의 앞에 있는 요소들은 n번 위치에 있는 요소보다 정렬 순서에서 뒤에 오지 않는다.
- 위치 n의 뒤에 있는 요소들은 n째 위치에 있는 요소보다 정렬 순서에서 앞에 오지 않는다.
// 상위 20개 요소를 widget의 앞에 놓는다.
// 단, 20개 요소 각각의 정렬은 보장되지 않는다.
nth_element(widgets.begin(), widgets.begin() + 20, widgets.end(), qualityCompare);
stable_sort
- 동등한 요소들이 여러개 있다면 미묘한 문제가 생길 수 있다.
- 만약 1등 요소가 12개, 2등 요소가 15개 있다면
- 상위 20개에 들어갈 2등 요소 8개는 어떤 기준으로 골라지게 될까?
- partial_sort와 nth_element는 이 상황에서 '내키는대로' 동작한다.
- 이 상황에서 원래 요소들의 위치를 기준으로 선택하고 싶다면 stable sort를 사용한다.
- 이 종류의 정렬 알고리즘은 두 값이 동등하다면 정렬 후에도 그 둘의 상대적인 위치가 변하지 않는다.
- 즉, 정렬 되지 않았을 때의 벡터 상대 위치가 보장된다.
- 요소 A, B가 같은 점수를 가지고, A가 앞에 있었다면, 정렬 후에도 A가 앞에 있다.
- 아쉽게도 partial_sort와 nth_element의 경우 순서 유지성을 가지는 버전이 없다.
nth_element의 다른 용도
- 주어진 범위 내의 중앙값(median)을 찾거나
- 특정한 백분위(percentile) 위치에 있는 값을 찾아낼 때도 사용할 수 있다.
vector<Widget>::iterator begin(widgets.begin());
vector<Widget>::iterator end(widgets.end());
vector<Widget>::iterator goalPostion;
// 정렬된 벡터에서 정중앙 위치에 있는 Widget을 가리킨다.
goalPosition = begin + widgets.size() / 2;
nth_element(begin, goalPosition, end, qualityCompare);
// 원하는 Widget 객체가 widgets의 처음을 기준으로 얼마나 떨어져 있는지 계산한다.
vector<Widget>::size_type goalOffset = 0.25 * widgets.size();
goalPosition = begin + goalOffset; // goalPosition은 75 / 100 위치의 요소를 가리킨다.
nth_element(begin, goalPosition, end, qualityCompare);
- 즉, nth_element는 상위 n개의 요소나 특정 위치 요소를 찾아내기만 해도 될 때 알맞다.
partition
- 상위 20개가 아니라 1, 2등급 Widget 객체만을 골라내야 하는 경우라면?
- 벡터를 정렬해서 3등급의 첫 위치를 찾아도 되겠지만, 쓸데없이 과중한 작업이 될 것이다.
- 이 알고리즘은 범위의 요소 위치를 재배열하는데,
- 조건에 맞는 요소는 모두 범위의 앞부분에 몰아넣고,
- 조건에 맞지 않는 요소는 모두 범위의 뒷부분에 몰아넣는다.
bool hasAcceptableQuality(const Widget& w)
{
// Widget 객체의 등급이 2등급 이상인지 여부 반환
}
// hasAcceptableQuality를 만족하는 모든 Widget 객체는 widgets의 앞으로 몰고,
// 만족하지 않는 Widget 객체 중 처음 것의 반복자를 반환한다.
auto goodEnd = partial(widgets.begin(), widgets.end(), hasAcceptableQuality);
cout << "2등급 이상: ";
for (auto it = widgets.begin(); it != goodEnd; ++it)
cout << *it << " ";
cout << "\n";
cout << "2등급 이하: ";
for (auto it = goodEnd; it != widgets.end(); ++it)
cout << *it << " ";
cout << "\n";
- stable 버전도 존재한다.(stable_partition)
정렬 알고리즘 주의점
- 임의 접근 반복자와 함께 사용하므로 vector, string, deque, C++ 배열만 사용 가능하다.
- 표준 연관 컨테이너는 애초에 정렬되는 컨테이너이므로 정렬 알고리즘을 쓸 이유가 없다.
- 하지만 list는 정렬 알고리즘을 사용하지 못한다.
- 대신 sort 멤버 함수를 제공한다.(stable한 정렬 기능이다.)
- 즉, partial_sort나 nth_element는 불가능하다.
- 간접적인 list 정렬들
- 다른 컨테이너에 요소를 옮긴 후 알고리즘을 적용시키기.
- list::iterator의 컨테이너를 만들어서 알고리즘 적용시킨 후, 해당 컨테이너 반복자로 리스트 요소 데이터 엑세스하기
- list::iterator의 컨테이너를 만들어서 알고리즘 적용시킨 후, 컨테이너 정보를 써서, 원하는 위치에 리스트의 요소 데이터를 splice하기
- 다만 partition의 경우 양방향 반복자 이상이면 모두 동작 가능하므로
- STL 표준 시퀀스 컨테이너 모두에 대해 동작한다.
정리
- vector, string, deque, C++ 배열에 대해
- 전체 정렬을 원한다면 sort, stable_sort
- 상위 n개 요소까지만 정렬을 원한다면 partial_sort
- 상위 n개 요소를 뽑되, 순서는 필요 없다면 nth_element
- vector, string, deque, C++ 배열, 표준 시퀀스 컨테이너에 대해
- 특정 기준에 만족하는 것과 그렇지 않은 것을 모아 구분하고 싶다면 partition, stable_partition
- list
- partition, stable_partition은 직접 사용 가능하다.
- sort는 멤버 변수로 제공한다.
- 나머지 기능이 필요하다면 간접적인 방법을 사용해야 한다.
- 알고리즘의 수행 성능 순위
- partition
- table_partition
- nth_element
- partial_sort
- sort
- stable_osrt
728x90
'개발 > Effective STL' 카테고리의 다른 글
[Effective STL] 33. 포인터 요소에 remove 알고리즘 사용 주의하기 (0) | 2024.12.20 |
---|---|
[Effective STL] 32. remove-erase 관용구 (0) | 2024.12.19 |
[Effective STL] 30. 알고리즘 사용 시 목적지 범위(destination range) 주의 (0) | 2024.12.17 |
[Effective STL] 29. istream_iterator vs istreambuf_iterator (0) | 2024.12.16 |
[Effective STL] 28. 반복자 base() 유의점 (0) | 2024.12.13 |
Comments