剑指offer第二版面试题18:删除链表中的节点(java)

题目一描述:
给定单链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。

分析:
在单链表中删除一个节点,最常规的方法无疑是从链表的头结点开始,顺序遍历查找要删除的节点,并在链表中删除该节点。
比如图a所表示的链表中,我们要删除节点i,可以从链表的头节点a开始顺序遍历,发现节点h的m_PNext指向要删除的节点i,于是我们可疑把节点h的m_PNext指向i的下一个节点即为j。指针调整之后,我们就可以安全地删除节点i并保证链表没有断开。这种思路由于需要顺序查找,时间复杂度自然就是O(n)了。
这里写图片描述
之所以要从头开始查找,是因为我们需要得到被删除的节点的前面一个节点。在单链表中,节点中没有前一个节点的指针,所以只好从链表的头结点开始顺序查找。
那是不是一定要得到被删除的节点的前一个节点呢?答案是否定的。我们可以很方面地得到要删除的节点的下一个节点。如果我们把下一个节点的内容复制到要删除的节点上覆盖原有的内容,再把下一个节点删除,那是不是就相当于把当前要删除的节点删除了?
我们还是以前面的例子来分析这种思路,我们要删除的节点i,先把i的下一个节点j的内容复制到i,然后把i的指针指向节点j的下一个节点。此时再删除节点j,其效果刚好是把节点i给删除了(图c)
上述问题还有一个问题;如果要删除的节点位于链表的尾部,那么它就没有下一个节点,怎么办?我们让然从链表的头节点开始,顺序遍历得到该节点的前序节点,并完成删除操作。
最后需要注意的是,如果链表中只有一个节点,而我们又要 链表的头节点(也是尾节点),此时我们在删除节点之后,还需要把链表的头节点设置为NULL。

代码如下:

/**
 * 在O(1)时间内删除链表节点
 */
public class DeleteNodeOfO1 {

    /**
     * @param head 链表的头结点
     * @param toBeDeleted 待删除的节点
     */
    public void deleteNode(ListNode head, ListNode toBeDeleted){
        //参数校验
        if(head == null || toBeDeleted == null){
            return ;
        }

        //链表中只有一个节点,那么待删除的节点既是头结点,又是尾结点
        if(head == toBeDeleted && head.nextNode == null){
            head = null;
        }else{
            //待删除的节点是尾节点
            if(toBeDeleted.nextNode == null){
                ListNode temp = head;
                while(temp.nextNode != toBeDeleted){
                    temp = temp.nextNode;
                }
                temp.nextNode = null;
            }else{          //待删除的节点不是尾节点
                toBeDeleted.nodeValue = toBeDeleted.nextNode.nodeValue;
                toBeDeleted.nextNode = toBeDeleted.nextNode.nextNode;
            }
        }
    }

    /**
     * 打印链表
     * @param head 链表的头结点
     */
    public void printList(ListNode head){
        ListNode temp = head;
        while(temp != null){
            System.out.print(temp.nodeValue + "  ");
            temp = temp.nextNode;
        }
    }

    public static void main(String[] args) {
        DeleteNodeOfO1 test = new DeleteNodeOfO1();

        ListNode head = new ListNode();
        ListNode temp1 = new ListNode();
        ListNode temp2 = new ListNode();
        ListNode temp3 = new ListNode();

        head.nodeValue = 1;
        temp1.nodeValue = 2;
        temp2.nodeValue = 3;
        temp3.nodeValue = 4;
        head.nextNode = temp1;
        temp1.nextNode = temp2;
        temp2.nextNode = temp3;
        temp3.nextNode = null;

        System.out.println("删除前的链表:");
        test.printList(head);

        System.out.println("-----------------------");

        test.deleteNode(head, temp3);       //执行删除操作
        System.out.println("删除后的链表:");
        test.printList(head);

    }
}

class ListNode{
    int nodeValue;
    ListNode nextNode;
}

总的平均时间复杂度就是[(n-1)*1+O(n)]/n,结果还是O(1)。

题目二描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
例如:
输入链表:1->2->3->3->4->4->5
处理结果:1->2->5

分析:
1、新建一个头节点,以防止第一个节点被删除。
2、保存当前节点的前一个节点,循环遍历整个链表,如果当前节点的值与下一个节点的值相等,则将当前节点的值与next.next节点的值比较,直到不相等或者null为止,最后将当前节点的前一个节点pre指向最后比较不相等的节点。
3、如果当前节点与next节点不相等,则直接让节点指针全部向后移动一位。

代码如下:

/**
 * 删除链表中的重复节点
 */
public class DeleteNodeOfDuplication {

    public ListNode deleteDuplication(ListNode head) {
        if (head == null) {
            return null;
        }

        ListNode preNode = new ListNode(); // 前一个节点
        ListNode tmp = preNode;
        preNode.nextNode = head;
        ListNode nowNode = head; // 当前节点

        while(nowNode != null){
            //判断是否存在重复节点,是否应该删除
            if(nowNode.nextNode != null && nowNode.nodeValue == nowNode.nextNode.nodeValue){
                while(nowNode.nextNode != null && nowNode.nodeValue == nowNode.nextNode.nodeValue){
                    nowNode = nowNode.nextNode;
                }
                preNode.nextNode = nowNode.nextNode;        //删除相应的重复节点
            }else{      //preNode指针移动
                preNode = nowNode;
            }
            nowNode = nowNode.nextNode;
        }

        return tmp.nextNode;
    }

    public static void main(String[] args) {
        DeleteNodeOfDuplication test = new DeleteNodeOfDuplication();

        ListNode head = new ListNode();
        ListNode temp1 = new ListNode();
        ListNode temp2 = new ListNode();
        ListNode temp3 = new ListNode();
        ListNode temp4 = new ListNode();
        ListNode temp5 = new ListNode();
        ListNode temp6 = new ListNode();

        head.nodeValue = 1;
        temp1.nodeValue = 2;
        temp2.nodeValue = 3;
        temp3.nodeValue = 4;
        temp4.nodeValue = 5;
        temp5.nodeValue = 6;
        temp6.nodeValue = 7;
        head.nextNode = temp1;
        temp1.nextNode = temp2;
        temp2.nextNode = temp3;
        temp3.nextNode = temp4;
        temp4.nextNode = temp5;
        temp5.nextNode = temp6;
        temp6.nextNode = null;

        System.out.println("删除前的链表:");
        test.printList(head);

        System.out.println();
        System.out.println("-----------------------");

        ListNode resultList = test.deleteDuplication(head); // 执行删除操作
        System.out.println("删除后的链表:");
        test.printList(resultList);
    }

    /**
     * 打印链表
     * 
     * @param head
     *            链表的头结点
     */
    public void printList(ListNode head) {
        ListNode temp = head;
        while (temp != null) {
            System.out.print(temp.nodeValue + "  ");
            temp = temp.nextNode;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_37672169/article/details/80163042