对容器进行多次排序,使用什么容器和什么方法

时间:2017-07-01 15:16:32

标签: c++ sorting vector containers

我有一些我需要打印的数据,为简单起见,我们说它是一个有一些参数的人的容器(矢量)。在我的程序的不同部分,我需要打印所有按不同参数排序的部分。我的问题是

1。)选择哪个容器? (我个人选择了矢量)。

2.。)什么方法更好,每次都对整个矢量进行排序,或者最好制作该矢量的副本并将其保存排序?在我的解决方案中,我每次都对相同的矢量进行排序,但由于速度的原因,可能不是一个使用大型矢量的正确方法。

class Person
{
private:
    std::string name;
    std::string surname;
    int age;
public:
    Person(std::string name, std::string surname, int age) : name{ name }, surname{ surname }, age{ age } {};
    void print() { std::cout << name << " " << surname << " " << age << std::endl; };
    static bool sortName(Person const &A, Person const &B) { return A.name < B.name; };
    static bool sortSurname(Person const &A, Person const &B) { return A.surname < B.surname; };
    static bool sortAge(Person const &A, Person const &B) { return A.age < B.age; };
};

主:

int main()
{
    std::vector<Person> persons;
    Person person1("John", "Smith", 30);
    Person person2("Mark", "Cooper", 28);
    Person person3("George", "Orwell", 19);

    persons.push_back(person1);
    persons.push_back(person2);
    persons.push_back(person3);

    std::sort(persons.begin(), persons.end(), Person::sortSurname);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    // do some other stuff here ... and then ...
    std::sort(persons.begin(), persons.end(), Person::sortName);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    // do some other stuff here ... and then ...
    std::sort(persons.begin(), persons.end(), Person::sortAge);
    for (int i = 0; i < persons.size(); ++i)
    {
        persons[i].print();
    }

    return 0;
}

7 个答案:

答案 0 :(得分:5)

boost::multi_index_container允许您定义具有任意数量的不同索引或视图的任何类型的容器。

容器在插入和移除时自动保持索引最新。

这是一个庞大的模板库,需要一点时间来习惯,但文档很好,有很多例子。

这是以这种方式表达的实现:

#include <iostream>
#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>

class Person {
private:
    std::string name;
    std::string surname;
    int age;
public:
    Person(std::string name, std::string surname, int age) : name{name}, surname{surname}, age{age} {};

    auto get_name() const -> const std::string& { return name; }
    auto get_surname() const -> const std::string& { return surname; }
    auto get_age() const -> int { return age; }

    void print() const { std::cout << name << " " << surname << " " << age << std::endl; };
};

namespace bmi = boost::multi_index;

struct by_name {};
struct by_surname {};
struct by_age;
using PersonTable = boost::multi_index_container<Person,
        bmi::indexed_by<
                bmi::ordered_non_unique<bmi::tag<by_name>, bmi::const_mem_fun<Person,std::string const&,&Person::get_name>>,
                bmi::ordered_non_unique<bmi::tag<by_surname>, bmi::const_mem_fun<Person,std::string const&,&Person::get_surname>>,
                bmi::ordered_non_unique<bmi::tag<by_age>, bmi::const_mem_fun<Person,int,&Person::get_age>>
        >
>;

int main()
{
    PersonTable people;
    people.insert(Person("John", "Smith", 30));
    people.insert(Person("Mark", "Cooper", 28));
    people.insert(Person("George", "Orwell", 19));

    std::cout << "by name" << std::endl;
    for (auto&& person : people.get<by_name>())
    {
        person.print();
    }
    std::cout << "\nby surname" << std::endl;
    for (auto&& person : people.get<by_surname>())
    {
        person.print();
    }
    std::cout << "\nby age" << std::endl;
    for (auto&& person : people.get<by_age>())
    {
        person.print();
    }
}

预期产出:

by name
George Orwell 19
John Smith 30
Mark Cooper 28

by surname
Mark Cooper 28
George Orwell 19
John Smith 30

by age
George Orwell 19
Mark Cooper 28
John Smith 30

文档:http://www.boost.org/doc/libs/1_64_0/libs/multi_index/doc/index.html

答案 1 :(得分:2)

