내림차순으로 벡터 정렬


310

사용해야합니까

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

또는

std::sort(numbers.rbegin(), numbers.rend());   // note: reverse iterators

내림차순으로 벡터를 정렬하려면? 하나의 접근 방식이나 다른 접근 방식의 이점이나 단점이 있습니까?


2
+1 답은 명백하지만이 질문에는 흥미로운 내용이 있습니다. :)
wilhelmtell

3
나는 첫 번째 옵션에 투표했는데, 그 이유는의를 다룰 필요가 없기 때문 reverse_iterator입니다.
evandrix

2
@wilhelmtell 멍청한 질문이지만 왜 두 번째 질문은 내림차순으로 정렬해야합니까? sort 메소드의 입력과 동일한 배열을 제공합니다. ar.begin ()과 ar.end의 경우처럼 오름차순이 아닌 내림차순으로 정렬 해야하는 이유는 무엇입니까?
shshnk

6
@shshnk의 std::sort(b, e);풋에서 최소 b(우리의 경우에 rbegin, 이렇게 마지막 요소)과의 최대 e(우리의 경우에 rend, 소위 소자).
fredoverflow 2016

답변:


114

사실, 첫 번째는 나쁜 생각입니다. 두 번째 또는이 중 하나를 사용하십시오 .

struct greater
{
    template<class T>
    bool operator()(T const &a, T const &b) const { return a > b; }
};

std::sort(numbers.begin(), numbers.end(), greater());

그렇게하면 누군가가 또는 대신 대신 numbers해야한다고 결정할 때 코드가 자동으로 중단되지 않습니다 .longlong longint


1
@FredOverflow : 당신은 당신의 의견에 명예를했다;)
user541686

2
또는 첫 번째 것을 고수하십시오. stCon :: sort (numbers.begin (), numbers.end (), std :: greater <numContainer :: value_type> ( ));
RichardHowells

1
+1 첫 번째는 정말 혼란 스럽다. greater다른 것보다 무엇입니까 ? rbeginrend특정 목적을 위해 만들어졌다.
Abhishek Divekar

6
std::greater<typename decltype(numbers)::value_type>()또는 무엇을하지 않습니까?
einpoklum

1
이 답변은 구식입니다 std::greater<>()-C ++ 14부터 사용할 수 있습니다 .
Nikolai

70

첫 번째를 사용하십시오.

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

오독의 적은 기회 - 그것은에 무슨 일이 일어나고 있는지의 명시 적이다 rbeginbegin도 코멘트와 함께. 명확하고 읽을 수 있으며 정확히 원하는 것입니다.

또한 두 번째는 리버스 반복자의 특성으로 인해 첫 번째보다 효율이 떨어질 수 있지만 확실하게 프로파일 링해야합니다.


68

c ++ 14를 사용하면 다음을 수행 할 수 있습니다.

std::sort(numbers.begin(), numbers.end(), std::greater<>());

30

이건 어때?

std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());

13
추가적인 복잡성을 피하는 이유는 다음과 같습니다 : O (n * log (n)) + O (n) vs O (n * log (n))
greg

32
@greg O (n * log (n)) = O (n * log (n) + n). 이들은 동일한 세트를 정의하는 두 가지 방법입니다. "이것은 느려질 수 있습니다."
pjvandehaar

4
@pjvandehaar Greg는 괜찮습니다. 그는 O (n * log (n) + n)라고 명시 적으로 말하지 않았으며 O (n * log (n)) + O (n)이라고 말했습니다. 그의 말이 명확하지 않은 것 (특히 그의 복잡성에 대한 그의 오용)은 맞지만, 당신은 더 친절한 방법으로 대답 할 수있었습니다. 예 : '복잡성'이라는 단어 대신 '계산'이라는 단어를 사용하려고했을 수도 있습니다. 숫자를 반대로 바꾸는 것은 다른 O (n * log (n)) 단계로 불필요한 O (n) 단계입니다.
Ofek Gila

3
@OfekGila 저의 이해는 big-O 표기법은 함수와 표기법에 관한 =것이며 +편의를 의미 하고 단지 편의를 의미한다는 것 입니다. 이 경우, O(n*log(n)) + O(n)수있는 편리한 표기법 O(n*log(n)) ∪ O(n)하는 동일하다 O(n*log(n)). "computation"이라는 단어는 좋은 제안이며 그 말에 대해 옳습니다.
pjvandehaar

22

Mehrdad가 제안한 functor 대신 Lambda 함수를 사용할 수 있습니다.

sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });

16

내 컴퓨터에 따르면 long long첫 번째 방법을 사용하여 [1..3000000] 의 벡터를 정렬하는 데는 약 4 초가 걸리고 두 번째 방법을 사용하면 약 두 배가 걸립니다. 그것은 분명히 무언가를 말하지만, 나는 왜 그런지 이해하지 못합니다. 이것이 도움이 될 것이라고 생각하십시오.

여기에 동일한 내용이보고 되었습니다 .

