链表的目的之一是能够以很少的成本轻松添加和删除节点。
您可以放弃该功能并使用有效负载指针数组,但它不再是链表(当可以通过算术增量轻松获得同一节点时,拥有指向下一个节点的指针的目的是什么?)。
例如,而不是
struct
{
struct node *next;
void *payload;
...
} node;
node *root = NULL;
并为没有节点分配空间,你可以有
typedef struct
{
void *payload;
...
} node;
node *vector = NULL;
size_t vectorsize = 0;
并为最初需要的节点分配空间,然后realloc
在需要时使用扩展列表,并memmove
通过将节点移回已删除节点之外来删除节点。这在添加或删除节点时会导致明显的性能损失。另一方面,第 n 个节点正好是vector[n]
。
我再说一遍,这不再是一个链表:可能无论你需要它做什么,用指针数组代替链表可以更好地完成它。
这提醒了我,你最好解释一下为什么你需要直接寻址能力(“陈述问题,不要问如何实现解决方案”):也可能你需要的既不是数组也不是链表,但是,谁知道呢?可能是环形缓冲区、堆栈、山丘或二叉树。
在某些实现中,您甚至可以部署两个键合结构,例如,您可以在第一阶段使用(双重?)链表,其中包含大量插入和删除,尤其是最近插入的数据;然后为第二阶段构建并切换到指针数组,在该阶段您需要由节点号驱动的直接寻址(将数组用作列表节点地址的“缓存”):
for (listsize = 0, scan = root; scan; scan = scan->next)
listsize++;
if (NULL == (vector = (node *)malloc(listsize * sizeof(node))))
{
// out of memory
return EXIT_FAILURE;
}
for (listsize = 0, scan = root; scan; scan = scan->next)
vector[listsize++] = scan;
// Now vector[i]->payload is the payload of the i-th node