自学内容网 自学内容网

代码随想录刷题day09|(链表篇)203.移除链表元素

目录

一、链表理论基础 

1.链表的定义

2.链表的类型

3. 链表的操作

4.链表的存储方式

5.链表特点

二、移除链表元素

法1:在原有链表上直接移除

法2:虚拟头结点

三、相关算法题目

四、总结


一、链表理论基础 

代码随想录 (programmercarl.com)

1.链表的定义

不同语言代码不同;以java为例,定义链表结点:

public class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
        this.val = val;
    }
}

2.链表的类型

单链表(只有指向后一结点指针域)、双链表(有指向后一结点指针域、指向前一结点指针域)、循环链表(单链表+首尾相连)、循环双链表(双链表+循环链表)

3. 链表的操作

增删 只需改变该结点的前一结点的指针域值,所以增删中必须知道上一结点;查询操作,需要遍历整个链表;

4.链表的存储方式

和数组连续地址不同,链表在内存中地址的存放不是连续的,而是分散的;

5.链表特点

增删快,查询慢;

二、移除链表元素

删除(添加)链表结点时,一般两种方法:1.在原有链表上直接移除 2.虚拟头结点

法1:在原有链表上直接移除

定义一个临时指针cur,指向头结点,移动cur,遍历整个链表,判断cur指向结点的数值是否为目标值,若是,则改变链表指针域的值,移除该结点,否则,cur继续指向下一结点。

这种方法下,需要判断移除的结点是否为头结点,如果为头结点,那么移除操作为:头结点直接往后移动一个,将下一个结点作为当前的头结点;如果非头结点,移除操作为:将该结点的前一个结点指向该结点的后一个结点。

注意点:

  1. 首先判断移除的结点是否为头结点,进而要分开讨论两种情况下的操作逻辑;
  2. 判断要删除的结点是否为头结点 的过程要注意是while还是if,本题是while;
  3. 无需定义太多指针(front、index等),可以使用 .next.next进行指代;
  4. 注意判断结点是否为空的情况;
  5. 最后返回的头结点是原链表头结点;
  6. 定义临时结点必要性:不可用头结点遍历,否则头结点指向的值始终在变化,无法返回;

法2:虚拟头结点

自定义一个头结点dummy,并使其指向实际头结点head,定义一个临时指针cur进行遍历,寻找和目标值一致的结点,并进行移除操作。

虚拟一个头结点,能够用统一的规则去处理(删除、添加)所有结点(包括头结点),无需再判断被处理结点是否为头结点。

注意点:

  1. 定义结点的代码书写规范要熟悉;
  2. 最后返回的头结点,不是原本的头结点head,而是虚拟头结点的下一个结点(这才是经过处理以后的链表的头结点),因为head可能在操作过程中已被移除;
  3. 易错:注意⚠️2中如果返回dummy,则返回的链表第一个结点值是dummy结点的值(为0);
  4. head和head.next不同,前者是head指针指向的(头)结点,后者是head指向的结点的指针域的值,也就是 head指向的结点的下一个结点;
  5. 定义一个临时指针cur,指向dummy,也即被操作结点的上一个结点;

三、相关算法题目

203.移除链表元素

203. 移除链表元素 - 力扣(LeetCode)

法1:原有链表直接移除

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //原链表上直接删除
        ListNode cur;
        while(head != null && head.val == val){
        //删除结点为头结点
            head = head.next;
        }
        cur = head;//此时head.val != val 
        //要知道被删除结点的上一个结点
        while(cur != null && cur.next != null){
        //删除结点为非头结点
            if(cur.next.val == val){
                cur.next = cur.next.next;
            }
            else{
                cur = cur.next;//只有不等 cur才能移动到下一个结点
            }
    }
    return head;
}
}

 法2:虚拟头结点

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //虚拟头结点
        ListNode dummy = new ListNode(); //定义虚拟头结点
        dummy.next = head; //指向原本头结点
        ListNode cur = dummy;
        while(cur.next != null){
            //当指向最后一个结点时,上一轮循环会判断该结点的值,所以本轮不会进入while循环
            //不管最后一个结点是否为目标结点 最终返回的链表 尾结点均指向null
            if(cur.next.val == val){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        //return dummy; //报错 链表首个元素值为0
        return dummy.next; //返回实际头结点
}
}

四、总结

1.注意两种方法对头结点的不同处理;

2.定义临时指针时,指向的结点要想清楚;

3.最后返回的结点要明白;

4.链表的定义要会写;


原文地址:https://blog.csdn.net/m0_64265848/article/details/145193101

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!