在先前的文章里,我们已经简单介绍了链表。我们也创建了一个带有3个结点的简单链表,然后讨论了一下如何遍历它们。
这篇文章涉及到的所有程序均使用下面的结点表示链表。
1 | // 链表结点 |
这篇文章里, 会讨论如何在链表里插入结点。一般来说,有3个方法来添加结点。
- 在链表的首部。
- 在一个给定的结点之后。
- 在链表的尾部。
在首部添加一个结点: (4步)
新结点要添加在给定链表的首部。新添加的结点成为新的头结点。例如, 如果给定的链表是10->15->20->25 我们要再首部添加一个5, 然后链表就会变成5->10->15->20->25. 我们使用一个函数push()来表示。 这个函数必须接收一个指向头指针的指针, 因为需要修改它指向新加的结点。
下面是在首部添加结点的4个步骤。
/* 引用链表的头结点,指定一个整数, 然后在链表首部添加一个新的结点。*/
void push(struct Node** head_ref, int new_data)
{
/* 1. 分配结点 */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* 2. 指定数据 */
new_node->data = new_data;
/* 3. 指定新结点的后继为 head_ref */
new_node->next = (*head_ref);
/* 4. 头部指向新结点 */
(*head_ref) = new_node;
}
这个函数的时间复杂度为 O(1),因为它做的是连续数量的工作。
在指定的结点后面添加结点: (5 个步骤)
指定一个结点的指针, 然后新结点添加在它的后面。
/* 指定一个 prev_node, 然后在它后面添加一个新的结点 */
void insertAfter(struct Node* prev_node, int new_data)
{
/*1. 判断所给的结点是否为 NULL */
if (prev_node == NULL)
{
printf("the given previous node cannot be NULL");
return;
}
/* 2. 分配新的结点 */
struct Node* new_node =(struct Node*) malloc(sizeof(struct Node));
/* 3. 指定数据 */
new_node->data = new_data;
/* 4. 新结点的后继指定为 prev_node 的后继*/
new_node->next = prev_node->next;
/* 5. 指定 prev_node 后继为新结点 */
prev_node->next = new_node;
}
这个函数的时间复杂度为 O(1),因为它做的是连续数量的工作。
在链表尾部添加结点: (6 步)
新结点要求添加在链表的最后。例如,如果给定的链表是5->10->15->20->25然后在最后添加一个30, 然后链表就变成了5->10->15->20->25->30.
因为链表是用头结点表示的, 所以我们必须整个遍历它, 然后修改最后一个结点的后继为新的结点。
/* 引用链表的头结点, 指定一个整数, 在尾部添加一个新结点 */
void append(struct Node** head_ref, int new_data)
{
/* 1. 分配结点 */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. 指定数据 */
new_node->data = new_data;
/* 3. 新结点是最后一个结点,所以它的后继为 NULL*/
new_node->next = NULL;
/* 4. 如果链表为空, 那么新结点就是头结点 */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. 遍历到最后一个结点 */
while (last->next != NULL)
last = last->next;
/* 6. 修改最后一个结点的后继为新结点 */
last->next = new_node;
return;
}
这个函数的时间复杂度为 O(n) n 是链表的结点数。因为这里有一个循环。
这个函数可以优化到时间复杂度为 O(1),方法是使用一个尾部指针。
下面是一个完整的程序,使用上面所有的函数创建一个链表
#include <stdio.h>
#include <stdlib.h>
// 链表结点
struct Node
{
int data;
struct Node *next;
};
/* 指向头结点的指针和一个整数, 在链表前面插入一个新的结点. */
void push(struct Node** head_ref, int new_data)
{
/* 1. 分配结点 */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* 2. 指定数据 */
new_node->data = new_data;
/* 3. 新结点的后继作为头部 */
new_node->next = (*head_ref);
/* 4. 头部指向新结点 */
(*head_ref) = new_node;
}
/* 指定一个 prev_node, 在给定的结点后面插入一个新结点 */
void insertAfter(struct Node* prev_node, int new_data)
{
/*1. 判断所给的结点是否为 NULL */
if (prev_node == NULL)
{
printf("the given previous node cannot be NULL");
return;
}
/* 2. 分配结点 */
struct Node* new_node =(struct Node*) malloc(sizeof(struct Node));
/* 3. 指定数据 */
new_node->data = new_data;
/* 4. 指定新结点的后继为 prev_node 的后继 */
new_node->next = prev_node->next;
/* 5. 指定 prev_node 后继为新结点 */
prev_node->next = new_node;
}
/* 指向头结点的指针和一个整数, 在链表的尾部添加一个新结点 */
void append(struct Node** head_ref, int new_data)
{
/* 1. 分配结点 */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. 指定数据 */
new_node->data = new_data;
/* 3. 新结点会成为最后一个结点, 所以它的后继为 NULL*/
new_node->next = NULL;
/* 4. 如果链表为空, 新结点就是头结点 */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. 遍历到最后一个结点 */
while (last->next != NULL)
last = last->next;
/* 6. 修改最后一个结点的后继为新结点 */
last->next = new_node;
return;
}
// 这个函数从头打印链表的内容
void printList(struct Node *node)
{
while (node != NULL)
{
printf(" %d ", node->data);
node = node->next;
}
}
/* 测试函数*/
int main()
{
/* 空链表开始 */
struct Node* head = NULL;
// 插入 6. 链表变成 6->NULL
append(&head, 6);
// 在首部插入 7 . 链表变成 7->6->NULL
push(&head, 7);
// 在首部插入 1. 链表变成 1->7->6->NULL
push(&head, 1);
// 在尾部插入4. 链表变成 1->7->6->4->NULL
append(&head, 4);
// 在 7 后面插入 8, 链表变成 1->7->8->6->4->NULL
insertAfter(head->next, 8);
printf("\n Created Linked list is: ");
printList(head);
return 0;
}
输出:
Created Linked list is: 1 7 8 6 4