ฉันจะเรียงเวกเตอร์ของคู่ตามองค์ประกอบที่สองของคู่ได้อย่างไร


133

ถ้าฉันมีเวกเตอร์คู่:

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

มีวิธีง่ายๆในการจัดเรียงรายการตามลำดับที่เพิ่มขึ้นตามองค์ประกอบที่สองของคู่หรือไม่?

ฉันรู้ว่าฉันสามารถเขียนอ็อบเจกต์ฟังก์ชั่นเล็ก ๆ น้อย ๆ ที่จะทำงานได้ แต่มีวิธีใช้ชิ้นส่วนที่มีอยู่ของSTLและstd::lessทำงานโดยตรงหรือไม่

แก้ไข: ฉันเข้าใจว่าฉันสามารถเขียนฟังก์ชันหรือคลาสแยกกันเพื่อส่งต่อไปยังอาร์กิวเมนต์ที่สามเพื่อจัดเรียง คำถามคือฉันสามารถสร้างมันขึ้นมาจากสิ่งของมาตรฐานได้หรือไม่ ฉันมีบางอย่างที่ดูเหมือน:

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


1
c ++ ไม่มี lamdas ดังนั้นคุณจึงไม่สามารถทำสิ่งที่ต้องการได้อย่างแน่นอนคุณจะต้องสร้างฟังก์ชัน / functor แยกต่างหาก นี่อาจเป็นซับเดียวได้ดังนั้นจึงไม่ควรเป็นเรื่องใหญ่
Evan Teran

1
C ++ มี lambdas แล้ว! แอ่ว!
David Poole

คำตอบ:


212

แก้ไข : โดยใช้ c ++ 14 ทางออกที่ดีที่สุดนั้นง่ายมากในการเขียนด้วย lambdas ที่สามารถมีพารามิเตอร์ประเภทautoได้แล้ว นี่คือวิธีแก้ปัญหาที่ชื่นชอบในปัจจุบัน

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

เพียงใช้ตัวเปรียบเทียบที่กำหนดเอง (เป็นอาร์กิวเมนต์ที่ 3 ซึ่งเป็นทางเลือก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());

หากคุณใช้คอมไพเลอร์ C ++ 11 คุณสามารถเขียนแบบเดียวกันโดยใช้ lambdas:

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

แก้ไข : ในการตอบสนองต่อการแก้ไขของคุณกับคำถามของคุณที่นี่เป็นความคิดบางอย่าง ... ถ้าคุณจริงๆอยากจะมีความคิดสร้างสรรค์และสามารถนำมาใช้แนวคิดนี้มากเพียงให้แม่แบบ:

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);
    }
};

จากนั้นคุณสามารถทำได้เช่นกัน:

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

หรือแม้กระทั่ง

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

แม้ว่าตามจริงแล้วทั้งหมดนี้เป็นเรื่องที่มากเกินไปเพียงแค่เขียนฟังก์ชัน 3 บรรทัดและดำเนินการกับมัน :-P


เก็บไว้ในใจว่านี่คือความแตกต่างจากในoperator< pair<T1,T2>ตัวเปรียบเทียบเริ่มต้นใช้ทั้งองค์ประกอบแรกและองค์ประกอบที่สอง (ในกรณีที่องค์ประกอบแรกเท่ากัน) ที่นี่จะใช้เพียงอันที่สองเท่านั้น
Googol

@Googol: นั่นคือสิ่งที่ OP ขอ ... เขาพูดว่า: "is there and easy way to sort the list in increasing order based on the second element of the pair?"
Evan Teran

@ evan-teran ใช่ฉันรู้ ฉันเพียงระบุว่าหากองค์ประกอบวินาทีทั้งสองเท่ากันผลลัพธ์อาจทำให้สับสนได้ (เช่นหากใช้สำหรับการเรียงลำดับ) ปัญหานี้ไม่ได้รับผลกระทบจากตัวเปรียบเทียบเริ่มต้นเนื่องจากใช้องค์ประกอบที่สองสำหรับการทำลายการผูก ฉันมาถึงคำถามนี้โดยมองหาตัวเปรียบเทียบที่ใช้องค์ประกอบที่สองเป็นข้อมูลหลักในการเปรียบเทียบ แต่ฉันก็ต้องการให้มันใช้อันแรกในการผูกไทดังนั้นฉันจึงต้องการหลีกเลี่ยงไม่ให้คนอื่นพลาดจุดนั้น (ในขณะที่ฉัน จริงไม่)
Googol

