剑指Offer题目13:在O(1)时间删除链表结点(Java)

面试题13:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。node

Basic

只要涉及到链表的增、删操做,就须要考虑如下三种场景:面试

场景1:(特殊场景)只有一个头结点,对头结点进行操做

场景2:(普通场景)有多个结点,操做中间节点

场景3:(特殊场景)对尾节点进行操做
复制代码

解题思路

首先考虑普通场景,再考虑特殊场景。bash

普通场景:

因为要求的时间复杂度为O(1),若是采用常规思惟,从头结点遍历到目标节点,则时间复杂度为O(n),没法知足要求。

删除某节点,意味着将某个目标值 date 对应的节点从链表中抹去,能够是直接删除,也能够是用新的值来覆盖旧的值,而后用
新的后序节点覆盖旧的后序节点。

若是采用直接删除的方式,须要从头结点遍历起,时间复杂度为 O(n), 没法知足 O(1) 的要求。

所以须要采起覆盖抹去目标节点的方式来达到删除节点的目的。

解决方案:

Step 1: 将目标节点的 next 节点的值赋给目标节点

Step 2:将目标节点的 next 的后序节点赋给目标节点的 next

此时,目标节点被其后序节点 next 彻底覆盖,同时目标节点原来的后序节点 next 没指向它,即被删掉。

实现删除目标节点的目的,时间复杂度为 O(1)。

特殊场景:

1. 删除头节点

直接删除头结点,时间复杂度 O(1)。

2. 删除尾节点

须要从头结点开始遍历到尾部,对于 n-1 个中间节点,时间复杂度为 O(n)。

总的平均时间复杂度:

[(n-1) * O(1) + O(n)] / n 

结果仍是 O(1)

复制代码

代码实现

public static void deleteNodeInO1(Node head, Node target){
    if (head == null || target == null) {
        return;
    }
    //若是删除的是头结点,无论当前链表是否只有一个头结点,都直接赋 head.next,只有一个头结点时,
    //head.next 为 null
    if (head == target) {
        head = head.next;
    } else if (target.next != null) { //普通场景:覆盖抹除法
        Node nextNode = target.next;
        target.data = nextNode.data;
        target.next = nextNode.next;
    } else { //删除尾节点,只能从头遍历到导数第二个节点,再删除最后节点
        Node node = head;
        //注意,此处只能遍历到尾节点以前的一个节点,即倒数第二个节点,由于删除尾节点须要其前一个节点做为操做入口
        while (node.next != target) {
            node = node.next;
        }
        node.next = null; //此处 node.next 就是最后一个节点,将最后一个节点置空,就至关于删除最后节点
    }
}
复制代码

总结

删除节点也可使用 覆盖抹除法(自创词汇方便记忆),时间复杂度为 O(1)函数

相关文章
相关标签/搜索