链表是一种线性数据结构,它通过一系列节点(Node)连接在一起,其中每个节点都包含两部分:
- 数据(Data):存储节点中的值。
- 指针(Pointer):指向下一个节点的地址。
链表的特点
- 动态内存分配:链表节点在运行时分配,链表大小不需要事先声明,可以灵活扩展。
- 高效的插入和删除操作:链表中插入或删除元素不需要像数组那样移动其他元素。
- 不连续存储:链表节点不必在内存中连续存储,使用指针将节点链接起来。
链表的类型
-
单链表(Singly Linked List)
每个节点只包含一个指向下一个节点的指针。最后一个节点的指针为nullptr
。 -
双向链表(Doubly Linked List)
每个节点包含两个指针:一个指向下一个节点,另一个指向前一个节点。 -
循环链表(Circular Linked List)
最后一个节点的指针指向链表的头节点,形成一个环。
链表的使用
1. 定义链表节点
在 C++ 中,可以用结构体或类来定义链表节点。例如:
struct ListNode {
int data; // 数据部分
ListNode* next; // 指针部分,指向下一个节点
ListNode(int x) : data(x), next(nullptr) {} // 构造函数
};
2. 创建链表
通过动态分配节点的方式创建一个链表:
ListNode* head = new ListNode(1); // 创建头节点
head->next = new ListNode(2); // 创建第二个节点
head->next->next = new ListNode(3); // 创建第三个节点
此时链表结构为:1 -> 2 -> 3 -> nullptr
链表操作
- 遍历链表
void printList(ListNode* head) { ListNode* current = head; // 从头节点开始遍历 while (current != nullptr) { std::cout << current->data << " "; current = current->next; } }
-
插入节点
- 在链表头部插入:
ListNode* newNode = new ListNode(0); // 创建新节点 newNode->next = head; // 指向原链表的头节点 head = newNode; // 更新头节点
3.删除节点
- 删除头节点:
ListNode* temp = head; head = head->next; // 更新头节点 delete temp; // 释放内存
缺点
- 删除中间节点:
ListNode* temp = head->next; // 找到要删除的节点 head->next = temp->next; // 更新前一个节点的指针 delete temp; // 释放内存
链表的优缺点
优点
- 动态大小:链表的长度可以在运行时动态变化。
- 高效的插入和删除:在链表中间插入或删除元素非常高效(只需调整指针),不需要像数组那样移动大量元素。
- 随机访问效率低:无法通过索引直接访问某个元素,需要从头节点开始遍历。
- 额外的内存消耗:每个节点都需要额外的指针存储空间。
- 操作复杂性:与数组相比,链表需要更多的指针操作,代码复杂度较高。
- 节省内存:链表只在需要时分配内存,避免了数组需要提前声明固定大小的问题。