题目描述:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
首先节点的结构体
/**
* 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) {}
* };
*/
创建节点的时候可以直接ListNode* node=new ListNode(0, head);
意思是node这个节点值是0,指针域里的指针指向的下一个节点是head;
这个题要考虑头节点的值等于目标值时也是需要被删除的。
但是需要注意在链表里面做删除节点的操作时,这个节点必须有前驱。所以需要对头节点做特殊处理。
方法一:
单独判断头节点的值是否需要删除
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//必须判断head是否为空,为空的话直接返回head,不再进入循环
while(head!=NULL&&head->val==val)
{
ListNode* temp=head;
head=head->next;
delete temp;
}
ListNode* cur=head;
while(cur!=NULL&&cur->next!=NULL)
{
if(cur->next->val==val)
{
cur->next=cur->next->next;
}
cur=cur->next;
}
return head;
}
};
方法二:
设置虚拟节点,一开始这块我一直不太理解。现在按照自己的想法将链表这块说一下,只是这种说话方便我自己记忆,不一定正确。
无论链表是否为空,头指针都不为空,头指针是链表的必要元素。
一个链表相当于一串珠子,当我拆除某个珠子的时候我需要用到一个工具将这个珠子拿走,并将这个柱子原来两侧的珠子用线穿起来,这个工具相当于一个临时指针或者临时节点,它在不停的干活(去掉珠子),所以它的位置不停变化,由它来引起链表的变化。
前面已经说了,删除链表节点必须有前驱,所以在原链表的基础上再加一个节点,这个节点指向的下一个节点是原来的头节点,那么这个时候我就可以搬出我的工具来对我的链表珠子随意删除。相当于链表是链表,工具是游离在链表外的一个东西。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyhead=new ListNode(0, head);
ListNode* cur=dummyhead;
while(cur->next!=NULL)
{
if(cur->next->val==val)
{
ListNode* temp =cur->next;
cur->next=cur->next->next;
delete temp;
}
else
{
cur=cur->next;
}
}
return dummyhead->next;
}
};
妈呀,这是我自己的故事,我之所以这么来“说服”我自己,是因为我一开始认为dummyhead 和 cur这两个指针在最初指向同一个空间,即head,那么当head->val==val时,这时我们将cur指针指向的空间地址变到了head->next;
但是原来的指针dummyhead指向的地址还是一开始的head啊,没有变化,这是我原来一直不能理解的地方,同样现在我也觉得这种想法好像没错。。。
所以只能用上面的故事来说服自己
在b站上看到了这个视频,还有下面的评论,我觉得对理解链表有很大的用处
【C++链表】这可能是你能在网上找到最详细的C++链表实例讲解。_哔哩哔哩_bilibili
这里再说一下我自己的理解
创建链表的节点的时候跟创建指针的语法差不多ListNode* dummyhead=new ListNode(0)
节点本身就是一种数据结构,而我们最常用的int,一般说成数据类型,但是我觉得他俩本质上是一样的,都是用来定义数据,只不过int更简单一些而已
所以对于节点的数据类型,本身他的数据struct里面就包括了两个(val,next),所以对于node->next改变时,跟我们常说的
“当两个指针在最初指向同一个空间地址时,对其中一个指针指向的数值变化时,另一个指针指向的数值也跟着变化”是同一个意思
所以
ListNode* dummyhead=new ListNode(0, head);
ListNode* cur=dummyhead;
这两句代表dummyhead,cur指向同一个节点,那当cur->next 做改变时,dummyhead->next也跟着改变
但是当cur=cur->next;,这时的cur节点就跟一开始的 ListNode* cur=dummyhead;不是同一个节点了,这时候再对cur做改变,dummyhead也不会跟着变了。