【问题标题】:Please give an advice over which data structure should I use请就我应该使用哪种数据结构提供建议
【发布时间】:2012-04-06 10:40:36
【问题描述】:

代码在 C 中。我有两种类型的对象 (structs) 具有父子关系,一种父类型可以有 0 或更多子类型,一个子不能有自己的孩子们。我需要O(1) 父查找(由uID 结构成员)和子查找(也由uID 结构成员)而不知道谁是它的父。一旦我有一个指向父母的指针,我希望能够遍历它的孩子。当我有一个指向孩子的指针时,我希望能够知道谁是它的父母。在程序执行期间,可以删除或插入任何子项或任何父项,并且子项可以更改其父项。当父母被移除时,它的孩子也应该被移除。所有这一切都应该在多线程环境中完成,所以我需要线程安全的读取(我将使用只读锁进行密钥搜索,使用读写锁进行插入/删除/重新设置)。你会推荐什么数据结构?

添加:

目前我正在尝试使用 utash 库 (http://uthash.sourceforge.net/) 来实现它:

struct parent
{
    uint64_t uid;
    time_t mtime;
    struct ldata data;
    struct child *first_child;
    UT_hash_handle hh;
};

struct child
{
    uint64_t uid;
    time_t mtime;
    struct ldata data;
    struct parent *parent;
    UT_hash_handle hh;
};

struct parent *parents_list = NULL;
struct child *children_list = NULL;

问题是当一个新孩子到来时,它最终会排在后面, 与其“兄弟”没有联系。

【问题讨论】:

  • “查找”是什么意思?
  • 天真的方法:让每个孩子都有一个 prev、next 和 parent 指针,并给每个父母一个指向第一个孩子的指针——即保持孩子的双向链表。所有的指针都应该是原子的,以允许对关系结构进行无锁操作。
  • 通过查找我的意思是如果一个对象(父或子)有 uID XXXX 并且我有那个 uID,我想获取内存中结构对象的地址。
  • 我明白了。我想 O(1) 查找可以用哈希表完成。如果 ID 是连续的,一个简单的数组甚至可以解决问题。 (即具有平凡、完美哈希函数的哈希表。)
  • ID 不是连续的,它们是 64 位的唯一 ID。是的,我也倾向于使用哈希表。

标签: c data-structures struct


【解决方案1】:

怎么样:

  1. 父母的哈希表。
  2. 单独的儿童哈希表。
  3. 每个子项中的一个链接到其父项。
  4. 每个孩子中的一个链接到它的下一个和上一个兄弟姐妹(双链表)。
  5. 每个父级到其第一个子级的链接。

哈希表可能不是完全 O(1) 查找,但它们会很接近。您可能可以为它们使用现有的、完善的库。

在线程安全方面,您可以为哈希(用于项目插入/删除)设置互斥锁,在每个父级中也可以设置一个互斥锁,用于当它或其任何子级被操作时。当然要当心死锁:例如如果更改孩子的父母需要同时锁定新父母和新父母,请确保按照一致的顺序进行操作!

当然,找到无锁结构会更好,但我不能在那里给你真正的建议,除了研究看看你是否能找到任何似乎合适的结构。

【讨论】:

  • 谢谢,是的,多线程编程很容易出错
  • B+Trees 是否是无锁的,否则对于这个问题来说它们是多余的?
  • B+ 树(以及一般的树)可以有两个以上的级别,因此一个节点可以同时是父节点和子节点。它似乎不符合您的规格。
【解决方案2】:

如果我理解正确:

struct child;  /* Forward declaration */

struct parent {
    int child_count;
    /* Other info */
    struct child child[];  /* Flex array, must be the last field */
};

struct child {
    struct parent *parent;
    /* Other info */
};

struct parent *parent_registry;  /* Array of parents, index is the ID */
struct child *child_registry;  /* Array of children, index is the ID */

也许这太简单了,特别是在重新设置父级时,因为您必须移动数组切片,但这可能是一个好的开始。或者您可以预先分配(即摊销分配)并链接在一起(通过数组索引)所有空闲数组位置,以尽量减少内存移动。

【讨论】:

  • 谢谢你的朋友,我不知道那些 flex 数组,去了 rtfm :)
猜你喜欢
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多