在这里插入图片描述

引言

在计算机科学中,数据结构是存储和组织数据的一种方式,它不仅影响着数据的存取效率,还决定了算法的设计和实现。链表作为一种基础而强大的线性数据结构,在实际应用中占据着重要的地位。与数组相比,链表提供了更加灵活的内存使用方式,允许在运行时动态地调整其大小,并且易于实现插入和删除操作。然而,链表也因其指针操作的复杂性而闻名,对于初学者来说可能较为难以掌握。

本文旨在为读者提供一个全面深入的指南,涵盖单向链表、双向链表、单向循环链表及双向循环链表的基础知识、节点操作、插入删除方法以及遍历等核心概念。通过详细的代码示例和解释,帮助读者理解链表的工作原理,并能够在实际编程中灵活运用这些知识。无论是正在学习数据结构的计算机专业学生,还是希望提升编程技能的职业开发者,都能从中受益匪浅。

接下来的部分,我们将逐步介绍每种类型的链表,包括如何定义节点结构、创建新节点、进行各种插入和删除操作,以及如何遍历和反转链表。每一步都将配有详尽的代码片段和注释,以便读者能够轻松跟随并实践。让我们一起探索链表的奥秘吧!

一、单向链表
单向链表节点定义

在定义一个单向链表之前,我们需要首先定义一个节点(Node)。每个节点包含两个部分:存储数据的部分(例如整型变量)和一个指向下一个节点的指针。这里我们定义了一个名为 SingleNode 的结构体来表示单向链表中的节点。

struct SingleNode {
    int data;         // 存储数据的字段
    struct SingleNode *next; // 指向下一个节点的指针
};
typedef struct SingleNode SingleNode; // 定义一个类型别名简化后续代码
创建新节点

创建新节点时,我们需要动态分配内存来存储这个节点,并且设置它的数据域和指针域。如果内存分配失败,我们需要报告错误并退出程序。

SingleNode *createSingleNode(int data) {
    SingleNode *newNode = (SingleNode*)malloc(sizeof(SingleNode)); // 动态分配内存
    if (newNode == NULL) {
        perror("Memory allocation failed"); // 如果分配失败,则打印错误信息
        exit(EXIT_FAILURE); // 终止程序
    }
    newNode->data = data; // 设置数据域
    newNode->next = NULL; // 设置指向下一个节点的指针为 NULL
    return newNode; // 返回新创建的节点
}
在单向链表中插入节点

在单向链表中插入节点可以有三种方式:插入到头部、插入到尾部以及在指定节点之后插入。下面分别给出这三种情况下的实现。

// 在链表头部插入节点
void insertAtHeadSingle(SingleNode **head, int data) {
    SingleNode *newNode = createSingleNode(data); // 创建新节点
    newNode->next = *head; // 新节点的下一个指针指向原来的头节点
    *head = newNode; // 更新头指针指向新节点
}

// 在链表尾部插入节点
void insertAtTailSingle(SingleNode **head, int data) {
    SingleNode *newNode = createSingleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
    } else { // 如果链表非空
        SingleNode *current = *head; // 当前节点初始化为头节点
        while (current->next != NULL) { // 遍历至最后一个节点
            current = current->next;
        }
        current->next = newNode; // 在末尾添加新节点
    }
}

// 在指定节点之后插入节点
void insertAfterSingle(SingleNode *prevNode, int data) {
    if (prevNode == NULL) { // 检查前一个节点是否有效
        printf("Previous node cannot be NULL.\n");
        return;
    }
    SingleNode *newNode = createSingleNode(data); // 创建新节点
    newNode->next = prevNode->next; // 新节点的下一个指针指向前一个节点的下一个节点
    prevNode->next = newNode; // 前一个节点的下一个指针指向新节点
}
在单向链表中删除节点

删除节点也有三种情况:删除头部节点、删除尾部节点以及删除指定节点。下面是相应的实现代码。

// 删除链表头部节点
void deleteHeadSingle(SingleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    SingleNode *temp = *head; // 临时保存头节点
    *head = (*head)->next; // 头指针指向下一个节点
    free(temp); // 释放原头节点占用的内存
}

