目录
引言
1.1 什么是链表?
链表是一种基于节点间相互链接关系来存储数据的数据结构,每个节点包含两个主要部分:数据域(存储实际数据元素)和指针域(存储指向下一个节点的地址)。链表的最大特点在于其动态性和非连续性,允许数据元素在内存中任意位置分布。
1.2 链表与数组的对比分析
- 数组是连续存储的一系列元素,可通过下标快速访问,但插入和删除元素可能导致大量元素移动;
- 链表则以离散方式存储元素,插入和删除只需改变相邻节点的指针,但访问元素需要从头节点逐个查找。
第一部分:链表基础构建模块
1.3 节点结构剖析
1// 定义一个简单的链表节点结构体
2struct ListNode {
3 int data; // 数据域
4 ListNode* next; // 指针域,指向下一个节点
5};
1.4 链表基本类型详述
- 单向链表:
1// 创建一个单向链表并添加节点
2ListNode* createLinkedList() {
3 ListNode* head = new ListNode{1, nullptr};
4 ListNode* second = new ListNode{2, nullptr};
5 head->next = second;
6 // 更多节点的添加...
7 return head;
8}
- 双向链表:每个节点还需一个指向前驱节点的指针
prev
。
// 双向链表节点结构体
struct DListNode {
int data; // 数据域
DListNode* prev; // 指向前一个节点的指针
DListNode* next; // 指向下一个节点的指针
};
// 初始化一个新的双向链表节点
DListNode* createNewNode(int value) {
DListNode* newNode = new DListNode();
newNode->data = value;
newNode->prev = nullptr;
newNode->next = nullptr;
return newNode;
}
// 在双向链表中插入新节点
void insertNodeToDoublyLinkedList(DListNode*& head, int value) {
DListNode* newNode = createNewNode(value);
if (head == nullptr) {
head = newNode;
head->prev = head;
head->next = head;
} else {
// 找到最后一个节点
DListNode* tail = head->prev;
newNode->next = head;
newNode->prev = tail;
head->prev = newNode;
tail->next = newNode;
}
}
// 双向链表的遍历
void traverseDoublyLinkedList(DListNode* head) {
if (head == nullptr) {
return;
}
DListNode* curr = head;
do {
std::cout << curr->data << " ";
curr = curr->next;
} while (curr != head);
}
// 删除指定值的节点(假设链表不为空)
void deleteNodeFromDoublyLinkedList(DListNode*& head, int value) {
if (head == nullptr) {
return;
}
DListNode* curr = head;
do {
if (curr->data == value) {
if (curr->prev == curr) {
// 处理只有一个节点的情况
delete curr;
head = nullptr;
return;
}
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
delete curr;
return;
}
curr = curr->next;
} while (curr != head);
}
- 循环链表:将尾节点的
next
指针指向头节点。
// 创建一个空的双向循环链表
DListNode* createCircularDoublyLinkedList() {
DListNode* dummy = createNewNode(0); // 假设用一个虚拟节点作为头节点
dummy->next = dummy;
dummy->prev = dummy;
return dummy;
}
// 向循环双向链表中插入节点
void insertIntoCircularDoublyLinkedList(DListNode*& head, int value) {
DListNode* newNode = createNewNode(value);
DListNode* last = head->prev;
newNode->prev = last;
newNode->next = head;
last->next = newNode;
head->prev = newNode;
}
// 循环双向链表的遍历
void traverseCircularDoublyLinkedList(DListNode* head) {
if (head == nullptr || head->next == head) {
return;
}
DListNode* curr = head;
do {
std::cout << curr->data << " ";
curr = curr->next;
} while (curr != head);
}
// 从循环双向链表中删除指定值的节点(假设链表不为空)
void deleteFromCircularDoublyLinkedList(DListNode*& head, int value) {
if (head == nullptr || head->next == head) {
return;
}
DListNode* curr = head;
do {
if (curr->data == value) {
curr->prev->next = curr->next;
curr->next->prev = curr->prev;
if (curr == head) {
head = curr->next;
}
delete curr;
break;
}
curr = curr->next;
} while (curr != head);
}
第二部分:链表的操作与特性
2.1 链表的创建与销毁
1// 插入新节点到单向链表
2void insertNode(ListNode*& head, int value) {
3 ListNode* newNode = new ListNode{value, nullptr};
4 if (!head) {
5 head = newNode;
6 } else {
7 ListNode* temp = head;
8 while (temp->next) {
9 temp = temp->next;
10 }
11 temp->next = newNode;
12 }
13}
14
15// 删除链表中指定值的节点
16void deleteNode(ListNode*& head, int value) {
17 ListNode* current = head;
18 ListNode* prev = nullptr;
19
20 while (current && current->data != value) {
21 prev = current;
22 current = current->next;
23 }
24
25 if (current) {
26 if (prev) {
27 prev->next = current->next;
28 } else {
29 head = current->next;
30 }
31 delete current;
32 }
33}
2.2 链表的遍历方式及时间复杂度
1// 前向遍历打印链表元素
2void traverseForward(ListNode* head) {
3 while (head) {
4 std::cout << head->data << " ";
5 head = head->next;
6 }
7}
8
9// 时间复杂度分析:链表的遍历时间为O(n),n为链表长度
2.3 链表的应用优势与局限性
链表的优势在于动态管理和高效的插入删除操作,但受限于线性访问模式,不适用于需要频繁随机访问的场景。
第三部分:链表的高级应用与扩展
3.1 链表在其他数据结构中的体现
- 队列:借助链表实现先进先出(FIFO)原则
- 堆栈:利用链表实现后进先出(LIFO)原理
3.2 链表与其他算法结合
- 哈希表冲突解决:当哈希表发生冲突时,可采用链地址法将相同哈希值的元素通过链表链接起来
- 排序算法优化:例如,在快速排序和归并排序中,链表可作为暂存区,无需关心内存连续性问题
结语
总结链表的重要性和适用场景,并展望未来技术发展中链表可能的新应用和改进方向。