算法通关村第一关链表白银挑战笔记

 删除链表元素专题

删除特定结点

题目 LeetCode 203

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

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {

    }
}

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

示例 2:

输入:head = [], val = 1 输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7 输出:[]


分析

属于链表删除节点题型,此时就要知道,删除节点时的不同种情况

(1)删除链首结点

步骤简述:

  1. 创建一个新的节点,并为其分配内存空间。
  2. 将新节点的指针域(存储下一个节点的地址)指向当前的头节点。
  3. 将头指针(head)指向新节点的地址,以使得新节点成为新的头节点

(2)删除链中结点

步骤简述:

  1. 创建一个新节点,并为其分配内存空间。
  2. 遍历链表,找到要插入位置的前一个节点。
  3. 将新节点的指针域指向前一个节点原本指向的下一个节点。
  4. 将前一个节点的指针域指向新节点。

(3)删除链尾结点

步骤简述:

  1. 创建一个新节点,并为其分配内存空间。
  2. 将新节点的数据赋值为要添加的数据。
  3. 将新节点的指针域设置为null,表示它是链表的最后一个节点。
  4. 将最后一个节点的指针域指向新节点。

题目给出了头结点head和目标值val,

所以从头结点head开始遍历链表去寻找目标值val去删除链表中的节点


代码

class Solution {  
    public ListNode removeElements(ListNode head, int val) {  
        // 创建一个虚拟头结点  
        ListNode dummyHead = new ListNode(0);  
        // 让虚拟头节点指向链表的头结点head  
        dummyHead.next = head;  
        // 同时创建节点cur也指向虚拟头节点  
        // 也直接指向链表头结点head  
        ListNode cur = dummyHead;  
  
        // 用cur去遍历链表  
        while (cur.next != null){  
            // 符合就删除  
            if (cur.next.val == val){  
                cur.next = cur.next.next;  
            // 不符合就转到下一个  
            } else {  
                cur = cur.next;  
            }  
        }  
        // 返回之前一开始就指向头结点的虚拟节点  
        return dummyHead.next;  
    }  
}
  1. 创建虚拟头节点
    • ListNode dummyHead = new ListNode(0);:创建一个新的ListNode实例,名为dummyHead,并将它的值设置为0。这个节点是虚拟的,不会实际存储任何数据,只是作为一个辅助工具来简化链表的删除操作。
    • dummyHead.next = head;:将虚拟头节点dummyHeadnext指针设置为链表的头节点head,这样就可以通过虚拟头节点来访问整个链表。
  1. 创建当前节点
    • ListNode cur = dummyHead;:创建一个新的ListNode实例,名为cur,并将它设置为虚拟头节点dummyHead的下一个节点。
  1. 遍历链表
    • while (cur.next != null) {...}:使用循环遍历链表。循环条件是当前节点的下一个节点不为空。
  1. 删除元素
    • 在循环的每次迭代中,都会检查当前节点的下一个节点的值是否等于给定的值val
    • 如果等于,那么就通过cur.next = cur.next.next;将当前节点(即下一个节点)从链表中删除。这实际上跳过了下一个节点,因为它具有特定的值,我们不希望保留它。
    • 如果不等于,那么就通过cur = cur.next;将当前节点移动到下一个节点。继续遍历链表。
  1. 返回结果
    • 最后,当循环结束时,我们返回虚拟头节点dummyHead的下一个节点,也就是经过删除操作后的链表的头节点。

删除倒数第n个结点

题目 LeetCode 19

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {

    }
}

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

示例 2:

输入:head = [1], n = 1 输出:[]

示例 3:

输入:head = [1,2], n = 1 输出:[1]


分析

首先题目没告诉我们链表的长度,只告诉我们头结点和要删除的倒数第N个结点。

所以我们需要找到这个节点,问题就能解决了。

找到结点的方法也分为几种:

第一种

先遍历一遍链表,找到链表总长度L,然后重新遍历,位置L-N+1的元素就是我们要删的。

eg:假设有5个结点,要删除倒数第2个,所以我要删除的是5-2+1个即第4个

第二种

定义first和second两个指针,first先走N步,然后second再开始走,当first走到队尾的时候,second就是我们删除的节点

第三种

使用栈,先将元素全部压栈,然后弹出第N个的时候就是我们要的

要用栈,又要用链表,一个搞不好,全gg,不推荐


