在链表中,每个节点都有一个指向下一个节点的指针。删除一个节点的本质是将前一个节点的指针指向要删除节点的下一个节点,从而跳过要删除的节点。以下是详细解释为什么以及如何这样做:
1. **链表的结构**:
一个链表节点包含两个部分:存储的数据和指向下一个节点的指针。
```cpp
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
```
2. **删除节点的操作**:
为了从链表中删除一个节点,我们需要调整前一个节点的指针,使其指向要删除节点的下一个节点。
假设我们有一个链表:`1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6`,我们要删除所有值为 `6` 的节点。删除节点 `6` 时,我们需要让前一个节点(例如 `2`)的 `next` 指向节点 `6` 的下一个节点(即 `3`)。
3. **指针操作的具体步骤**:
- 我们遍历链表,用指针 `current` 指向当前节点,用指针 `prev` 指向当前节点的前一个节点。
- 如果 `current->val` 等于 `val`,即我们要删除的节点,我们需要跳过这个节点。
- `prev->next = current->next` 这行代码的作用是:将前一个节点的 `next` 指针指向当前节点的下一个节点。这样,当前节点 `current` 就被跳过了,相当于从链表中删除了。
4. **为什么这样写**:
通过这种方式,我们可以有效地删除节点,而不需要实际释放节点的内存。这种方式也避免了重新遍历链表或创建新的链表。
```cpp
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 创建虚拟头节点
dummyHead->next = head;
ListNode* current = head;
ListNode* prev = dummyHead;
while (current != nullptr) {
if (current->val == val) { // 如果当前节点的值等于给定值
prev->next = current->next; // 跳过当前节点
} else {
prev = current; // 前一个节点移动到当前节点
}
current = current->next; // 当前节点移动到下一个节点
}
ListNode* newHead = dummyHead->next;
delete dummyHead; // 释放虚拟头节点的内存
return newHead;
}
};
写法1:class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 设置一个虚拟头节点
// 这个虚拟头节点有助于处理删除头节点的情况
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
// 初始化当前节点和前一个节点指针
// current指向实际的头节点,prev指向虚拟头节点
ListNode* current = head;
ListNode* prev = dummyHead;
// 遍历链表
while (current != nullptr) {
// 如果当前节点的值等于给定值val
if (current->val == val) {
// 跳过当前节点,即将前一个节点的next指针指向当前节点的下一个节点
prev->next = current->next;
} else {
// 如果当前节点的值不等于val,则前一个节点移动到当前节点
prev = current;
}
// 当前节点向前移动
current = current->next;
}
// 新的头节点是虚拟头节点的下一个节点
ListNode* newHead = dummyHead->next;
// 释放虚拟头节点的内存
delete dummyHead;
return newHead;
}
};
写法2:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 设置一个虚拟头结点,以处理删除头节点的情况
ListNode* dummyHead = new ListNode(0);
// 将虚拟头结点指向head,这样方便后面做删除操作
dummyHead->next = head;
// 初始化当前节点指针,指向虚拟头节点
ListNode* cur = dummyHead;
// 遍历链表,直到最后一个节点
while (cur->next != NULL) {
// 如果下一个节点的值等于给定值val
if (cur->next->val == val) {
// 保存需要删除的节点
ListNode* tmp = cur->next;
// 将当前节点的next指向需要删除节点的下一个节点,跳过需要删除的节点
cur->next = cur->next->next;
// 释放被删除节点的内存
delete tmp;
} else {
// 如果下一个节点的值不等于val,当前节点指针向前移动
cur = cur->next;
}
}
// 更新head指针,指向新的头节点
head = dummyHead->next;
// 释放虚拟头结点的内存
delete dummyHead;
// 返回新的头节点
return head;
}
};
通过使用虚拟头节点 (dummyHead
) 和当前节点 (cur
) 来维护和操作链表,使得代码在处理删除节点时更加简洁和统一。这里的关键点是通过 cur
指针的移动和判断,来确保正确地删除节点并维护链表的完整性。