// 删除链表尾部节点
void deleteTailSingle(SingleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    if ((*head)->next == NULL) { // 如果只有一个节点
        free(*head); // 释放内存
        *head = NULL; // 头指针置空
    } else { // 如果有多个节点
        SingleNode *current = *head; // 当前节点初始化为头节点
        while (current->next->next != NULL) { // 遍历至倒数第二个节点
            current = current->next;
        }
        free(current->next); // 释放尾节点占用的内存
        current->next = NULL; // 将当前节点的下一个指针置空
    }
}

// 删除指定节点
void deleteNodeSingle(SingleNode **head, SingleNode *del) {
    if (*head == NULL || del == NULL) return; // 如果链表为空或待删除节点无效,则直接返回
    if (*head == del) { // 如果待删除节点是头节点
        *head = del->next; // 更新头指针
    }
    SingleNode *current = *head; // 当前节点初始化为头节点
    while (current->next != del) { // 寻找待删除节点的前一个节点
        current = current->next;
    }
    current->next = del->next; // 将前一个节点的下一个指针指向待删除节点的下一个节点
    free(del); // 释放待删除节点占用的内存
}
遍历单向链表

遍历链表是一个基本操作,它允许我们查看链表中的所有元素。我们可以从头节点开始,沿着 next 指针遍历直到 NULL。

void printListSingle(SingleNode *head) {
    SingleNode *current = head; // 当前节点初始化为头节点
    while (current != NULL) { // 遍历至最后一个节点
        printf("%d ", current->data); // 输出数据
        current = current->next; // 移动到下一个节点
    }
    printf("\n"); // 换行
}
反转单向链表

反转链表意味着改变所有节点的 next 指针方向,使得链表从尾到头。这可以通过迭代地交换每个节点的 next 指针来实现。

void reverseListSingle(SingleNode **head) {
    SingleNode *prev = NULL; // 上一个节点初始化为 NULL
    SingleNode *current = *head; // 当前节点初始化为头节点
    SingleNode *next = NULL; // 临时保存下一个节点
    while (current != NULL) { // 遍历链表
        next = current->next; // 保存下一个节点
        current->next = prev; // 反转当前节点的下一个指针
        prev = current; // 当前节点成为上一个节点
        current = next; // 下一个节点成为当前节点
    }
    *head = prev; // 更新头指针指向最后一个节点(即反转后的头节点)
}

在这里插入图片描述

二、双向链表
双向链表节点定义

与单向链表不同,双向链表的每个节点除了有一个指向下个节点的指针外,还有一个指向上一个节点的指针。这样可以在两个方向上访问链表中的节点。

struct DoubleNode {
    int data;         // 数据域
    struct DoubleNode *prev; // 指向前一个节点的指针
    struct DoubleNode *next; // 指向下一个节点的指针
};
typedef struct DoubleNode DoubleNode; // 定义一个类型别名简化后续代码
创建新节点

创建双向链表的新节点类似于单向链表的新节点创建,只是多了指向前一个节点的指针。

DoubleNode *createDoubleNode(int data) {
    DoubleNode *newNode = (DoubleNode*)malloc(sizeof(DoubleNode)); // 动态分配内存
    if (newNode == NULL) {
        perror("Memory allocation failed"); // 如果分配失败,则打印错误信息
        exit(EXIT_FAILURE); // 终止程序
    }
    newNode->data = data; // 设置数据域
    newNode->prev = NULL; // 设置指向前一个节点的指针为 NULL
    newNode->next = NULL; // 设置指向下一个节点的指针为 NULL
    return newNode; // 返回新创建的节点
}
在双向链表中插入节点

插入节点的操作与单向链表相似,只是需要额外处理前一个节点的指针。

// 在链表头部插入节点
void insertAtHeadDouble(DoubleNode **head, int data) {
    DoubleNode *newNode = createDoubleNode(data); // 创建新节点
    newNode->next = *head; // 新节点的下一个指针指向原来的头节点
    if (*head != NULL) (*head)->prev = newNode; // 如果原来的头节点存在,则更新其前一个指针
    *head = newNode; // 更新头指针指向新节点
}