代码

第一种,两次遍历

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 创建一个虚拟头结点  
        ListNode dummy = new ListNode(0);
        // 让虚拟头节点指向链表的头结点head  
        dummy.next = head;
        // 同时创建节点cur也指向虚拟头节点  
        // 也直接指向链表头结点head 
        ListNode cur = dummy;
        
        //获取链表长度,再遍历到要删除位置的前一个
        int length = getLength(head);
        for (int i = 1; i < length - n + 1; i++){
            cur = cur.next;
        }
        //实现删除
        cur.next = cur.next.next;
        //返回虚拟头结点
        return dummy.next;
    }
    
	//链表长度获取
    public int getLength(ListNode head){
        int length = 0;
        while (head != null){
            length++;
            head = head.next;
        }
        return length;
    }
}

第二种

双指针

class Solution { 
    public ListNode removeNthFromEnd(ListNode head, int n) { 
        // 创建虚拟指针  
        ListNode dummyNode = new ListNode(0); 
        // 将虚拟指针指向头指针  
        dummyNode.next = head;   
        // 创建first指针指向head  
        ListNode first = head;   
        // second指针始终指向dummyNode,也就是始终指向head的前一个节点 
        ListNode second = dummyNode; 

        // 将first指针向后移动n个节点
        for (int i = 1; i <= n; i++) { 
            first = first.next;   
        }  
  
        while (first != null) {   
            first = first.next; // 将first指针向后移动一个节点  
            second = second.next; // 将second指针向后移动一个节点  
        }  
        //删除目标结点
        second.next = second.next.next;
        // 返回dummyNode的下一个节点,也就是删除节点后的链表头节点
        return dummyNode.next; 
    }  
}

删除重复元素

只删除重复的一个

题目 LeetCode 83

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {

    }
}

示例 1:

输入:head = [1,1,2] 输出:[1,2]

示例 2:

输入:head = [1,1,2,3,3] 输出:[1,2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

分析

题目已经保证是升序排列的,所以重复的元素是挨在一起的

即:1、2、2、3、3、4、6、8这样

所以可以直接从头节点遍历,找到重复了就直接删除


代码
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        //判断头结点是否为空
        if (head == null){
            return head;
        }

        //创建节点cur指向head
        //再用cur遍历链表
        ListNode cur = head;
        while (cur.next != null){
            //发现重复删除
            if (cur.val == cur.next.val){
                cur.next = cur.next.next;
            }else{
            //否则转到下一个
                cur = cur.next;
            }
        }
        //返回删除后的头结点
        return head;
    }
}

把所有重复的删除

题目

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {

    }
}

示例 1:

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

示例 2:

输入:head = [1,1,1,2,3] 输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

分析

题目已经保证是升序排列的,所以重复的元素是挨在一起的

即:1、2、2、3、3、4、6、8这样

只是说要把所有重复的都删除

需要在遍历的时候去发现重复元素后,多一层while判断后面的元素也是否重复

注意为指针空情况


代码
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        //判断头结点是否为空
        if (head == null){
            return head;
        }

        //创建虚拟头结点并指向头结点
        ListNode dummy = new ListNode(0, head);
        
        // 同时创建节点cur也指向虚拟头节点  
        // 也直接指向链表头结点head 
        ListNode cur = dummy;
        //再使用cur指针去遍历链表
        while (cur.next != null && cur.next.next != null){
            //当发现重复元素时,先把重复元素记录下来
            if (cur.next.val == cur.next.next.val){
                int x = cur.next.val;
                //在从这重复的节点开始往后遍历
                //看后面的是否也重复,重复就删除
                while (cur.next != null && cur.next.val == x){
                    cur.next = cur.next.next;
                }
            } else {
                //没发现重复元素就转到下一个
                cur = cur.next;
            }   
        }
        //返回虚拟节点指向的头结点
        return dummy.next;
    }
}

最后推荐一个知识星球


编辑

编辑

编辑

详情可至链接

编程导航知识星球 | 编程导航知识星球

https://wx.zsxq.com/mweb/views/joingroup/join_group.html?group_id=51122858222824&secret=218iwimac5qa452q3o6pz2os5ajftbb3&inviter_id=212542158511251&share_from=GroupQRCodeSaved&keyword=11RQPQaqp&abtest_source=ABTest_90

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨空集

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值