链表10:链表中删除元素的8道题之一

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表10:链表中删除元素的8道题之一相关的知识,希望对你有一定的参考价值。

如果一道道刷题,你会发现算法题目毫无章法,但是如果将相关类型放在一起,你瞬间发现,这不就是在改改条件、不断造题吗?我们前面已经多次见证这个情况,今天再来看一个链表中删除元素的造题合集。如果在链表中删除元素搞清楚了,一下子就搞定了8道LeetCode题,是不是很爽?

1.题目

如果只看下面这些文字要求,眼睛会有点花、脑子会有点晕。没关系,我们将其分成了三组,用三期来会逐个拆解。这里你只要注意到这些题目要求差不多就行了。

第一组:

【1】LeetCode 237:删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为要被删除的节点 。

【2】LeetCode 203:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点 。

第二组:

【3】LeetCode 19. 删除链表的倒数第 N 个节点

【4】LeetCode 1474. 删除链表 M 个节点之后的 N 个节点。

第三组:

【5】LeetCode 83 存在一个按升序排列的链表,请你删除所有重复的元素,使每个元素只出现一次。

【6】LeetCode 82 存在一个按升序排列的链表,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中没有重复出现的数字。

【7】LeetCode 1836. 从未排序链表中删除重复元素。

下面这个题目比较特殊,我们在后面分析nSum问题的时候统一来看:

【8】LeetCode 1171 请你编写代码,反复删去链表中由总和值为 0 的连续节点组成的序列,直到不存在这样的序列为止。 

这一期我们先拆解第一组的问题。

在链表中删除元素有两种要求,一种是给定要删除的位置编号来删,一种是根据传入的值value,先找到值为value的位置,然后将其删除。对于后一种,题目可能要求找到一个删除就行了,也可能是将所有节点值为value的都删掉,LeetCode 237和203题貌似与后面两种情况很类似,但是仔细看发现还不太一样。我们一个个来分析。

1.单链表中如何删除节点

绝大部分情况 删除链表节点的操作套路都是固定的,这个我们在单链表的基本操作部分介绍过,这里再复习一下。

链表的删除主要是删除头部,删除尾部和中间位置三种情况。 

我们依次看一下。

(1)删除表头元素

如下图,最容易晕的是遍历游标cur从4到15之后,head一定要指向cur,也就是要有head=cur的操作。废话不多说,看着这个图想明白就行了。

(2)删除表尾元素

核心思想是找到尾节点时,需要将其前驱的next设置为null。如果只用一个遍历游标,可以判断遍历游标的cur.next.next是否为空,因为cur.next.next为null时,就说明cur.next是尾节点了,此时只要将cur.next设置为null就行了,如下图:
cur为7的时,cur.next是40,是尾节点,因为cur.next.next=null。此时只要设置7的next指针为null,节点40就脱离链表了。之后节点40会在某个时刻被jvm回收。

(3)删除中间元素

删除的元素在中间时,如下图,如果删除7。因为链表是单向的,此时必须提前知道节点15的地址,否则就无法将15连接到40上。

所以完整的删除代码是:

/**     * 删除节点     * @param head     链表头节点     * @param position 删除节点位置,取值从1开始     * @return 删除后的链表头节点     */    public static Node deleteNode(Node head, int position) {        if (head == null) {            return null;        }        int size = getListLength(head);        if (position > size || position <= 0) {            System.out.println("输入的参数有误");            return head;        }        if (position == 1) {            //curNode就是链表的新head            return head.next;        } else {            Node preNode = head;            int count = 1;            while (count < position) {                preNode = preNode.next;                count++;            }            Node curNode = preNode.next;            preNode.next = curNode.next;        }        return head;    }

如果自己手写这个方法,经常会出现没考虑首元素,没考虑尾元素等等情况,所以还是需要练习一下的。

2.一种罕见的元素删除方法

我们前面说LeetCode 237看似普通,但是又和平时做的题目不一样,具体怎么回事呢?我们看一下要求:

LeetCode 237:删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为要被删除的节点 。

示例1:

输入:head = [4,5,1,9], node = 5输出:[4,1,9]

解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例2:

输入:head = [4,5,1,9], node = 1输出:[4,5,9]解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

这里的意思是给你的node节点不是链表的头节点,而是直接要删除的节点。例如上面给的node=5,这个node是直接要删除的,而不是首节点,那就不用使用前面的方式先遍历找前驱,再删除了。

那该怎么删呢, 其实也不难,我们可以采用数组移动的思想,将node后面的元素值逐个覆盖其前面的元素就行了。

例如下面这个图:

我们要删除的node=3,那我们就用node.next的值4来覆盖3。然后后面的5覆盖4就可以了。这就是数组删除元素的套路嘛(又是套路)

所以,代码就可以这么写:

class Solution {    public void deleteNode(ListNode node) {         node.val = node.next.val;         node.next = node.next.next;    }}

你是不是觉得写错了,为啥只有两行?但是确实想清楚之后,两行就解决了。

3.LeetCode 203  移除链表中的目标元素

LeetCode203就是一个常规的删除节点的题了。

我们先看一下完整的题目要求:

LeetCode203:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点 。

示例1:

输入:head = [1,2,6,3,4,5,6], val = 6输出:[1,2,3,4,5]

一般情况下,我们删除节点cur时,必须知道其前驱pre节点和后继next节点,然后让pre.next=next就讲cur脱离开链表了。cur节点会在gc进行垃圾回收时回收掉。

对于删除来说,最麻烦的处理是如果删除的元素是首元素该怎么处理,因为其指针移动规律和后面的不一样,为了解决这个问题,我们可以先创建一个虚拟节点 dummyHead,使其指向head,也就是dummyHead.next=head,这样就不用单独处理首节点了。当然,在返回的时候注意要返回的地址是dummyHead.next,而不是dummyHead。

完整的步骤是:

1.我们创建一个虚拟链表头dummyHead,然后使用temp = dummyHead进行链表操作

2.开始循环head链表

3.当该节点的值不等于val时,将temp的next指向该节点,然后将temp指向它的next节点。

这里注意temp在结束时,需要将它的未节点指向None,避免出现符合条件的最后一次赋值后,链接指向错误问题。

代码实现过程:

class Solution {    public ListNode removeElements(ListNode head, int val) {        ListNode dummyHead = new ListNode(0);        dummyHead.next = head;        ListNode temp = dummyHead;        while (temp.next != null) {            if (temp.next.val == val) {                temp.next = temp.next.next;            } else {                temp = temp.next;            }        }        return dummyHead.next;    }}

以上是关于链表10:链表中删除元素的8道题之一的主要内容,如果未能解决你的问题,请参考以下文章

链表11:链表中删除元素的8道题之二

删除有序链表中相同的元素ii

被火车撞了都不能忘记的几道题(你会了吗?)

删除排序链表中的重复元素(简单)

删除数组中的元素(链表)

83删除链表中的重复元素