// 在链表尾部插入节点
void insertAtTailDouble(DoubleNode **head, int data) {
    DoubleNode *newNode = createDoubleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
    } else { // 如果链表非空
        DoubleNode *current = *head; // 当前节点初始化为头节点
        while (current->next != NULL) { // 遍历至最后一个节点
            current = current->next;
        }
        current->next = newNode; // 在末尾添加新节点
        newNode->prev = current; // 新节点的前一个指针指向当前节点
    }
}

// 在指定节点之后插入节点
void insertAfterDouble(DoubleNode *prevNode, int data) {
    if (prevNode == NULL) { // 检查前一个节点是否有效
        printf("Previous node cannot be NULL.\n");
        return;
    }
    DoubleNode *newNode = createDoubleNode(data); // 创建新节点
    newNode->next = prevNode->next; // 新节点的下一个指针指向前一个节点的下一个节点
    newNode->prev = prevNode; // 新节点的前一个指针指向当前节点
    if (prevNode->next != NULL) prevNode->next->prev = newNode; // 如果前一个节点的下一个节点存在,则更新其前一个指针
    prevNode->next = newNode; // 前一个节点的下一个指针指向新节点
}
在双向链表中删除节点

删除节点时,除了要处理下一个节点的指针外,还需要处理前一个节点的指针。

// 删除链表头部节点
void deleteHeadDouble(DoubleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    DoubleNode *temp = *head; // 临时保存头节点
    *head = (*head)->next; // 头指针指向下一个节点
    if (*head != NULL) (*head)->prev = NULL; // 如果新的头节点存在,则更新其前一个指针
    free(temp); // 释放原头节点占用的内存
}

// 删除链表尾部节点
void deleteTailDouble(DoubleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    if ((*head)->next == NULL) { // 如果只有一个节点
        free(*head); // 释放内存
        *head = NULL; // 头指针置空
    } else { // 如果有多个节点
        DoubleNode *current = *head; // 当前节点初始化为头节点
        while (current->next->next != NULL) { // 遍历至倒数第二个节点
            current = current->next;
        }
        free(current->next); // 释放尾节点占用的内存
        current->next = NULL; // 将当前节点的下一个指针置空
    }
}

// 删除指定节点
void deleteNodeDouble(DoubleNode **head, DoubleNode *del) {
    if (*head == NULL || del == NULL) return; // 如果链表为空或待删除节点无效,则直接返回
    if (*head == del) { // 如果待删除节点是头节点
        *head = del->next; // 更新头指针
    }
    if (del->prev != NULL) del->prev->next = del->next; // 如果待删除节点的前一个节点存在,则更新其下一个指针
    if (del->next != NULL) del->next->prev = del->prev; // 如果待删除节点的下一个节点存在,则更新其前一个指针
    free(del); // 释放待删除节点占用的内存
}
遍历双向链表

遍历双向链表的方式与单向链表相同,只是我们有了更多的选择,可以从头节点开始也可以从尾节点开始。

void printListDouble(DoubleNode *head) {
    DoubleNode *current = head; // 当前节点初始化为头节点
    while (current != NULL) { // 遍历至最后一个节点
        printf("%d ", current->data); // 输出数据
        current = current->next; // 移动到下一个节点
    }
    printf("\n"); // 换行
}
反转双向链表

反转双向链表的过程与单向链表类似,但是需要同时处理 prev 和 next 指针。

void reverseListDouble(DoubleNode **head) {
    DoubleNode *current = *head; // 当前节点初始化为头节点
    DoubleNode *temp = NULL; // 临时保存前一个节点
    while (current != NULL) { // 遍历链表
        temp = current->prev; // 保存前一个节点
        current->prev = current->next; // 反转当前节点的前一个指针
        current->next = temp; // 反转当前节点的下一个指针
        current = current->prev; // 当前节点成为前一个节点
    }
    if (temp != NULL) *head = temp->prev; // 更新头指针指向最后一个节点(即反转后的头节点)
}

在这里插入图片描述

三、单向循环链表
单向循环链表节点定义

单向循环链表与单向链表的区别在于,最后一个节点的 next 指针指向的是头节点,形成了一个闭环。

