介绍
在编程中,处理链表是一个基本的技能,本位主要介绍如何删除特定的节点。这是一个非常常见的操作。
问题陈述
给定一个单链表,我们需要实现一个函数,该函数接受一个值作为输入,并在链表中删除具有该值的所有节点。最终,我们将返回修改后的链表。
解决方案
我们将通过遍历链表来找到并删除目标值的节点。具体来说,我们将保持两个指针:一个用于当前节点,另一个用于追踪前一个节点。当我们找到目标值时,我们将通过修改前一个节点的next指针来删除当前节点。
算法
- 初始化一个指向头节点的指针current和一个指向前一个节点的指针prev,并将它们都指向头节点。
- 遍历链表,直到当前节点为空。
- 如果当前节点的值等于目标值,则:
- 将前一个节点的next指针指向当前节点的下一个节点。
- 释放当前节点的内存。
- 将当前节点指针移动到下一个节点。
- 否则,更新prev指针为当前节点,将当前节点指针移动到下一个节点。
- 返回修改后的链表。
代码实现
#include <bits/stdc++.h>
using namespace std;
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
class LinkedList {
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
void append(int val) {
if (!head) {
head = new Node(val);
return;
}
Node* temp = head;
while (temp->next) {
temp = temp->next;
}
temp->next = new Node(val);
}
void deleteNode(int key) {
Node* current = head;
Node* prev = nullptr;
while (current) {
if (current->data == key) {
if (prev)
prev->next = current->next;
else
head = current->next;
Node* temp = current;
current = current->next;
delete temp;
} else {
prev = current;
current = current->next;
}
}
}
void display() {
Node* temp = head;
while (temp) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
};
int main() {
LinkedList linkedList;
linkedList.append(1);
linkedList.append(2);
linkedList.append(3);
linkedList.append(4);
cout << "原始链表:" << endl;
linkedList.display();
linkedList.deleteNode(3);
cout << "删除节点后的链表:" << endl;
linkedList.display();
return 0;
}
示例与测试
int main() {
LinkedList linkedList;
linkedList.append(1);
linkedList.append(2);
linkedList.append(3);
linkedList.append(4);
cout << "原始链表:" << std::endl;
linkedList.display();
linkedList.deleteNode(3);
cout << "删除节点后的链表:" << std::endl;
linkedList.display();
return 0;
}
注意点**
deleteNode()函数中有这么一串负责删除的代码:
if (current->data == key) {
if (prev)
prev->next = current->next;
else
head = current->next;
Node* temp = current;
current = current->next;
delete temp;
}
删除之前为什么要进行 i f ( p r e v ) if(prev) if(prev)的判定呢,这是因为在删除单链表中的节点时,我们需要考虑两种情况:
- 如果要删除的节点是头节点。
- 如果要删除的节点不是头节点。
当要删除的节点是头节点时,我们需要更新链表的头指针,使其指向被删除节点的下一个节点。而如果要删除的节点不是头节点,我们需要将前一个节点的next指针指向被删除节点的下一个节点,以确保链表仍然保持连续性。
因此,在代码中的if (prev)判定的目的是检查是否存在前一个节点。如果前一个节点存在,我们可以安全地更新前一个节点的next指针,以删除当前节点。如果前一个节点不存在(即当前节点是头节点),我们需要特别处理,将链表的头指针指向当前节点的下一个节点。
其他写法对比
版本一:
void Delete LinkList(LNode * L,int key)
{
//删除以L为头结点的单链表中值为key的第一个节点
LNode *p=L,*q=L->next;
while(q!=NULL&&q->data!=key){p=q;q=q->next;}
if(q->data==key){p->next=q->next;free();}
else printf("NULL\n");
}
版本二:
while(p->next!=NULL&&p->next->data!=key){p=p->next;}
if(p->next->data==key)
{
q=p->next;
p->next=q->next;
free();
}
这两个版本的区别是版本一是判断当前节点是否为空以及是否为目标值,而版本二则是针对下一个节点进行判断,版本一的好处就是能够保证最后停留的节点不是NULL,然后在进行是否是目标节点的判断,但是版本二无法保证最后停止的节点是否为NULL,所以上面的写法是错误的,需要在if语句前面加上一个是否是NULL的判断,否则p==NULL的情况下进行p->next->data操作会出现bug。