剑指offer-面试题18:删除链表的节点

题目描述

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 返回删除后的链表的头节点。
注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的节点,调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的节点,调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:题目保证链表中节点的值互不相同

方法一(递归)

1.解题思路

当链表中没有节点,或者当前节点的值刚好为给定val时,递归终止。递归过程只需每一层往后传递即可,每一层返回当前的head。

2.代码实现
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head==null) return head;
        if(head.val==val) return head.next;
        head.next=deleteNode(head.next,val);
        return head;
    }
}
3.复杂度分析
  • 时间复杂度:最坏情况下需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:递归栈的深度为链表的长度,所以空间复杂度为O(n)

方法二(哑节点+双指针)

1.解题思路

用一个虚拟的哑节点接在链表前面,然后定义一个pre指针跟踪当前节点的前驱,定义一个cur指针指向当前节点,如果当前节点为val,则将pre指向cur的后继,终止循环。

2.代码实现
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode pre=dummy;
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==val){
                pre.next=pre.next.next;
                break;
            }
            pre=cur;
            cur=cur.next;
            
        }
        return dummy.next;
    }
}
3.复杂度分析
  • 时间复杂度:最坏情况下需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:pre,cur,dummy占用常数大小的内存空间,所以空间复杂度为O(1)

方法三(单指针)

1.解题思路

先判断特殊情况,当链表为空时,直接返回head;当head的值为val时,直接返回head的后继作为新的头节点。用一个cur指针跟踪当前节点,找到值为val节点的前驱,然后将这个前驱和当前节点的后继接在一起。

2.代码实现
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head==null) return head;
        if(head.val==val) return head.next;
        ListNode cur=head;
        while(cur.next!=null&&cur.next.val!=val){
        	cur=cur.next;
        }
        if(cur.next!=null){
        	cur.next=cur.next.next;
        }
        return head;
    }
}
3.复杂度分析
  • 时间复杂度:最坏情况下需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:cur占用常数大小的内存空间,所以空间复杂度为O(1)

扩展(删除排序链表中的重复元素I)

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。 返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,1,2]
输出:[1,2]
示例 2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序排列

方法一(递归)

1.解题思路

当前节点为空或只有一个节点时,递归终止。如果当前节点值与其后继值不相等,则直接进入下一层递归;如果相等,则走到最后一个相等的节点,返回当前层。

2.代码实现
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null||head.next==null) return head;
        if(head.val!=head.next.val){
            head.next=deleteDuplicates(head.next);
        }
        else{
            while(head.next!=null&&head.val==head.next.val){
                head.next=head.next.next;
            }
            return deleteDuplicates(head);
        }
        return head;
    }
}
3.复杂度分析
  • 时间复杂度:需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:递归栈的深度为链表的长度,所以空间复杂度为O(n)

方法二(单指针)

1.解题思路

定义一个cur指针跟踪当前节点,如果重复,则走到最后一个重复节点,否则,直接往后遍历。

2.代码实现
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode cur=head;
        while(cur!=null&&cur.next!=null){
            while(cur.next!=null&&cur.val==cur.next.val){
                cur.next=cur.next.next;
            }
            cur=cur.next;
        }
        return head;
    }
}
3.复杂度分析
  • 时间复杂度:需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:不需要额外的内存开销,所以空间复杂度为O(1)

扩展(删除排序链表中的重复元素II)

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序排列

方法一(递归)

1.解题思路

与“删除排序链表中的重复元素I”思路相似,只是在处理当前节点与后继节点相等时,走到最后一个相等节点,然后返回当前层的下一层。

2.代码实现
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null||head.next==null) return head;
        if(head.val!=head.next.val){
            head.next=deleteDuplicates(head.next);
        }
        else{
            while(head.next!=null&&head.val==head.next.val){
                head.next=head.next.next;
            }
            return deleteDuplicates(head.next);
        }
        return head;
    }
}
3.复杂度分析
  • 时间复杂度:需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:递归栈的深度为链表的长度,所以空间复杂度为O(n)

方法二(哑节点+双指针)

1.解题思路

用一个虚拟的哑节点接在链表前面,然后定义一个pre指针跟踪当前节点的前驱,定义一个cur指针指向当前节点。如果当前节点等于其后继,先走到最后一个相等节点(这个过程前驱保持不变),然后将前驱的next指向当前节点的后继;如果当前节点不等于其后继,直接往后遍历即可。最后返回哑节点的next作为新的头节点。

2.代码实现
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null||head.next==null) return head;
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode pre=dummy;
        ListNode cur=head;
        while(cur!=null&&cur.next!=null){
            if(cur.val==cur.next.val){
                while(cur.next!=null&&cur.val==cur.next.val){
                    cur=cur.next;
                }
                pre.next=cur.next;
            }
            else{
                pre=cur;
            }
            cur=cur.next;

        }
        return dummy.next;
    }
}
3.复杂度分析
  • 时间复杂度:需要遍历所有节点,时间复杂度为O(n)
  • 空间复杂度:不需要额外的内存空间,所以空间复杂度为O(1)

剑指offer全集入口: 请戳这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值