Làm cách nào để sắp xếp một vectơ các cặp dựa trên phần tử thứ hai của cặp?


133

Nếu tôi có một vectơ cặp:

std::vector<std::pair<int, int> > vec;

Có cách nào dễ dàng để sắp xếp danh sách theo thứ tự tăng dần dựa trên yếu tố thứ hai của cặp không?

Tôi biết tôi có thể viết một đối tượng hàm nhỏ sẽ thực hiện công việc, nhưng có cách nào để sử dụng các phần hiện có của STLstd::lessthực hiện công việc trực tiếp không?

EDIT: Tôi hiểu rằng tôi có thể viết một hàm hoặc lớp riêng biệt để chuyển đến đối số thứ ba để sắp xếp. Câu hỏi là liệu tôi có thể xây dựng nó từ những thứ tiêu chuẩn hay không. Tôi thực sự muốn một cái gì đó trông giống như:

std::sort(vec.begin(), vec.end(), std::something_magic<int, int, std::less>());

Dưới đây là một ví dụ: <br> std :: sort trong một vectơ của các cặp
LeppyR64

1
c ++ không có lamdas nên bạn không thể làm chính xác những gì bạn muốn, bạn sẽ cần tạo một hàm / functor riêng. Đây có thể là một lớp lót vì vậy nó thực sự không phải là một vấn đề lớn.
Evan Teran

1
C ++ có lambdas bây giờ! Woo!
David Poole

Câu trả lời:


212

EDIT : sử dụng c ++ 14, giải pháp tốt nhất là rất dễ viết nhờ lambdas hiện có thể có các tham số về loại auto. Đây là giải pháp yêu thích hiện tại của tôi

std::sort(v.begin(), v.end(), [](auto &left, auto &right) {
    return left.second < right.second;
});

Chỉ cần sử dụng một bộ so sánh tùy chỉnh (đó là đối số thứ 3 tùy chọn std::sort)

struct sort_pred {
    bool operator()(const std::pair<int,int> &left, const std::pair<int,int> &right) {
        return left.second < right.second;
    }
};

std::sort(v.begin(), v.end(), sort_pred());

Nếu bạn đang sử dụng trình biên dịch C ++ 11, bạn có thể viết tương tự bằng lambdas:

std::sort(v.begin(), v.end(), [](const std::pair<int,int> &left, const std::pair<int,int> &right) {
    return left.second < right.second;
});

EDIT : để trả lời các chỉnh sửa của bạn cho câu hỏi của bạn, đây là một số suy nghĩ ... nếu bạn thực sự muốn sáng tạo và có thể sử dụng lại khái niệm này rất nhiều, chỉ cần tạo một mẫu:

template <class T1, class T2, class Pred = std::less<T2> >
struct sort_pair_second {
    bool operator()(const std::pair<T1,T2>&left, const std::pair<T1,T2>&right) {
        Pred p;
        return p(left.second, right.second);
    }
};

sau đó bạn cũng có thể làm điều này:

std::sort(v.begin(), v.end(), sort_pair_second<int, int>());

hoặc thậm chí

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

Mặc dù thành thật mà nói, đây là một chút quá mức cần thiết, chỉ cần viết hàm 3 dòng và được thực hiện với nó :-P


Hãy nhớ rằng điều này khác với operator<trong pair<T1,T2>. Bộ so sánh mặc định sử dụng cả phần tử thứ nhất và thứ hai (trong trường hợp phần tử thứ nhất bằng nhau). Ở đây chỉ có cái thứ hai đang được sử dụng.
Googol

@Googol: Đó chính xác là những gì OP yêu cầu ... Anh ấy nói: "is there and easy way to sort the list in increasing order based on the second element of the pair?"
Evan Teran

@ evan-teran, vâng, tôi biết. Tôi chỉ chỉ ra rằng nếu cả hai phần tử giây bằng nhau, thì kết quả có thể gây nhầm lẫn (ví dụ nếu được sử dụng để sắp xếp). Vấn đề này không phải chịu bởi bộ so sánh mặc định vì nó sử dụng phần tử thứ hai để ngắt kết nối. Tôi đã đạt được câu hỏi này để tìm kiếm một bộ so sánh sử dụng yếu tố thứ hai làm thông tin chính để so sánh, nhưng tôi cũng cần nó sử dụng yếu tố đầu tiên để phá vỡ mối quan hệ, vì vậy tôi muốn tránh những người khác bỏ lỡ điểm đó (như tôi, trong thực tế, đã làm).
Googol

71

