1.虚拟头结点的应用
虚拟头结点在处理head节点的操作时经常用到,如果不用虚拟头结点,可能得分情况讨论,如删除操作,我们知道在删除一个节点的时候是需要用前一个节点来操作,使得前一个节点的next=next->next,而在处理head时候,由于没有之前的节点,所以创作一个虚拟头结点dummy就非常要必要而且省去了分类讨论,具体代码模拟如下
ListNode* del(ListNode *head, int x) {
ListNode dummy;
dummy.next = head;
ListNode* prev = &dummy;
ListNode* curr = head;
while (curr != NULL) {
if (curr->val == x) {
prev->next = curr->next;
delete curr; // 使用 delete 代替 free
} else {
prev = curr;
}
curr = prev->next;
}
return dummy.next; // 返回新的头节点
}
2.如果题目是删除倒数第n个节点,如果恰好是头结点,那么也必用dummy
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode *dummy=malloc(sizeof(struct ListNode));
struct ListNode*first=dummy;
struct ListNode*second=dummy;
dummy->next=head;
n++;
while(n--&&first!=NULL)
{
first=first->next;
}
while(first!=NULL)
{
first=first->next;
second=second->next;
}
struct ListNode*op=second->next;
second->next=second->next->next;
free(op);
return dummy->next;
}
这是给dummy分配动态内存的写法,dummy本来就是指针类型所以给*first赋值合情合理,而下边的法二就不一样了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
struct ListNode dummy;
struct ListNode*fast=&dummy;
struct ListNode*slow=&dummy;
dummy.next=head;//是个对象而不是指针不能用dummt->next
n++;
while(n--&&fast!=NULL)
{
fast=fast->next;
}
while(fast!=NULL)
{
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return dummy.next;//不return head是为了防止删除首节点,
}
};
如图,dummy就是这个ListNode类型的变量,不是指针类型,所以给*first赋值的时候需要对
dummy取地址即&dummy,因为dummy不是指针变量,所以访问下一个时候用dummy.next而不是dummt->next,如果dummy被定义的时候是指针类型(如第一个代码)那么使用dummy->next而不是dummy.next