本文介绍双链表的一些接口函数,以及实现方法。
在一个双链表中,每个节点都包含两个指针 ——指向前一个节点的指针和指向后一个节点的指针。这可以使我们以任何方向遍 历双链表,甚至可以忽前忽后地在双链表中访问。下面的图展示了一个双链表
现在,存在两个根指针:一个指向链表的第1个节点,另一个指向最后一个节 点。这两个指针允许我们从链表的任何一端开始遍历链表。 我们可能想把两个根指针分开声明为两个变量。但这样一样,我们必须把两个 指针都传递给插入函数。为根指针声明一个完整的节点更为方便,只是它的值字段 绝不会被使用。在我们的例子中,这个技巧只是浪费了一个整型值的内存空间。对 于值字段非常大的链表,分开声明两个指针可能更好一些。另外,我们也可以在根 节点的值字段中保存其他一些关于链表的信息,例如链表当前包含的节点数量。
实现一个双链表需要定义一个节点的数据结构
typedef struct Node
{
struct Node* next;
struct Node* prev;
int data;
}ListNode ,*nodep;
再使用动态内存空间创建一个头结点:
nodep ListNode_Creat(void)
{
nodep root = (nodep)malloc(sizeof(ListNode));
if (root == NULL) return NULL;
root->data = 0;
root->next = root;
root->prev = root;
return root;
}
接下来可以写一个插入函数,将数据一个个插入到链表中,就可以实现一个简单的双向链表。
头部插入:
int ListNode_Add_Head(nodep root, int val)
{
if (root == NULL) return -1;
nodep first = root->next;
nodep new = (nodep)malloc(sizeof(ListNode));
if (new == NULL)return 0;
new->data = val;
new->next = first;
first->prev = new;
new->prev = root;
root->next = new;
return 1;
}
它的功能是在一个双向链表的头部插入一个新的节点。它的参数是 root(链表的头节点)和 val(新节点的数据)。它的返回值是 0(成功)或 -1(失败)。它的具体步骤如下:
- 首先判断 root 是否为空,如果为空,说明链表不存在,返回 -1。
- 然后获取 root 的下一个节点,即链表的第一个节点,赋值给 first。
- 接着分配内存空间给新节点 new,如果分配失败,返回 0。
- 然后给新节点 new 的数据域赋值为 val。
- 然后把新节点 new 的下一个节点指向 first,即把新节点插入到链表的开头。
- 然后把 first 的前一个节点指向 new,即把新节点和原来的第一个节点连接起来。
- 然后把新节点 new 的前一个节点指向 root,即把新节点和头节点连接起来。
- 最后把 root 的下一个节点指向 new,即更新头节点的指针。
- 返回 1,表示插入成功。
尾部插入:
int ListNode_Add_Tail(nodep root,int val)
{
if(root == NULL) return -1;
nodep tail = root->prev;
nodep new = (nodep)malloc(sizeof(ListNode));
if(new == NULL) return 0;
new->data = val;
new->prev = tail;
tail->next = new;
new->next = root;
root->prev = new;
return 1;
}
尾部移除:
int ListNode_Remove_Tail(nodep root)
{
if (root == NULL)return -1;
nodep tail = root->prev;
nodep newtail = tail->prev;
root->prev = newtail;
newtail->next = root;
free(tail);
return 1;
}
的功能是在一个双向链表的尾部删除一个节点。它的参数是 root(链表的头节点)。它的返回值是 1(成功)或 -1(失败)。它的具体步骤如下:
- 首先判断 root 是否为空,如果为空,说明链表不存在,返回 -1。
- 然后获取 root 的前一个节点,即链表的最后一个节点,赋值给 tail。
- 然后获取 tail 的前一个节点,即链表的倒数第二个节点,赋值给 newtail。
- 然后把 root 的前一个节点指向 newtail,即把倒数第二个节点作为新的尾节点。
- 然后把 newtail 的下一个节点指向 root,即把链表形成一个循环。
- 然后释放 tail 占用的内存空间,即删除原来的尾节点。
- 返回 1,表示删除成功。
头部移除:
int ListNode_Remove_Head(nodep root)
{
if (root == NULL) return -1;
nodep head = root->next;
nodep newhead = head->next;
root->next = newhead;
newhead->prev = root;
free(head);
return 1;
}
在指定节点前后添加:
int ListNode_Add_AheadNode(nodep node,int val)
{
if (node == NULL)return -1;
nodep new = (nodep)malloc(sizeof(ListNode));
if (new == NULL)return 0;
new->data = val;
nodep last = node->prev;
last->next = new;
new->prev = last;
new->next = node;
node->prev = new;
return 1;
}
int ListNode_Add_BehindNode(nodep node, int val)
{
if (node == NULL)return -1;
nodep next = node->next;
nodep new = (nodep)malloc(sizeof(ListNode));
if (new == NULL)return 0;
new->data = val;
new->prev = node;
node->next = new;
new->next = next;
next->prev = new;
return 1;
}
在一个双向链表中在指定的节点前后插入新的节点。它们的参数都是 node(要插入的位置的节点)和 val(新节点的数据)。它们的返回值都是 1(成功)或 -1(失败)。它们的具体步骤如下:
- ListNode_Add_AheadNode 函数是在 node 节点的前面插入新节点。它的步骤是:
- 首先判断 node 是否为空,如果为空,说明链表不存在,返回 -1。
- 然后分配内存空间给新节点 new,如果分配失败,返回 0。
- 然后给新节点 new 的数据域赋值为 val。
- 然后获取 node 的前一个节点,即链表的前一个节点,赋值给 last。
- 然后把 last 的下一个节点指向 new,即把新节点和前一个节点连接起来。
- 然后把 new 的前一个节点指向 last,即把新节点和前一个节点连接起来。
- 然后把 new 的下一个节点指向 node,即把新节点和当前节点连接起来。
- 然后把 node 的前一个节点指向 new,即把当前节点和新节点连接起来。
- 返回 1,表示插入成功。
- ListNode_Add_BehindNode 函数是在 node 节点的后面插入新节点。它的步骤是:
- 首先判断 node 是否为空,如果为空,说明链表不存在,返回 -1。
- 然后分配内存空间给新节点 new,如果分配失败,返回 0。
- 然后给新节点 new 的数据域赋值为 val。
- 然后获取 node 的下一个节点,即链表的后一个节点,赋值给 next。
- 然后把 new 的前一个节点指向 node,即把新节点和当前节点连接起来。
- 然后把 node 的下一个节点指向 new,即把当前节点和新节点连接起来。
- 然后把 new 的下一个节点指向 next,即把新节点和后一个节点连接起来。
- 然后把 next 的前一个节点指向 new,即把后一个节点和新节点连接起来。
- 返回 1,表示插入成功
查找节点:
nodep ListNode_FindVal(nodep root, int val)
{
if (root == NULL)return -1;
nodep cur = root->next;
while (cur != root && cur->data != val)
{
cur = cur->next;
}
return cur;
}
删除节点:
int ListNode_RemoveNode(nodep node)
{
if (node == NULL) return -1;
node->prev->next = node->next;
node->next->prev = node->prev;
free(node);
return 1;
}
清空链表:
int ListNode_FreeAll(nodep root)
{
if (root == NULL)return -1;
nodep cur = root->next;
while (cur != root)
{
cur = cur->next;
nodep temp = cur;
free(temp);
}
free(root);
return 1;
}
打印链表:
int ListNode_Show(nodep root)
{
if (root == NULL)return -1;
nodep cur = root->next;
while (cur != root)
{
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
return 1;
}