71

คุณสามารถใช้การเพิ่มเช่นนี้:

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

ฉันไม่รู้วิธีมาตรฐานในการทำสิ่งนี้ให้สั้นและกระชับเท่ากัน แต่คุณสามารถจับboost::bindได้ว่าทั้งหมดประกอบด้วยส่วนหัว


1
+1 สำหรับการใช้ Boost Btw ด้วยคอมไพเลอร์ที่ทันสมัยคุณอาจแทนที่ boost ด้วย std :: tr1 ได้แล้วเพราะจะเป็นมาตรฐานในไม่ช้า
Andreas Magnusson

น่าเสียดายที่ฉันลองแบบเดียวกันกับ c ++ 1x std :: bind ของ gcc trunk และล้มเหลวเพราะไม่มี op <สำหรับผูก ไม่รู้ว่า c ++ 1x พูดถึงเรื่องนี้หรือไม่ อาจบอกให้คุณใช้ lambda เพื่อสิ่งนั้น :)
Johannes Schaub - litb

1
ฉันคิดว่าบูสต์ไม่ได้มาตรฐาน แต่ก็ใกล้พอ :-)
David Norman

โพสต์คำถามติดตามสำหรับคำตอบนี้ที่นี่: stackoverflow.com/q/4184917/220636
nabulke

34

มันค่อนข้างง่ายที่คุณจะใช้ฟังก์ชันการเรียงลำดับจากอัลกอริทึมและเพิ่มฟังก์ชันการเปรียบเทียบของคุณเอง

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

ตอนนี้คุณต้องทำการเปรียบเทียบตามการเลือกครั้งที่สองดังนั้นให้ประกาศว่าคุณเป็น "myComparison"

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

5
ง่ายและ "ตรงประเด็น" ไม่จำเป็นต้องเพิ่มหรือรุ่น C ++ เฉพาะ +1
Thomio

1
สิ่งนี้ควรถูกระบุว่าเป็นทางออกที่ดีที่สุด ไม่จำเป็นต้องใช้ c ++ 14 ในการใช้งาน
Kartik Chauhan

คุณช่วยอธิบายให้ฉันฟังได้ไหมว่าการเปรียบเทียบนี้ทำงานอย่างไร เราส่งผ่านองค์ประกอบสองรายการไปยัง myComparision พร้อมกันแล้วจะเรียงลำดับอย่างไรได้บ้าง? นอกจากนี้ a.second <b.second มีบทบาทอย่างไร
era s'q

30

ด้วย C ++ 0x เราสามารถใช้ฟังก์ชันแลมด้า:

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; } );

ในตัวอย่างนี้ประเภทการส่งคืนboolจะอนุมานโดยปริยาย

ประเภทการส่งคืน Lambda

เมื่อแลมบ์ดาฟังก์ชั่นมีคำสั่งเดียวและนี่คือคำสั่งส่งคืนคอมไพเลอร์สามารถอนุมานประเภทการส่งคืนได้ จาก C ++ 11, §5.1.2 / 4:

...

  • ถ้าคำสั่งผสมอยู่ในรูปแบบ{ return expression ; }ประเภทของนิพจน์ที่ส่งกลับหลังจากการแปลง lvalue-to-rvalue (4.1) การแปลงอาร์เรย์เป็นตัวชี้ (4.2) และการแปลงฟังก์ชันเป็นตัวชี้ (4.3)
  • มิฉะนั้นvoid.

หากต้องการระบุประเภทการส่งคืนอย่างชัดเจนให้ใช้แบบฟอร์ม[]() -> Type { }เช่นใน:

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
ทำไมif (lhs.second == 0)?
สุกร

ไม่มีความหมายเฉพาะ lhs.second < rhs.secondอาจจะกลับมาtrueหรือและเรียบเรียงอย่างชัดเจนสามารถอนุมานfalse boolแค่อยากจะแสดงให้เห็นถึง[]() -> Type { }กรณี
Andreas Spindler

อย่างน้อยด้วยเสียงดังการหักโดยนัยนี้อาจทำงานไม่ถูกต้องฉันต้องเพิ่ม -> bool เป็นประเภทการส่งคืนแลมบ์ดาเพื่อให้มันทำงานได้อย่างถูกต้อง
MoDJ

5

สำหรับสิ่งที่ใช้ซ้ำได้:

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);
    }
};

คุณสามารถใช้เป็นไฟล์

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

หรือ

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


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.