前言
“代码随想录”刷题记录。总结笔记均会放在“算法刷题-代码随想录”该专栏下,以下为原文的链接。
代码随想录此题链接
题目
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
提示:
列表中的节点数目在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50
1. 移除链表
思路
分两种,一种是含有虚拟头结点,一种不含有虚拟头节点,因为含有虚拟头节点的处理更加通用,故只介绍含有虚拟头节点的方式(卡哥对这两种都有详细解释)。
需要的变量:
- 定义链表结构的结构体struct
- 虚拟头节点 dummyHead指针
- 当前节点 cur指针
- 指向删除节点的temp指针
2. 本题思路分析:
- 每次通过当前节点去检查下一个节点的值是否符合移除的条件(即cur->next->val是否等于规定值val),而不是去检查当前节点是否满足移除条件(因为要通过上一个节点,移除下一个节点(上一个节点next指向下下一个节点,相当于从链表中移除了下一个节点,不过还要在内存中删除节点所占用的空间))。
- 若不满足条件,则cur指针后移一位(cur=cur->next)
- 跳出循环后,head头指针需要指向虚拟头指针的next指针,如果head头指针指向的节点是需要删除的,那么在节点被删除后,head头指针指向的就是释放后的节点(head节点变成了未定义的指针(虽然还指在原始的空间地址上,但是该地址已经被释放不具备存储功能,变成了垃圾空间,指向这样垃圾地址的指针不安全,这样的指针被称为野指针)),在力扣上返回被释放的指针,会报heap-use-after-free这样的错,并且最后也需要删除虚拟头节点,避免占用多余内存空间。
3. 算法实现
- 代码:
#include<stdio.h>
#include<vector>
using namespace std;
struct ListNode{
int val;
ListNode* next;
ListNode* () : val(0),next(NULL){}
ListNode* (int x):val(x),next(NULL){}
ListNode* (int x,ListNode* next):val(x),next(next){}
}
ListNode* removeElements(ListNode* head, int val) {
//虚拟头节点
ListNode* dummyHead = new ListNode(0);
dummyHead -> next = head;
ListNode* cur = dummyHead;
ListNode* temp = new ListNode(0);
while(cur != NULL && cur -> next != NULL){
//判断此节点不为最后一个节点(最后一个节点是虚拟头节点),并且不为空节点
if(cur -> next -> val == val){
//删除next指针的节点
temp = cur -> next;
cur -> next = cur -> next -> next;
delete temp;
}else{
//cur指针后移一位
cur = cur -> next;
}
}
//此时所有该被删除的节点已经全部被删除
head = dummyHead -> next;
delete dummyHead;
return head;
}
4. 算法分析
n为数组长度
时间复杂度:O(n)
空间复杂度:O(1)
5. 算法坑点
- 1.避免产生野指针:设置虚拟节点的解法中,在删除了所有应该删除的节点后,记得要把head头结点赋值为dummyHead虚拟节点的后一个节点,并且删除dummyHead虚拟头节点。(因为head有可能会被释放,指向了一个释放节点,成为一个野指针)