我会使用std::setstd::shared_ptr<Person>个实例,每个Person个实例按int main() { std::shared_ptr<Person> person1 = std::make_shared<Person>("John", "Smith", 30); std::shared_ptr<Person> person2 = std::make_shared<Person>("Mark", "Cooper", 28); std::shared_ptr<Person> person3 = std::make_shared<Person>("George", "Orwell", 19); std::set<std::shared_ptr<Person>> persons1([](std::shared_ptr<Person> a, std::shared_ptr<Person> b) { return a->name < b->name; }); std::set<std::shared_ptr<Person>> persons2([](std::shared_ptr<Person> a, std::shared_ptr<Person> b) { return a->surname < b->surname; }); std::set<std::shared_ptr<Person>> persons3([](std::shared_ptr<Person> a, std::shared_ptr<Person> b) { return a->age < b->age; }); persons1.insert(person1); persons1.insert(person2); persons1.insert(person3); persons2.insert(person1); persons2.insert(person2); persons2.insert(person3); persons3.insert(person1); persons3.insert(person2); persons3.insert(person3); return 0; } 的相应字段排序:

std::shared_ptr
  • 使用std::set存储时不会浪费内存 几个容器中的对象。
  • {{1}}已经是已排序的容器,因此您无需排序 每次使用它时,只需枚举从头开始的元素 端。

答案 2 :(得分:2)

考虑通过指向Person的指针向量替换Person的向量。有了它,通过交换指针交换两个人是相当便宜的。然后使用类中定义的仿函数将指针放在所需的排序顺序中,然后开始打印。

答案 3 :(得分:1)

IMO,您现在使用的方法很好,即在运行时需要时进行排序。对于较大的数据集,您需要首先根据内存和处理能力评估您的要求。例如,对于非常大的数据集,您无法在内存中对其进行排序。并且,如果您决定采用多线程解决方案,那么会出现同步问题。因此,您需要一些像DBMS这样的专业解决方案,您可以在运行时根据需要查询已排序的数据。您将拥有索引等功能来优化查询时间。

答案 4 :(得分:1)

其中很多主要取决于3个因素 -
1.数据大小
2.您正在考虑什么样的表现 3.你可以换取#2

的空间量(内存)

一般来说,std::sort()在nlogn -

的平均值下执行
  

复杂性平均而言,第一个和第二个之间的距离是线性的   last:执行大约N * log2(N)(其中N是此距离)   元素的比较,以及多个元素交换(或移动)。

现在,如果您的用例涉及过于频繁调用的排序方法,那么预先排序和保存向量可能是有意义的 - 在这种情况下,您将获得相当大的性能提升。现在在这个设计中你必须考虑像可修改这样的集合?如果是,那么多久一次?那么你必须考虑avg案例插入性能命中。

所以总结取决于

答案 5 :(得分:1)

不是对对象的矢量进行排序(对于具有许多字段的复杂对象来说相当昂贵),您应该为存储在主向量中的对象构建几个索引向量,并按各种标准对它们进行排序。

#include <algorithm>
...

::std::vector< Person > persons;
//  add persons...

::std::vector< ::std::size_t > sorted_indexes;
sorted_indexes.reserve(persons.size());
{
    ::std::size_t index{};
    ::std::generate
    (
        sorted_indexes.begin()
    ,   sorted_indexes.end()
    ,   [&index]{return index++;}
    );
}
::std::sort
(
    sorted_indexes.begin()
,   sorted_indexes.end()
,   [&persons](::std::size_t const left, ::std::size_t const right)
    {
        return Person::sortSurname(persons[left], persons[right]);
    }
);
for(auto person_index: sorted_indexes)
{
    persons[person_index].print();
}

sortSurname也应该使用const引用来避免复制:

static bool sortSurname(Person const & left, Person const & right) { return left.surname < right.surname; };

答案 6 :(得分:1)

如果矢量很小或复制的元素很便宜,您可以在需要时重新排序,而不会出现任何问题。

如果向量的元素很大且复制起来很昂贵,你可以用你需要的方式对向量进行一次排序,然后创建第二个std::reference_wrapper向量并以不同的方式排序,以创建原始矢量的第二个“视图”,它不会修改原始矢量,也不会将元素复制到第二个矢量。

至于容器的选择;只需使用std::vector,除非您特别需要其他容器之一的特殊属性。

在任何情况下,基准不同的解决方案(具有优化的构建)并在设置之前测量不同解决方案的性能。