我目前正在尝试找到适合我案例的最佳数据结构/算法:
我收到单个唯一的随机ID(uint32_t),并创建一个与每个新ID相关联的元素。
我需要从id中检索元素。
我还需要按照创建顺序从任何元素(甚至id)访问下一个和前一个元素。创建的顺序主要取决于当前的元素,它总是可以放在一边,所以新元素应该是它的下一个元素。
以下是一个例子:
(12) <-> (5) <-> (8) <-> (1)
^ ^
'------------------------'
如果我假设当前元素为(8)并且创建了一个新元素(3),它应该如下所示:
(12) <-> (5) <-> (8) <-> (3) <-> (1)
^ ^
'--------------------------------'
要考虑的重要事项是插入,删除和搜索几乎以相同(高)频率发生。不完全确定有多少元素会同时存在,但我会说max~1000。
知道了所有这些,我考虑使用带有id的AVL作为排序键,同时保留前一个和下一个元素。
在C语言中,类似这样:
struct element {
uint32_t id;
/* some other fields */
struct element *prev;
struct element *next;
}
struct node {
struct element *elt;
struct node *left;
struct node *right;
};
static struct element* current;
另一个想法可能是使用哈希映射,但后来我需要找到正确的哈希函数。不完全确定它在实践中总是胜过AVL这个数量的元素。这取决于哈希函数。
AVL是个好主意还是我应该考虑其他事情呢?
谢谢!
PS:我不是一个试图让你做作业的学生,我只是想开发一个简单的窗口管理器(只是为了好玩)。答案 0 :(得分:2)
您正在寻找java LinkedHashMap
中所谓的变体这基本上是hash-table和(双向)linked list 的组合。
链接列表具有所需顺序的元素。在O(1)
中完成在已知位置插入元素(假设您有指向正确位置的指针)。同样适用于删除。链表包含所需顺序的所有元素。
第二个数据结构是哈希映射(或树映射)。此数据结构从密钥(您的唯一ID)映射到链接列表中的POINTER。这样,给定一个id - 你可以快速找到它在链表上的位置,从那里你可以轻松访问下一个和前一个元素。
用于插入的高级伪代码:
insert(x, v, y): //insert key=x value=v, after element with key=y
if x is in hash-table:
abort
p = find(hash-table,y) //p is a pointer
insert_to_list_after(x,v,p) //insert key=x,value=v right after p
add(hash-table,x,p) //add x to the hash-table, and make it point to p.
用于搜索的高级伪代码:
search(x):
if x is not in hash-table:
abort
p = find(hash-table,x)
return p->value;
删除应该与插入非常相似(并且同时复杂)。
请注意,找到x
之后的元素
p = find(hash-table,x)
if (p != NULL && p->next != NULL):
return p->next->value
答案 1 :(得分:0)
我的建议是你使用两种数据结构的组合 - 按照插入顺序存储元素的列表和用于在id和list节点之间实现关联数组(map)的哈希映射或二进制搜索树。您将使用关联数组执行搜索,并且可以使用列表访问相邻元素。删除也相对容易,但您需要从两个结构中删除。
如果使用二叉搜索树,则查找/插入/删除的复杂性将为log(n)
,如果使用哈希表,则预期复杂性是常量。
答案 2 :(得分:0)
您一定要考虑Skip List数据结构。
它似乎非常适合您的情况,因为它具有预期的O(log(n))
插入/搜索/删除,如果您有指向节点的指针,则可以在O(1)
中找到上一个和下一个元素只需移动指针即可。
结论是,如果你刚刚创建了一个节点,你有一个指向它的指针,你可以在O(1)
时间内找到prev / next元素。