struct CircularSingleNode {
    int data;         // 数据域
    struct CircularSingleNode *next; // 指向下一个节点的指针
};
typedef struct CircularSingleNode CircularSingleNode; // 定义一个类型别名简化后续代码
创建新节点

创建单向循环链表的新节点与创建普通单向链表的新节点相同。

CircularSingleNode *createCircularSingleNode(int data) {
    CircularSingleNode *newNode = (CircularSingleNode*)malloc(sizeof(CircularSingleNode)); // 动态分配内存
    if (newNode == NULL) {
        perror("Memory allocation failed"); // 如果分配失败,则打印错误信息
        exit(EXIT_FAILURE); // 终止程序
    }
    newNode->data = data; // 设置数据域
    newNode->next = NULL; // 设置指向下一个节点的指针为 NULL
    return newNode; // 返回新创建的节点
}
在单向循环链表中插入节点

插入节点时,需要注意最后一个节点的 next 指针应该指向头节点。

// 在链表头部插入节点
void insertAtHeadCircularSingle(CircularSingleNode **head, int data) {
    CircularSingleNode *newNode = createCircularSingleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
        newNode->next = newNode; // 形成单个节点的循环
    } else { // 如果链表非空
        newNode->next = *head; // 新节点的下一个指针指向原来的头节点
        CircularSingleNode *tail = *head; // 临时保存尾节点
        while (tail->next != *head) { // 遍历至最后一个节点
            tail = tail->next;
        }
        tail->next = newNode; // 更新尾节点的下一个指针
        *head = newNode; // 更新头指针指向新节点
    }
}

// 在链表尾部插入节点
void insertAtTailCircularSingle(CircularSingleNode **head, int data) {
    CircularSingleNode *newNode = createCircularSingleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
        newNode->next = newNode; // 形成单个节点的循环
    } else { // 如果链表非空
        CircularSingleNode *tail = *head; // 临时保存尾节点
        while (tail->next != *head) { // 遍历至最后一个节点
            tail = tail->next;
        }
        tail->next = newNode; // 更新尾节点的下一个指针
        newNode->next = *head; // 新节点的下一个指针指向头节点
    }
}

// 在指定节点之后插入节点
void insertAfterCircularSingle(CircularSingleNode *prevNode, int data) {
    if (prevNode == NULL) { // 检查前一个节点是否有效
        printf("Previous node cannot be NULL.\n");
        return;
    }
    CircularSingleNode *newNode = createCircularSingleNode(data); // 创建新节点
    newNode->next = prevNode->next; // 新节点的下一个指针指向前一个节点的下一个节点
    prevNode->next = newNode; // 前一个节点的下一个指针指向新节点
}
在单向循环链表中删除节点

删除节点时,需要特别注意最后一个节点的 next 指针应该重新指向头节点。

// 删除链表头部节点
void deleteHeadCircularSingle(CircularSingleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    CircularSingleNode *temp = *head; // 临时保存头节点
    *head = (*head)->next; // 头指针指向下一个节点
    CircularSingleNode *tail = *head; // 临时保存尾节点
    while (tail->next != *head) { // 遍历至最后一个节点
        tail = tail->next;
    }
    tail->next = *head; // 更新尾节点的下一个指针
    free(temp); // 释放原头节点占用的内存
}

// 删除链表尾部节点
void deleteTailCircularSingle(CircularSingleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    CircularSingleNode *current = *head; // 当前节点初始化为头节点
    CircularSingleNode *prev = NULL; // 前一个节点初始化为 NULL
    while (current->next != *head) { // 寻找倒数第二个节点
        prev = current;
        current = current->next;
    }
    if (prev == NULL) { // 如果只有一个节点
        free(current); // 释放内存
        *head = NULL; // 头指针置空
    } else { // 如果有多个节点
        prev->next = *head; // 更新前一个节点的下一个指针
        free(current); // 释放尾节点占用的内存
    }
}

// 删除指定节点
void deleteNodeCircularSingle(CircularSingleNode **head, CircularSingleNode *del) {
    if (*head == NULL || del == NULL) return; // 如果链表为空或待删除节点无效,则直接返回
    if (*head == del) { // 如果待删除节点是头节点
        *head = del->next; // 更新头指针
    }
    if (del->next != NULL) del->next->prev = del->prev; // 如果待删除节点的下一个节点存在,则更新其前一个指针
    free(del); // 释放待删除节点占用的内存
}
遍历单向循环链表

