移除链表元素

32 阅读3分钟

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

  • 解题思路
    链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域,一个是指针域(存放指向下一个节点的指针),最后一个节点的指针指向null(空指针)。 链表的入口节点称为链表的头节点,即head。

链表类型:

  • 单链表

  • 双链表

     每一个节点有两个指针域,一个指向下一个节点,一个指向前一个节点。  
    
     双链表既可以向前查询,也可以向后查询。
    
  • 循环链表
    链表首尾相连

链表的存储方式:

链表通过指针域的指针链接在内存中的各个节点。  

链表的操作:

  • 删除节点

image.png

删除D节点,将C节点的next指针指向E节点即可。 
  • 添加节点

image.png

在添加节点时,需要注意操作顺序,如图,在添加F节点时,如果先将C节点的next指针指向F节点,那么
就会造成 “断链”。因此添加节点F时,要先将F节点的next指针指向D节点,再将C节点的next指针指向F节点。

综上所述,链表的增添和删除操作的时间复杂度都是O(1),不会影响到其他节点。

在遇到链表相关的问题时,要想链表和数组的数据存储底层区别,数组存储方式在检索元素和修改元素时很方便,根据数组的下标检索或修改即可,但是数组存储方式有一个问题,就是在插入元素和删除元素时比较麻烦,插入元素需要将该位置之后的元素全部后移,空出位置以便可以插入元素;同时,数组的元素是不能删的,只能覆盖。删除元素时需要将该位置之后的元素全部前移,覆盖掉待删除的元素。上述的操作导致仅仅是增删元素便花费了很多时间,同时浪费了资源。
使用链表存储方式在增删元素时,修改指针的指向即可,这样就大大方便了增删元素。

  • 在做链表类的题目时要注意空指针问题,当出现空指针,就会运行出错。

解决这道题目时使用链表中删除节点的常规操作即可,但是要注意头节点的问题,如果头节点的值等于带求值的话,就要单独删除头节点,这个操作需要单独写一段代码。

代码如下:

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

上述处理头节点时需要单独的一段代码逻辑,是否有方法可以以一种统一的逻辑来移除头节点? 可以设置一个虚拟头节点,这个虚拟头节点为新的头节点,此时移除头节点也就是删除节点的常规操作了。
同时在最后返回时,要记得需要返回头节点的后继节点。
代码如下:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) return head;
        ListNode node = new ListNode(-1, head);
        ListNode pre = node;
        ListNode cur = head;
        while (cur != null ){
            if (cur.val == val){
                pre.next = cur.next;

            }else {
                pre = cur;
            }
            cur = cur.next;
        }
        return node.next;
    }
}

上面两种解法的时间复杂度都是O(n),空间复杂度都为O(1)。