【问题标题】:What kind of data structure is suitable for facebook-model users什么样的数据结构适合facebook-model用户
【发布时间】:2019-05-01 01:48:18
【问题描述】:

我想为这种情况构建一个有效的数据结构:
有很多用户,每个用户都有一个 ID 和一个名称。每个用户都可以关注其他用户。我需要处理四种命令:create-user、follow、delete-user 和 cancel-follow。这是一个例子:

create-user id=3 name="Chandler"
create-user id=7 name="Janice"
create-user id=2 name="Joey"
follow id=3 id=7     # Chandler follows Janice
follow id=7 id=3     # Janice follows Chandler
follow id=2 id=7
follow id=7 id=2
delete-user id=7
follow id=3 id=2
follow id=2 id=3
cancel-follow id=3 id=2

总而言之,我需要读取一个文件,其中包含大量上述命令并处理所有数据。

这是我尝试过的(处理四种命令的函数):

struct User
{
    unsigned long id;
    string name;
    unordered_set<User *> followers;
    unordered_set<User *> fans;

    User(unsigned long pid, const string & pname) : id(pid), name(pname) {}
};

list<User> users;

User & getUserById(list<User> & users, unsigned long id)
{
    auto it = std::find_if(users.begin(), users.end(), [&](User & u) {return id == u.id;});
    return *it;
}

void createUser(list<User> & users, unsigned long id, const string & str)
{
    users.emplace_back(User(id, str));
}

void deleteUser(list<User> & users, unsigned long id)
{
    auto itUser = std::find_if(users.begin(), users.end(), [&](User & u) {return id == u.id;});
    auto itFans = itUser->fans;
    for (auto it = itFans.begin(); it != itFans.end(); ++it)
    {
        (*it)->followers.erase(&*itUser);
    }
    auto itFollowers = itUser->followers;
    for (auto it = itFollowers.begin(); it != itFollowers.end(); ++it)
    {
        (*it)->fans.erase(&*itUser);
    }
    users.erase(itUser);
}

void buildRelation(list<User> & users, unsigned long follower, unsigned long fan)
{
    User & u1 = getUserById(users, follower);  //3
    User & u2 = getUserById(users, fan);  //7
    u1.fans.insert(&u2);
    u2.followers.insert(&u1);
}

void cancelRelation(list<User> & users, unsigned long follower, unsigned long fan)
{
    User & u1 = getUserById(users, follower);       //3
    User & u2 = getUserById(users, fan);  //2
    u1.fans.erase(&u2);
    u2.followers.erase(&u1);
}

它没有任何错误。

但是,我用我的代码处理一个文件,包含 70000 行命令,大约需要 67 秒。

我真的很想获得更好的性能(也许是 20 秒?),我知道我需要一个更好的数据结构,但现在我不知道如何设计一个更好的。

【问题讨论】:

  • 您正在使用优化的构建进行测试,对吧?您是否进行了分析以查看花费时间的内容?
  • 关于std::list 的注释:它非常适合在任何地方插入和删除,它有非常有利的iterator invalidation,您可以充分利用它,但对于几乎所有其他您可以做的事情它,它彻头彻尾的烂。 getUserById 有可能成为指针追逐和缓存未命中的绝对恐怖表演。

标签: c++ algorithm performance data-structures performance-testing


【解决方案1】:

您的输入数据看起来非常适合键值数据结构,例如 std::unordered_map (https://en.cppreference.com/w/cpp/container/unordered_map)

id 值可以作为存储和检索用户数据结构的键。

如果您可以将数据处理成类似的结构 std::unordered_map 而不是列表 这样您的 getUserById 函数就不需要每次都搜索列表来检索用户数据。

【讨论】:

  • 作为一个额外的好处,unordered_map 具有非常宽容的引用失效,即使在插入项目时迭代器很容易失效..
  • 太棒了。我刚刚测试了std::unordered_map,现在只需要 13 秒。非常好。谢谢。
【解决方案2】:

对于用户来说,我有一个映射类型的数据结构键和值作为名称。

实现一个有向图作为结构

Graph{ intV;LinkedList<Map<Long,String>>adjList[]; }
  1. 创建用户:
    在图中创建一个节点

  2. 关注用户: 创建从该用户到其他用户的定向链接

  3. 删除用户: 使用图遍历技术找到用户并删除节点

  4. 取消 - 关注: 从图中删除链接,因为它是有向图

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多