【链表专题】剑指 Offer 18. 删除链表的节点
本问题对应课程链接:剑指offer刷题第二期
要求:
1、请打卡你的解题代码,如果能简单说一下思路则简单说一下思路,请代码不要直接抄袭复制我题解区给的代码哦。
2、给出你代码所用的时间复杂度和空间复杂度
第一次打卡主要:打卡的时候,代码支持 markdown 格式,并且必须要指定语言,比如

本问题对应课程链接:剑指offer刷题第二期
要求:
1、请打卡你的解题代码,如果能简单说一下思路则简单说一下思路,请代码不要直接抄袭复制我题解区给的代码哦。
2、给出你代码所用的时间复杂度和空间复杂度
第一次打卡主要:打卡的时候,代码支持 markdown 格式,并且必须要指定语言,比如

共以下 100 个回答
思路
添加一个数据为空的头节点,这样原来的头节点删除就不用做特殊判断
– 时间复杂度O(N)
– 空间复杂度O(1)
代码
static class Solution {
public ListNode deleteNode(ListNode head, int val) {
return delNode(null,head,head,val);
}
时间O(n),空间O(1)
下面是代码
链表删除 复杂度O(1)
两步走
第一步:定位节点
while遍历,直到head.valval时弹出
第二步:修改引用
当找到目标节点时,pre.next=cur.next
特例
当应删除头节点head时,直接返回head.next即可,这一步可以大大提升效率
链表首先要找到前驱节点和后继节点
创新思维:
当我们要删除一个节点时,并不一定要删除这个节点本身,可以先把下一个节点的内容复制出来覆盖被删除的节点内容,然后把下一个节点删除即可。
代码如下
代码如下
时间o(n)空间(1)
下面是代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode deleteNode(ListNode* head, int val) {
ListNode *p = head->next;
if(head NULL)
return NULL;
if(head->val val)
return head->next;
};
跨考仔努力中
//测试Demo1
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
dummy = ListNode(next = head)
cur = dummy
while cur.next:
if cur.next.val val:
if not cur.next.next!=None:
cur.next = None
else:
cur.next = cur.next.next
cur = cur.next
else:
cur = cur.next
return dummy.next
时间o(n),空间o(1)
删除空结点思路:
找到结点位置,将结点前一个结点的地址部分指向下一个结点即可完成删除
注意头结点判断与尾结点为空。
复杂度:
时间复杂度为O(n),空间复杂度O(1)
时间复杂度O(n)
空间复杂度O(1)
class Solution {
public ListNode deleteNode(ListNode head, int val) {
//定义一个虚拟节点
ListNode res=head;
//判断头节点
if(head.val val) return head.next;
//判断后面的节点
while(head!=null){
if(head.next.val val){
head.next=head.next.next;
return res;
}
head=head.next;
}
return res;
}
}
“`打卡C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur->next != NULL){
if(cur->next->val == val){
};
“`
时间复杂度 n
空间复杂度 1
“`c++
class Solution {
public:
};
时间复杂度:O(n)
空间复杂度:O(1)
“`java
//leetcode submit region begin(Prohibit modification and deletion)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode temp = head;
while (temp.next != null) {
ListNode aha = temp;
temp = temp.next;
if(temp.val val){
aha.next = temp.next;
}else if(head.val val){
head = head.next;
}
}
return head;
}
}
//leetcode submit region end(Prohibit modification and deletion)
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:41.3 MB,击败了5.94% 的Java用户
双指针
1. 时间复杂度:O(n)
2. 空间复杂度:O(1)
3. 代码
使用的是单指针
(1)当要删除的节点为头节点head时,返回头节点的下一节点head.next;
(2)初始化cur;
(3)循环遍历,直到找到要删除的节点则结束;
(4)删除节点:cur的next指向待删除节点的下一节点(即:cur的下下一节点)cur.next.next;
(5)返回头节点head。
时间复杂度:O(n)
空间复杂度:O(1)
时间复杂度 O(N)
空间复杂度O(1)
首先要找到删除的这个元素
删除一个数之前要找到他的前一个结点,然后把他的next直接指向next.next
时间复杂度:O(n)
这道题同样是运用双指针
时间复杂度θ(n),空间复杂度θ(1)
1,找到匹配的值,将前一个节点的next指向下一个节点
2,时间复杂度O(n)
空间复杂度O(n)
时间复杂度:O(n)
空间复杂度:O(1)
思路:双指针,指向前一个节点,以及当前节点,注意判断边界值,以及初始化值的处理。
解法:迭代
思路:删除链表的节点其实就是找到要删除节点的位置,让上一个节点的next指针指向next.next指针即下下个节点
特例:删除的节点是头节点or尾节点
时间复杂度:O(n),n为链表长度,while循环的删除操作为n/2次,最差为n次
空间复杂度:O(1)
时间复杂度O(n)
时间复杂度:O(n)
空间复杂度:O(1)
不太会算时间空间复杂度,先去学一下~
比较笨拙的方法,先用一个指针保存一下head->next 以便判断是否删除的是头节点。用虚拟节点遍历链表并解链要删除的节点。
时间复杂度:O(n)
空间复杂度:O(1)
时间复杂度O(n)
空间复杂度O(1)
方法:双指针
复杂度分析:
● 时间复杂度: O(n)
● 空间复杂度: O(1)
注意:当头节点是所需要删除的节点的特殊情况
思路:找到要删除节点target的前一个节点pre,让pre的指针域指向target的下一个节点,再把target的指针域设为空,让节点target从链表中脱离出来,方便Java虚拟机回收,对于OJ来说,程序指向完后会自动销毁内存,所以让target的指针域指向空这一步可以不做。因为头节点没有前一个节点,所以需要特殊处理,可以使用虚拟头节点让头节点也有前一个节点,也可以使用递归实现,实现的思路都是一样的。
leetcode中测试用例中好像只有一个要删除的节点,我给出两个版本,一个是删除第一次出现的删除节点,另一个是删除所有要删除的节点,作为递归练习。
递归实现的时间和空间复杂度都为O(n)
思路:
step 1:首先我们加入一个虚拟头部节点(dummyHead),并让这个虚拟头节点指向链表的头节点,这样做是因为有可能要删除的元素是链表的头节点。
step 2:准备两个指针遍历链表,一个指针(cur)指向当前要遍历的元素,另一个指针(pre)指向该元素的前序节点,便于获取它的指针。
step 3:遍历链表,找到目标节点,则断开连接,指向后一个。
step 4:返回虚拟头节点的下一个节点(即是删除原素后链表的头节点)。
涉及结点的增删注意头结点 -> 添加虚拟头结点进行统一处理:
解题思路
1.在链表中删除一个结点需要它和它之前的结点,可以设置一个指针或两个指针
2.设置两个指针,一个sce指向删除节点的自身,另一个指向被删除节点之前的结点
3.利用pre->next = sce->next;删除结点
一定记得判断被删除结点是否为头结点和判断链表是否为空
时间复杂度O(n)
空间复杂度O(1)
时间复杂度O(N)
空间复杂度O(1)
时间复杂度O(N)
空间复杂度O(1)
class Solution {
public ListNode deleteNode(ListNode head, int val) {
//判断一下如果头部为查找节点返回其下个节点的地址
if(head.valval){
return head.next;
}
//创建双指针一个指向头部一个指向下一个地址即初始化双指针
ListNode pre = head;
ListNode cur = head.next;
//遍历查找直到找到该节点时跳出
while(cur.val!=val&&cur.next!=null){
pre=cur;
cur=cur.next;
}
//实现删除操作
if(cur!=null){
pre.next=cur.next;
}
return head;
}
解题思路:
1.双指针。初始化双指针
2.需要对下一个节点判断,是否为空
时间复杂度O(n);长度为
空间复杂度O(1)
先判断特殊情况,然后单指针遍历,让val所在节点的上一个节点指向val的next节点
时间复杂度:O(n)
空间复杂度:O(1)
1、先考虑特殊情况,将头节点为空和头节点为要删除节点的情况做一个返回
2、定义双指针(pre cur)分别指向前两个节点,当第二个指针不为NULL时,就每次向后移动一步
3、每次判断cur的值是否等于val,如果等于就将前一个节点的指向本节点的后一个节点。如果不等于val,那么双指针同时后移一步
<
pre>
class Solution {public:
ListNode* deleteNode(ListNode* head, int val) {
if(head NULL) return NULL;
if(head->val val) return head->next;
<
pre>
ListNode* cur = head->next;ListNode* pre = head;
while(cur != NULL){
if(cur->val val){
pre->next = cur->next;
return head;
}
cur = cur->next;
pre = pre->next;
}
return head;
}
};
先考虑特殊情况:空链表、头指针的值即为删除值
排除特殊后定义当前节点以及当前节点前一个节点,遍历链表,删除
双指针
单指针遍历
时间复杂度O(1) 空间复杂度O(1)
双指针
递归
虚拟头节点
时间复杂度:O(N)
空间复杂度:O(1)
解法二:递归
时间复杂度:O(N)
空间复杂度:O(N)
解法三:虚拟头节点
解法一和解法二删除头节点和删除非头节点 代码相似。
定义一个虚拟头节点,避免将头节点单独逻辑判断,返回虚拟节点的下一节点
时间复杂度:O(N)
空间复杂度:O(1)
直接遍历
代码
时间复杂度:O(n)
空间复杂度:O(1)
时间O(n), 空间O(1)
时间复杂度 O(n)
复杂度O(1)怎么算的,,应该是O(n)哦
思路:遍历列表,找出val,删除。由于需要知道前序节点,所以需要哑节点指向头结点遍历
下面是代码
用虚拟头结点
先后两个节点从头节点开始遍历,前面的节点为目标节点的话,后节点的next为前节点的next。
“`java
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head.val == val) {
return head.next;
}
//定义指针的前后点
ListNode pre = head, cur = head.next;
//注意别用节点去和val 比较 这里错了 好久
while(cur.val != val){
pre = cur;
cur = cur.next;
}
//相等的时候 跳过当前节点
if(cur.val ==val){
pre.next = cur.next;
}
return head;
}
}
下面是代码
第一天第一题
三个代码格式的符号,不是三点哦,是三个反向顿号
额
这里就是使用了一个虚拟头结点,来简化出现上来就要删除的这种情况!
初级,熟悉语言
好久没写有些忘记了
递归
简单
先判断头节点
你的代码有问题,需要用
“` 围起来,你少了结尾哦
前置一个自定义节点,双指针共同移动即可!
对开头就是要删除的数据特判
/*
* 删除结点时,指针需指向待删除结点的上一个结点。
* 因为可能恰好删除的是头结点,所以可以创建一个虚拟节点放在头结点前面,成为虚拟头结点。
*/
你的代码没有高亮解析,应该是格式出了问题哦
对的,我又重新修改了下233
删除单链表节点需要获得其前一个节点,用虚拟头结点简化逻辑,题干说明中有链表中节点值不相同的限制,不用判断是否为空链表
自己写的没有考虑到head节点为空的情况,也没有将指针置零
第一次提交while 忘记写break跳出了…
注意判空和目标值为头结点的情况
第一次提交忘记写pre = pre.next;导致超出时间限制。
删除链表的结点,直接将结点指向下一结点,注意链表尾部判空。