如何使用一个指针来实现双向链表操作

1、基础

数学基础: x ^ y ^ y = x ^ ( y ^ y ) = x ^ 0 = x
双指针双向链表结点:
struct double_link_list {
    struct double_link_list *next;
    struct double_link_list *prev;
    ... value;
} head;

单指针使用的双向链表:  

struct double_link_list {
    void *np;
    ... value;
} head;

两链表之间的关系:

head->np = head->next ^ head->prev

2、 搜索

我们通常所用的双指针双向链表head的prev为null,因此有

head->np = head->prev ^ head->next = head->next;

这样,我们根据链表的头部就可以获得head->next,即链表的第二个结点地址,这样:
node->next = node->np ^ node->prev; // node->np = node->next ^ node->prev

此处可将node指为head->next,即为head->np,则有:

node->prev = head, node->np = head->np->np,从而获知第三个结点地址。

以此类推,只要在整个搜索过程中跟踪两个连续结点地址,就可以获得第三个结点地址,具体实现:
node = head->np;
p_node = head; // 假设前两个结点都不是所搜结点

while ( p_next = node->np ^ p_node ) {
    if ( p_next->value == x )
        return p_next;
    else {
        p_node = node;
        node = p_next;
    }
}
return NULL;


3、 插入

在head之后插入node的代码如下(主要用于建表)
nodep = &node;
nodep->np = head->np ^ head;
head->np->np = head->np->np ^ head ^ nodep;
// head->next->np = head->next->np ^ head ^ nodep
// = head->next->next ^ nodep
head->np = nodep;
如果想在任意一个结点之后插入结点,必须先得定位此结点与它之前(或之后)的结点地址(通常使用搜索与备忘)。

4、删除

类似于插入,必须先得定位此结点以及它之前或之后的结点,然后:
// nodep = node->prev
node_n = node->np ^ nodep;
nodep->np = (nodep->np ^ node) ^ node_n;
node_n->np = node_n->np ^ node ^ node_p;
free(node);
注意: 如果为循环链表的话,如果只知道一个结点信息的话,是不能遍历整个链表的,同时,如果此循环链表只有一个结点时会出现node->prev = node->next的情况,此时node-> = NULL,丢失信息。 而如果不是循环链表的话,由于内存地址的唯一性,这种问题是不会发生的

你可能感兴趣的:(struct,list,null)