📅  最后修改于: 2023-12-03 15:12:32.480000             🧑  作者: Mango
链表是计算机科学中最基本、最重要的数据结构之一。像树、堆、图这样的数据结构都是以链表为基础的。链表可以通过指针来实现,它将一系列的节点按照一定的次序串联在一起。
在计算机科学中,链表是由一组节点组成的数据结构,每个节点包含了数据和指向下一个节点的指针。链表的头指针指向第一个节点。
链表的定义通常包括链表的数据类型(如整型、字符型等),节点的结构,以及链表操作的函数:
// 链表节点的结构体
typedef struct Node {
int data; // 节点的数据
struct Node *next; // 指向下一个节点的指针
} Node;
// 链表的结构体
typedef struct LinkedList {
Node *head; // 链表头指针
} LinkedList;
// 链表的操作函数
void append(LinkedList *list, int data); // 在链表中插入一个节点
void remove(LinkedList *list, int data); // 从链表中删除一个节点
上面的代码定义了链表节点的结构体、链表的结构体和链表操作的函数。其中 append
和 remove
是链表的两个基本操作,分别用于在链表中插入和删除节点。
链表可以分为单向链表、双向链表和循环链表。
单向链表中每个节点只有一个指向下一个节点的指针。头节点指针指向第一个节点,尾节点的指针为空。
单向链表的特点是只能单向遍历,不能反向遍历。
单向链表示例:
head -> 1 -> 2 -> 3 -> 4 -> NULL
双向链表中每个节点有两个指针,一个指向下一个节点,一个指向上一个节点。头节点的上一个指针为空,尾节点的下一个指针为空。
双向链表的特点是可以双向遍历,即可以从头节点开始往后遍历,也可以从尾节点开始往前遍历。
双向链表示例:
NULL <- head <-> 1 <-> 2 <-> 3 <-> 4 <-> NULL
循环链表中最后一个节点的指针指向头节点,形成了一个循环。循环链表可以是单向的或双向的。
循环链表的特点是可以从任意节点开始循环遍历整个链表。
单向循环链表示例:
head -> 1 -> 2 -> 3 -> 4 -> head
双向循环链表示例:
NULL <- head <-> 1 <-> 2 <-> 3 <-> 4 <-> head ->
链表的基本操作包括插入节点、删除节点、查找节点、遍历链表等。下面介绍链表的基本操作。
链表中插入一个节点通常有两种情况,一种是在链表末尾插入,一种是在链表中间插入。
在链表末尾插入节点的实现如下:
void append(LinkedList *list, int data) {
Node *node = (Node *) malloc(sizeof(Node)); // 创建一个新节点
node->data = data; // 设置节点数据
node->next = NULL; // 将节点的 next 指针置为空指针
if(list->head == NULL) { // 如果链表为空,则将头指针指向新节点
list->head = node;
} else {
Node *last = list->head;
while(last->next != NULL) { // 找到最后一个节点
last = last->next;
}
last->next = node; // 将最后一个节点的 next 指针指向新节点
}
}
在链表中间插入节点的实现如下:
void insert(LinkedList *list, Node *prev, int data) {
Node *node = (Node *) malloc(sizeof(Node)); // 创建一个新节点
node->data = data; // 设置节点数据
if(prev == NULL) { // 如果前一个节点为空,则在头部插入新节点
node->next = list->head;
list->head = node;
} else {
node->next = prev->next; // 将新节点的 next 指针指向前一个节点的后继节点
prev->next = node; // 将前一个节点的 next 指针指向新节点
}
}
链表中删除一个节点通常也有两种情况,一种是删除头节点,一种是删除中间节点。
删除头节点的实现如下:
void remove(LinkedList *list, int data) {
Node *node = list->head;
Node *prev = NULL;
while(node != NULL && node->data != data) { // 找到要删除的节点
prev = node;
node = node->next;
}
if(node != NULL) { // 如果找到了,则删除该节点
if(prev == NULL) {
list->head = node->next;
} else {
prev->next = node->next;
}
free(node);
}
}
删除中间节点的实现如下:
void removeAt(LinkedList *list, int index) {
Node *node = list->head;
Node *prev = NULL;
int i = 0;
while(node != NULL && i != index) { // 找到要删除的节点
prev = node;
node = node->next;
i++;
}
if(node != NULL) { // 如果找到了,则删除该节点
if(prev == NULL) {
list->head = node->next;
} else {
prev->next = node->next;
}
free(node);
}
}
遍历链表的实现如下:
void traverse(LinkedList *list) {
Node *node = list->head;
while(node != NULL) { // 依次遍历链表节点
printf("%d ", node->data);
node = node->next;
}
}
链表是计算机科学中最基本、最重要的数据结构之一,可以通过指针来实现。链表可以分为单向链表、双向链表和循环链表。链表的基本操作包括插入节点、删除节点、查找节点和遍历链表等。链表的优点是可以动态地分配和释放内存,缺点是不能随机访问节点。在使用链表时需要注意内存泄漏的情况。