Bạn có thể sử dụng boost như thế này:

std::sort(a.begin(), a.end(), 
          boost::bind(&std::pair<int, int>::second, _1) <
          boost::bind(&std::pair<int, int>::second, _2));

Tôi không biết một cách tiêu chuẩn để làm điều này ngắn và ngắn gọn như nhau, nhưng bạn có thể lấy boost::bindtất cả bao gồm các tiêu đề.


1
+1 để sử dụng Boost. Btw, với một trình biên dịch hiện đại, có lẽ bạn đã có thể thay thế boost bằng std :: tr1 vì điều này sẽ sớm có trong tiêu chuẩn.
Andreas Magnusson

thật không may, tôi đã thử tương tự với c ++ 1x std :: bind của gcc trunk, và nó đã thất bại vì nó không có op <for bind. dunno tuy nhiên cho dù những gì c ++ 1x nói về điều này. có lẽ nó bảo bạn sử dụng lambda cho việc đó :)
Johannes Schaub - litb

1
Tôi cho rằng tăng không phải là tiêu chuẩn, nhưng nó đủ gần. :-)
David Norman

Đăng một câu hỏi tiếp theo cho câu trả lời này tại đây: stackoverflow.com/q/4184917/220636
nabulke

34

Rất đơn giản, bạn sử dụng hàm sắp xếp từ thuật toán và thêm chức năng so sánh của riêng bạn

vector< pair<int,int > > v;
sort(v.begin(),v.end(),myComparison);

Bây giờ bạn phải thực hiện so sánh dựa trên lựa chọn thứ hai để khai báo bạn là "myComparison" là

bool myComparison(const pair<int,int> &a,const pair<int,int> &b)
{
       return a.second<b.second;
}

5
Ngắn gọn và đúng trọng tâm". Không cần tăng hoặc phiên bản C ++ cụ thể. +1
Thomio

1
Điều này nên được đánh dấu là giải pháp tốt nhất. Không cần c ++ 14 để thực hiện nó.
Kartik Chauhan

Bạn có thể giải thích cho tôi cách so sánh này hoạt động? Có phải chúng ta chuyển hai phần tử cho myComparision tại một thời điểm sau đó làm thế nào nó có thể sắp xếp? Ngoài ra, a.second <b.second đóng vai trò gì?
thời đại

30

Với C ++ 0x, chúng ta có thể sử dụng các hàm lambda:

using namespace std;
vector<pair<int, int>> v;
        .
        .
sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) {
             return lhs.second < rhs.second; } );

Trong ví dụ này, kiểu trả về boolđược suy diễn ngầm.

Các loại trả lại Lambda

Khi hàm lambda có một câu lệnh đơn và đây là câu lệnh return, trình biên dịch có thể suy ra kiểu trả về. Từ C ++ 11, §5.1.2 / 4:

...

  • Nếu câu lệnh ghép có dạng biểu thức { return expression ; }được trả về sau khi chuyển đổi lvalue-to-rvalue (4.1), chuyển đổi mảng thành con trỏ (4.2) và chuyển đổi hàm thành con trỏ (4.3);
  • nếu không void.

Để chỉ định rõ ràng kiểu trả về, hãy sử dụng biểu mẫu []() -> Type { }, như trong:

sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
             if (lhs.second == 0)
                 return true;
             return lhs.second < rhs.second; } );

1
Tại sao if (lhs.second == 0)?
con lợn

Không có ý nghĩa đặc biệt; lhs.second < rhs.secondcó thể trở lại truehoặc falsetrình biên dịch có thể suy luận rõ ràng bool. Chỉ muốn chứng minh []() -> Type { }vụ việc.
Andreas Spindler

Ít nhất với clang, suy luận ngầm này có thể không hoạt động đúng, tôi đã phải thêm -> bool như kiểu trả về lambda để làm cho nó hoạt động đúng.
MoDJ

5

Đối với một cái gì đó có thể tái sử dụng:

template<template <typename> class P = std::less >
struct compare_pair_second {
    template<class T1, class T2> bool operator()(const std::pair<T1, T2>& left, const std::pair<T1, T2>& right) {
        return P<T2>()(left.second, right.second);
    }
};

Bạn có thể sử dụng nó như

std::sort(foo.begin(), foo.end(), compare_pair_second<>());

hoặc là

std::sort(foo.begin(), foo.end(), compare_pair_second<std::less>());


-1

Hãy thử hoán đổi các yếu tố của các cặp để bạn có thể sử dụng std::sort()như bình thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.