剑指Offer对答如流系列 - 在O时间删除链表结点

Posted jefferychenxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer对答如流系列 - 在O时间删除链表结点相关的知识,希望对你有一定的参考价值。

面试题17:在O(1)时间删除链表结点

问题描述

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

链表结构

  public class Node{
        int val;
        Node next;
        public Node(int value,Node next) {
            val=value;
            next=next;
        }
    }

问题分析

曾经未碰到这道题之前,删除链表的节点,用的方法非常原始、基础(代码如下),很明显这种原始的方式带来的时间复杂度为O(n)

    //从链表中删除index(0~based)位置的元素,返回删除的元素
    public E remove(int index) {
        if(index<0 || index >=size) {
            throw new IllegalArgumentException("remove failed. Illegal index");
        }

        Node prev = dummyHead;
        for(int i=0;i<index;i++) {
            prev = prev.next;
        }

        Node retNode = prev.next;
        prev.next = retNode.next;
        retNode.next = null;
        size--;
        return retNode.e;
    }

过去刚遇到这道题,半天也没想起来,看到解析后豁然开朗。说明见多识广还是很重要的。

技术图片

要在O(1)时间删除某结点,可以这样实现:设待删除结点j,j的上一个节点为i,把j的值复制到i,再把i的指针指向j的下一个结点,最后删除j,效果就相当于删除j。
技术图片

不过注意特殊情况:

(1)当待删除结点i为尾结点时,无下一个结点,则只能从头到尾顺序遍历;

(2)当链表中只有一个结点时(即是头结点,又是尾结点),必须把头结点也设置为null。

问题解答

代码如下:

    public class Node{
        int val;
        Node next;
        public Node(int value, Node next) {
            val=value;
            next=next;
        }
    }

    /**
     * 返回值:头结点
     * 返回值不可以为void,否则头结点无法删除
     * 即:函数中虽然令head=null,但返回到主程序后,
     * head还是不变,所以令该函数返回值为Node
     */
    public Node deleteNode(Node head, Node pToBeDeleted) {
        
        if(head==null||pToBeDeleted==null)
            return head;
        
        if(pToBeDeleted.next != null) {  //待删除结点不是尾结点
            Node nextNode = pToBeDeleted.next;
            pToBeDeleted.val=nextNode.val;
            pToBeDeleted.next=nextNode.next;
            nextNode=null;
        }else if(head == pToBeDeleted) {    //只有一个结点(即是尾结点,又是头结点)
            pToBeDeleted=null;
            head=null;
        }else { //链表含多个结点,删除尾结点
            Node preNode=head;
            while(preNode.next!=pToBeDeleted && preNode!=null) {
                preNode=preNode.next;
            }
            if(preNode==null) {
                System.out.println("无法找到待删除结点!");
                return head;
            }
            preNode.next=null;
            pToBeDeleted=null;
        }
        return head;
    }

做完这道题,你可能觉得为啥这种方法没有普及开来?其实这种方法有一个致命的缺陷:假设了待删除的结点的确在表中。

以上是关于剑指Offer对答如流系列 - 在O时间删除链表结点的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer对答如流系列 - 复杂链表的复制

剑指Offer对答如流系列 - 链表中环的入口节点

剑指Offer对答如流系列 - 圆圈中最后剩下的数字

剑指Offer对答如流系列 - 合并两个排序的链表

剑指Offer对答如流系列 - 链表中倒数第k个结点

剑指Offer对答如流系列 - 数组中数字出现的次数