遍历单向循环链表时,我们需要确保不会陷入无限循环。为此,我们在遍历时检查当前节点是否再次到达头节点。

void printListCircularSingle(CircularSingleNode *head) {
    CircularSingleNode *current = head; // 当前节点初始化为头节点
    do { // 遍历直至回到头节点
        printf("%d ", current->data); // 输出数据
        current = current->next; // 移动到下一个节点
    } while (current != head);
    printf("\n"); // 换行
}
反转单向循环链表

反转单向循环链表时,除了反转 next 指针外,还需要重新连接头节点以形成闭环。

void reverseListCircularSingle(CircularSingleNode **head) {
    // 断开循环
    CircularSingleNode *last = *head;
    while (last->next != *head) {
        last = last->next;
    }
    last->next = NULL;

    // 反转链表
    CircularSingleNode *prev = NULL;
    CircularSingleNode *current = *head;
    CircularSingleNode *next = NULL;
    while (current != NULL) {
        next = current->next;
        current->next = prev;
        prev = current;
        current = next;
    }
    *head = prev;

    // 重新连接形成循环
    last = *head;
    while (last->next != NULL) {
        last = last->next;
    }
    last->next = *head;
}

在这里插入图片描述

四、双向循环链表
双向循环链表节点定义

双向循环链表结合了双向链表和单向循环链表的特点,每个节点都有指向前一个节点和下一个节点的指针,并且最后一个节点的 next 指针指向头节点,头节点的 prev 指针指向最后一个节点。

struct CircularDoubleNode {
    int data;         // 数据域
    struct CircularDoubleNode *prev; // 指向前一个节点的指针
    struct CircularDoubleNode *next; // 指向下一个节点的指针
};
typedef struct CircularDoubleNode CircularDoubleNode; // 定义一个类型别名简化后续代码
创建新节点

创建双向循环链表的新节点与创建普通双向链表的新节点相同。

CircularDoubleNode *createCircularDoubleNode(int data) {
    CircularDoubleNode *newNode = (CircularDoubleNode*)malloc(sizeof(CircularDoubleNode)); // 动态分配内存
    if (newNode == NULL) {
        perror("Memory allocation failed"); // 如果分配失败,则打印错误信息
        exit(EXIT_FAILURE); // 终止程序
    }
    newNode->data = data; // 设置数据域
    newNode->prev = NULL; // 设置指向前一个节点的指针为 NULL
    newNode->next = NULL; // 设置指向下一个节点的指针为 NULL
    return newNode; // 返回新创建的节点
}
在双向循环链表中插入节点

插入节点时,除了要处理 next 指针外,还要处理 prev 指针,确保闭环正确。

// 在链表头部插入节点
void insertAtHeadCircularDouble(CircularDoubleNode **head, int data) {
    CircularDoubleNode *newNode = createCircularDoubleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
        newNode->next = newNode; // 形成单个节点的循环
        newNode->prev = newNode;
    } else { // 如果链表非空
        newNode->next = *head; // 新节点的下一个指针指向原来的头节点
        newNode->prev = (*head)->prev; // 新节点的前一个指针指向原来的尾节点
        (*head)->prev->next = newNode; // 原来的尾节点的下一个指针指向新节点
        (*head)->prev = newNode; // 原来的头节点的前一个指针指向新节点
        *head = newNode; // 更新头指针指向新节点
    }
}

// 在链表尾部插入节点
void insertAtTailCircularDouble(CircularDoubleNode **head, int data) {
    CircularDoubleNode *newNode = createCircularDoubleNode(data); // 创建新节点
    if (*head == NULL) { // 如果链表为空
        *head = newNode; // 直接更新头指针
        newNode->next = newNode; // 形成单个节点的循环
        newNode->prev = newNode;
    } else { // 如果链表非空
        newNode->prev = (*head)->prev; // 新节点的前一个指针指向原来的尾节点
        newNode->next = *head; // 新节点的下一个指针指向原来的头节点
        (*head)->prev->next = newNode; // 原来的尾节点的下一个指针指向新节点
        (*head)->prev = newNode; // 原来的头节点的前一个指针指向新节点
    }
}