Xeo가 말했듯이, -O3그들은 거의 같은 시간을 사용하여 마무리합니다.


12
최적화가 설정된 상태에서 컴파일하지 않았습니까? reverse_iterator작업이 인라인되지 않은 것처럼 들리며 실제 반복자를 감싸는 래퍼 일 뿐이므로 인라인없이 두 배의 시간이 걸리는 것은 놀라운 일이 아닙니다.
Xeo

@Xeo 인라인 된 경우에도 일부 구현에서는 역 참조 당 추가를 사용합니다.
Pubby

@ ildjarn : 그것은 그렇게 때문입니까? base()예를 들어, 반환에 대한 멤버 함수는 반복자를 감쌌다.
Xeo

1
@Xeo 이제 둘 다 1 초 안에 끝납니다. 감사!
zw324

3
@Xeo : 다시 가져옵니다. 표준은 실제로의 측면에서 구현되는 것을 요구 합니다 . 기괴한; 오늘 나는 배웠다. std::vector<>::reverse_iteratorstd::reverse_iterator<>
:-P

11

첫 번째 접근 방식은 다음과 같습니다.

    std::sort(numbers.begin(), numbers.end(), std::greater<>());

두 번째보다 효율성이 높아 첫 번째 방법을 사용할 수 있습니다.
첫 번째 방법의 시간 복잡도는 두 번째 방법보다 적습니다.


이것은 mrexciting의 답변과 같은 답변입니다. 복잡성에 대한 언급도 명확하지 않습니다.
Philipp Claßen

7
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);

4
유효한 답변이 되려면 OP의 언급 방법과 장점의 단점 / 단점에 대한 내용을 작성하는 것이
좋습니다.

3

TL; DR

아무거나 사용하십시오. 그들은 거의 같습니다.

지루한 답변

평소와 같이 장단점이 있습니다.

사용 std::reverse_iterator:

  • 사용자 정의 유형을 정렬하고 구현하지 않으려는 경우 operator>()
  • 입력하기에 너무 게으른 경우 std::greater<int>()

다음과 같은 std::greater경우에 사용하십시오 .

  • 보다 명확한 코드를 원할 때
  • 모호한 역 반복자를 사용하지 않으려는 경우

성능면에서 두 방법 모두 동일하게 효율적입니다. 다음 벤치 마크를 시도했습니다.

#include <algorithm>
#include <chrono>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std::chrono;

/* 64 Megabytes. */
#define VECTOR_SIZE (((1 << 20) * 64) / sizeof(int))
/* Number of elements to sort. */
#define SORT_SIZE 100000

int main(int argc, char **argv) {
    std::vector<int> vec;
    vec.resize(VECTOR_SIZE);

    /* We generate more data here, so the first SORT_SIZE elements are evicted
       from the cache. */
    std::ifstream urandom("/dev/urandom", std::ios::in | std::ifstream::binary);
    urandom.read((char*)vec.data(), vec.size() * sizeof(int));
    urandom.close();

    auto start = steady_clock::now();
#if USE_REVERSE_ITER
    auto it_rbegin = vec.rend() - SORT_SIZE;
    std::sort(it_rbegin, vec.rend());
#else
    auto it_end = vec.begin() + SORT_SIZE;
    std::sort(vec.begin(), it_end, std::greater<int>());
#endif
    auto stop = steady_clock::now();

    std::cout << "Sorting time: "
          << duration_cast<microseconds>(stop - start).count()
          << "us" << std::endl;
    return 0;
}

이 명령 행으로 :

g++ -g -DUSE_REVERSE_ITER=0 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out
g++ -g -DUSE_REVERSE_ITER=1 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out

std::greater demo std::reverse_iterator demo

타이밍은 동일합니다. Valgrind는 같은 수의 캐시 누락을보고합니다.


2

나는 두 가지 방법이 혼란 스럽기 때문에 질문에 방법 중 하나를 사용해야한다고 생각하지 않으며 두 번째 방법은 Mehrdad가 제안한 것처럼 깨지기 쉽습니다.

나는 표준 라이브러리 함수처럼 보이고 의도를 명확하게하기 때문에 다음을 옹호합니다.

#include <iterator>

template <class RandomIt>
void reverse_sort(RandomIt first, RandomIt last)
{
    std::sort(first, last, 
        std::greater<typename std::iterator_traits<RandomIt>::value_type>());
}

2
이것은 std::greater비교기를 사용하는 것보다 1000 배나 더 혼란스러워합니다 ....
Apollys는 Monica

@Apollys C ++ 14부터 std :: greater <>가 선호하는 솔루션처럼 보입니다. C ++ 14가없는 경우 std :: greater <int>를 사용하여 놀라움을 배제하려는 경우에도 유용합니다 (예 : 특정 시점의 유형이 int에서 long으로 변경되는 경우).
Philipp Claßen

2

첫 번째 코드를 사용하거나 아래 코드를 사용해보십시오.

sort(&a[0], &a[n], greater<int>());
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.