c++STL通过int和string的向量进行排序

c++ STL sort by a vector of int and string

本文关键字:向量 排序 string 通过 int c++STL      更新时间:2023-10-16

今天我在解决一个关于面漆的问题,在这个问题中,我必须按照奥运会奖牌对国家进行排序。我有一个STL容器vector<pair<vector<int>, string> > v;vector<int>不包含任何一个国家获得的金牌、银牌和铜牌。我必须按照金、银、青铜和国家(字母顺序)的特殊顺序对结构进行排序。

我使用了排序(v.begin(),v.end()),但这种排序只根据配对中的第一个值(即金牌、银牌和铜牌),当两个国家的g、s、b奖牌相同时,它不按字母顺序排列国家。

您必须提供比较函数对象。

typedef pair<vector<int>, string> Country;
struct CmpCountry {
    bool operator()(const Country& lhs, const Country& rhs)
    {
        if(lhs.first[0] != rhs.first[0])
            return lhs.first[0] > rhs.first[0];
        if(lhs.first[1] != rhs.first[1])
            return lhs.first[1] > rhs.first[1];
        if(lhs.first[2] != rhs.first[2])
            return lhs.first[2] > rhs.first[2];
        return lhs.second < rhs.second;
    }
};
// then, call sort as following
std::sort(v.begin(), v.end(), CmpCountry());

如果你真的写了你描述的代码,它会做你想要的事情:

compare.cpp:

#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
using namespace std;
typedef pair<vector<int>, string> Country;
int main(int, char*[]) {
  vector<Country> v;
  while (cin.good()) {
    string name;
    int g, s, b;
    cin >> name >> g >> s >> b;
    vector<int> c;
    c.push_back(g);
    c.push_back(s);
    c.push_back(b);
    v.push_back(make_pair(c, name));
  }
  sort(v.begin(), v.end());
  for (vector<Country>::const_iterator it = v.begin(); it != v.end(); ++it) {
    cout << it->second << " " << it->first[0]
     << " " << it->first[1] << " " << it->first[2] << "n";
  }
  return 0;
}

compare.in:

US 3 2 1
CA 4 1 3
DE 1 3 5
FR 1 3 5
BE 1 3 5
RU 3 1 2

现在这样做:

$ clang++ -o compare compare.cpp
$ ./compare < compare.in
BE 1 3 5
DE 1 3 5
FR 1 3 5
RU 3 1 2
US 3 2 1
CA 4 1 3

请注意,它是按金牌第一(先是BE/DE/FR,然后是RU/US,然后是CA)、银牌第二(RU,然后是US)、铜牌第二(尽管我没有输入),然后是名字(BE,然后是DE,然后是FR)排序的(升序)。正是你所要求的。

(实际上,你要求按字母顺序排列,这将对g、s和b进行数字顺序排列。这可能是你想要的(例如,2枚金牌大于11枚)。如果没有,则必须编写自己的比较函子,在比较int之前对其进行字符串化。)

那么,为什么这样做呢?如果你看一下std::pair的定义,它按字典顺序进行比较——也就是说,它比较lhs.firstrhs.first,然后只有在first相等的情况下,才转到lhs.secondrhs.second。如果你看一下std::vector的定义,它也按照字典顺序进行比较——也就是说,它比较lhs[0]rhs[0],然后只有当[0]相等时,才转到lhs[1]rhs[1],以此类推。这正是你在这里追求的比较顺序。

从你的评论中,听起来你想颠倒数值的正常排序顺序,但不想颠倒国家名称。要做到这一点,您必须定义自己的比较器。但请注意,问题不在于对和向量没有按照你想要的方式排序——它们确实如此——而是int没有按照你需要的方式排序。

如果你理解的话,这一切都是非常琐碎的,我会一步一步地解释,而不仅仅是给出答案。

首先,以下是默认排序(实际上,而不是字面上)要做的事情:

struct CountryComparator {
  bool operator()(const Country& lhs, const Country& rhs) const {
    if (!(lhs.first == rhs.first))
      return (lhs.first < rhs.first);
    return (lhs.second < rhs.second);
  }
};

(请注意,我将尽量只使用==<。这在您的情况下并不重要,因为您只是在比较int,但STL的设计理念是,即使在只支持这两个运算符的类上,每个算法也应该工作,这是一个好习惯。)

扩展向量比较会使事情变得非常冗长,所以我们不用麻烦了。如果你真的想反转向量中某些成员的排序顺序,而不是其他成员,你必须这样做,但你试图反转整个向量的排序顺序。这与反转向量本身的排序顺序相同。所以,只要定义一下:

struct CountryComparator {
  bool operator()(const Country& lhs, const Country& rhs) const {
    if (!(lhs.first == rhs.first))
      return (rhs.first < lhs.first);
    return (lhs.second < rhs.second);
  }
};

现在,只需将排序行更改为:

  sort(v.begin(), v.end(), CountryComparator());

现在让我们试试:

$ ./compare < compare.in
CA 4 1 3
US 3 2 1
RU 3 1 2
BE 1 3 5
DE 1 3 5
FR 1 3 5

CA获得4枚金牌,领先于其他所有人。然后,美国队和俄罗斯队各获得3枚金牌,按银牌排序;美国队获得两枚银牌,排名第一。然后,BE、DE和FR,每个都有1枚金牌,同样数量的银牌和同样数量的青铜器,按字母顺序排列。

您需要一个自定义比较器,以便STL排序可以比较您的向量。最简单的方法是将向量定义为struct,然后添加一个比较器。

struct country {
    vector<int> medals;
    string country_name;
}
struct compare { 
    bool operator()(country const &a, country const &b) { 
        for(int i = 0; i <  3; i++){ // 3 medals, right?
            if(a.medals[i] != b.medals[i]) 
                return a.medals[i] < b.medals[i];
        //else both countries have same number of medals
        return a.country_name < b.country_name;
    }
};