// 在指定节点之后插入节点
void insertAfterCircularDouble(CircularDoubleNode *prevNode, int data) {
    if (prevNode == NULL) { // 检查前一个节点是否有效
        printf("Previous node cannot be NULL.\n");
        return;
    }
    CircularDoubleNode *newNode = createCircularDoubleNode(data); // 创建新节点
    newNode->next = prevNode->next; // 新节点的下一个指针指向前一个节点的下一个节点
    newNode->prev = prevNode; // 新节点的前一个指针指向当前节点
    prevNode->next->prev = newNode; // 如果前一个节点的下一个节点存在,则更新其前一个指针
    prevNode->next = newNode; // 前一个节点的下一个指针指向新节点
}
在双向循环链表中删除节点

删除节点时,除了要处理 next 指针外,还要处理 prev 指针,确保闭环正确。

// 删除链表头部节点
void deleteHeadCircularDouble(CircularDoubleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    CircularDoubleNode *temp = *head; // 临时保存头节点
    *head = (*head)->next; // 头指针指向下一个节点
    if (*head != NULL) (*head)->prev = temp->prev; // 如果新的头节点存在,则更新其前一个指针
    free(temp); // 释放原头节点占用的内存
}

// 删除链表尾部节点
void deleteTailCircularDouble(CircularDoubleNode **head) {
    if (*head == NULL) return; // 如果链表为空,则直接返回
    CircularDoubleNode *current = *head; // 当前节点初始化为头节点
    CircularDoubleNode *prev = NULL; // 前一个节点初始化为 NULL
    while (current->next != *head) { // 寻找倒数第二个节点
        prev = current;
        current = current->next;
    }
    if (prev == NULL) { // 如果只有一个节点
        free(current); // 释放内存
        *head = NULL; // 头指针置空
    } else { // 如果有多个节点
        prev->next = *head; // 更新前一个节点的下一个指针
        free(current); // 释放尾节点占用的内存
    }
}

// 删除指定节点
void deleteNodeCircularDouble(CircularDoubleNode **head, CircularDoubleNode *del) {
    if (*head == NULL || del == NULL) return; // 如果链表为空或待删除节点无效,则直接返回
    if (*head == del) { // 如果待删除节点是头节点
        *head = del->next; // 更新头指针
    }
    del->prev->next = del->next; // 如果待删除节点的前一个节点存在,则更新其下一个指针
    del->next->prev = del->prev; // 如果待删除节点的下一个节点存在,则更新其前一个指针
    free(del); // 释放待删除节点占用的内存
}
遍历双向循环链表

遍历双向循环链表的方式与单向循环链表相同,只需确保不会陷入无限循环。

void printListCircularDouble(CircularDoubleNode *head) {
    CircularDoubleNode *current = head; // 当前节点初始化为头节点
    do { // 遍历直至回到头节点
        printf("%d ", current->data); // 输出数据
        current = current->next; // 移动到下一个节点
    } while (current != head);
    printf("\n"); // 换行
}
反转双向循环链表

反转双向循环链表时,除了反转 next 指针外,还要反转 prev 指针,并重新连接头节点以形成闭环。

void reverseListCircularDouble(CircularDoubleNode **head) {
    CircularDoubleNode *current = *head; // 当前节点初始化为头节点
    CircularDoubleNode *temp = NULL; // 临时保存前一个节点
    do { // 遍历链表
        temp = current->prev; // 保存前一个节点
        current->prev = current->next; // 反转当前节点的前一个指针
        current->next = temp; // 反转当前节点的下一个指针
        current = current->prev; // 当前节点成为前一个节点
    } while (current != *head);
    if (temp != NULL) *head = temp->prev; // 更新头指针指向最后一个节点(即反转后的头节点)
}
结论

链表是C语言中非常有用的数据结构,适用于需要频繁添加或删除元素的场景。理解链表的基本概念和操作方法是成为优秀程序员的关键。希望这篇指南能够帮助你更好地理解和使用链表。随着实践的深入,你将掌握更多高级技巧,并能灵活应用到实际开发中。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