我们如何使用 stl 容器有效地存储对象? (即根据字段值进行搜索)[关闭]

Posted

技术标签:

【中文标题】我们如何使用 stl 容器有效地存储对象? (即根据字段值进行搜索)[关闭]【英文标题】:How can we efficiently store objects using stl containers? (viz. for searching based on values of their fields) [closed] 【发布时间】:2019-06-26 05:04:55 【问题描述】:

我有类似这样的对象:

struct stStudents 

    int roll_number; // Can be think as unique key
    std::string name;
    // There could be more fields here

我需要使用 stl 容器以这样一种方式存储这些对象,以便我应该能够根据字段尽快搜索它们,例如上面示例中的 roll_numbername(或两者)。

我已经尝试/想到的:

    尽可能简单,如果我只是将它们存储在std::vector(可能使用std::find_if)中,搜索将是 O(n)。

    使用 std::setstd::map 需要 O(log N),但是为此,对象的重载比较运算符需要基于特定字段(或者可以使用 @ 设置字段987654329@)

    具有指向这些对象的各种std::unordered_set 指针(在结构内部)。并根据搜索条件在结构中定义比较运算符(就像我们在数据库中定义多个索引一样)。这将是 O(1) 搜索,但仅限于预定义的搜索条件。

问题:

这些方法如何?我们还能想到哪些更好的替代方案?

【问题讨论】:

您试图解决的真正问题是什么?为什么你需要这个“高效”?你对“高效”的定义是什么?您是否使用例如向量和std::find_if 不是“足够好”(几乎总是足够好)? @Someprogrammerdude .. 没有真正描述。它目前处于 PoC 级别。因此,在设计时,请确保从最有效的数据结构开始。是的,vector 和 std::find_if 可能是最佳候选者,所以在这种情况下,只需寻找验证即可。 您忘记定义一个同样重要的属性:roll_number 是唯一的吗?并且:name 是唯一的吗?根据该问题的答案,您最终可能会选择 std::multimapstd::multisetstd::copy_if @user23573 有效点。谢谢。将相应地更新问题 boost::multi_index 你可能会感兴趣。 【参考方案1】:

Mapset 在搜索方面是最好的。 因此,如果我们真的只想要搜索速度,那么为名称和滚动制作地图。 但我们正在放弃空间效率。

示例代码: 用 Visual Studio 2012 编写 search 的两个函数都可以达到 O(log(N))。

typedef struct _tagStudent

    int m_nRoll;
    std::string m_strName;
    _tagStudent(): m_nRoll(0)
    
    _tagStudent( int nRoll, std::string strName): m_nRoll( nRoll ), 
        m_strName(strName)
    
    bool operator<( _tagStudent x) const
    
        return x.m_nRoll > m_nRoll;
    
Student;

class Students

    std::map< int, Student > m_mapStudent;
    std::map< std::string, std::set<Student> > m_mapStudentIndexName;
public:
    Students();
    ~Students();
    //Adding students Index
    void Add( const Student & );
    //Searching Students by Roll
    Student Search( const int& ) const;
    //Searching Students by Name
    std::set<Student> Search( const std::string&) const;
;


Students::Students()

Students::~Students()

void Students::Add( const Student& oStudent )

    m_mapStudent[oStudent.m_nRoll] = oStudent;
    m_mapStudentIndexName[oStudent.m_strName].insert(oStudent);


Student Students::Search( const int& nRoll ) const

    if( m_mapStudent.find( nRoll ) != m_mapStudent.end() )
    
        return m_mapStudent.at( nRoll );
    
    return Student();

std::set<Student> Students::Search( const std::string& strName) const

    std::set<Student> oStudents;
    if( m_mapStudentIndexName.find( strName ) != m_mapStudentIndexName.end() )
    
        return m_mapStudentIndexName.at(strName);
    
    return std::set<Student>();


int _tmain(int argc, _TCHAR* argv[])
   
    Students oStudents;
    oStudents.Add( Student(1, "Tom") );
    oStudents.Add( Student(2, "Jerry") );
    oStudents.Add( Student(15, "Jerry") );
    oStudents.Add( Student(3, "Tim") );

    auto search1 = oStudents.Search( 1 );
    auto search3 = oStudents.Search( "Jerry" );

    return 0;

【讨论】:

"Mapset 在搜索方面是最好的。"定义“最佳”。可能是最方便的界面,但肯定不是最快的。 是的。如果您关心性能,则始终使用std::vector&lt;&gt;std::unordered_set&lt;&gt;std::unordered_map&lt;&gt;。如果你想要一个堆栈,你使用std::vector&lt;&gt;,如果你不关心顺序,你使用std::vector&lt;&gt;。从性能的角度来看,很少有链表有意义,而有序的std::set&lt;&gt;std::map&lt;&gt; 与它们的无序对应物相比太慢了。基本上是:如果 std::vector&lt;&gt; 能解决问题,请使用它。 您不能将 typedef 设置为 C++ 类型。 typedef 用于 C 编译器,重载运算符用于 C++! @Ajay 说你“不能”拥有它是错误的。它是有效的 C++,只是愚蠢且毫无意义。 @JonathanWakely,我没有说它不会编译,只是建议它一定没有(不应该更轻)。我已经看到很多模板代码隐藏在“typedef'ed”类下!因此,我讨厌它。

以上是关于我们如何使用 stl 容器有效地存储对象? (即根据字段值进行搜索)[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

用于 STL 容器的 std::string_view

STL容器

(C++ 继承)在 stl 容器中存储具有共同父对象的对象

deque的STL常用库函数

将一种类型的 STL 对象存储到另一种类型的有效方法

stl中顺序性容器,关联容器两者粗略解释