当前位置: 首页 > article >正文

代码随想录算法训练营第3天(链表1)| 203.移除链表元素 707.设计链表 206.反转链表

一、203.移除链表元素

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

视频:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili

讲解:代码随想录

注意:

针对头结点和非头结点的删除方式是不一样的:

正常节点:要找到该节点的上一节点

头结点:直接把 head 往后移一位

所以就要进行判断,要删除的节点是不是头结点(方法一)

或者,在头结点前设立一个虚拟节点(dummy head)(方法二)

方法一:判断链表删除元素

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while (head != null && head.val == val) {
            head = head.next;
        }

        ListNode curr = head;
        while (curr != null && curr.next != null) { 
            if (curr.next.val == val) {
                curr.next = curr.next.next;
            } else {
                curr = curr.next;
            }
        }
        return head;
    }
}

要点 1:

判断头结点是否符合的时候,使用 if 就只能判断一次,如果第二个元素也符合条件,就漏删了

所以要用 while ,在头结点不符合条件的时候,才继续往下走

要点 2:

往后查找的时候,要定义一个指针,那么指针的位置指向哪里?

如果是第二位,第二位符合删除条件,找不到上一位的 next 指针。(x)

所以要指向头结点

尝试过程:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while (head != null && head.val == val) {
            head = head.next;
        }

        ListNode curr = head;
        while (curr.next != null && curr != null) {     //这里有问题!!
            if (curr.next.val == val) {
                curr.next = curr.next.next;
            } else {
                curr = curr.next;
            }
        }
        return head;
    }
}

报这个错误的原因:

虽然从逻辑上看是想同时确保当前节点 curr 和它的下一个节点 curr.next 都不为 null,但如果 curr 本身已经是 null 了,再去访问 curr.next 就会直接抛出空指针异常。

正确的做法应该是先判断 curr 是否为 null再去判断 curr.next 是否为 null,像这样修改条件为 while (curr!= null && curr.next!= null)

方法二:设置虚拟头结点

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode();
        dummy.next = head;

        ListNode curr = dummy;
        while(curr != null && curr.next != null){
            if(curr.next.val == val){
                curr.next = curr.next.next;
            } else {
                curr = curr.next;
            }
        }
        return dummy.next;
    }
}

注意头结点的指针是不能更改的,因为最后要用到头结点返回。

二、707.设计链表

题目:707. 设计链表 - 力扣(LeetCode)

视频:帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili

讲解:代码随想录

利用虚拟头结点方式,对所有结点统一操作

curr 指针从什么位置开始

循环从什么地方结束

增加/删除时几个指针的操作顺序

class ListNode {    //定义链表
    ListNode next;
    int val;
    public ListNode() {}

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

class MyLinkedList {
    ListNode dummy;
    int size;


    //以此开始
    public MyLinkedList() {    //初始化
        size = 0;
        dummy = new ListNode(-1);
        
    }
    
    public int get(int index) {    //获取
        if(index < 0 || index > size-1) return -1;

        ListNode cur = dummy;

        for(int i=0; i < index; i++){
            cur = cur.next;
        }

        return cur.next.val;
    }
    
    public void addAtHead(int val) {   //
        addAtIndex(0, val);        
    }
    
    public void addAtTail(int val) {     //
        addAtIndex(size, val);
    }
    
    public void addAtIndex(int index, int val) {   //插入
        if(index < 0 || index > size)  return;
        
        ListNode newNode = new ListNode(val);
        ListNode curr = dummy;

        for(int i = 0; i < index; i++){
            curr = curr.next;
        }

        newNode.next = curr.next;
        curr.next = newNode;

        size++;      
    }
    
    public void deleteAtIndex(int index) {   //删除
        if(index < 0 || index > size-1) return;

        ListNode curr = dummy;
        for(int i = 0; i < index; i++){
            curr = curr.next;
        }
        
        curr.next = curr.next.next;
        size--;
    }
}


class MyLinkedList {
    class ListNode {
        int val;
        ListNode next;

        ListNode(int val) {
            this.val = val;
        }
    }

    private int size;
    private ListNode head;

    // 初始化
    public MyLinkedList() {
        this.size = 0;
        this.head = new ListNode(0);

    }

    public int get(int index) {
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode cur = head;
        for (int i = 0; i <= index; i++) {
            cur = cur.next;
        }
        return cur.val;

    }

    public void addAtHead(int val) {
        ListNode newNode = new ListNode(val);
        newNode.next = head.next;
        head.next = newNode;
        size++;

    }

    public void addAtTail(int val) {
        ListNode newNode = new ListNode(val);
        ListNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = newNode;
        size++;

    }

    public void addAtIndex(int index, int val) {
        if (index < 0 || index >= size) {
            return;
        }

        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        ListNode newNode = new ListNode(val);
        newNode.next = cur.next;
        cur.next = newNode;
        size++;
    }

    public void deleteAtIndex(int index) {
        if(index < 0 || index >=size){
            return;
        }

        ListNode cur = head;
        for(int i=0; i<index; i++){
            cur = cur.next;
        }
        cur.next = cur.next.next;
        size--;

    }
}

要点是要确定每个循环停在哪里,否则会报空指针异常

三、206.反转链表

题目:206. 反转链表 - 力扣(LeetCode)

视频:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

讲解:代码随想录

要点:

1、 两个指针(加上 temp 是三个指针)的起始位置

2、 循环的终止条件

3、 调换每一个节点,调整的顺序

正确答案:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode pre = null;
        ListNode cur = head;
        ListNode temp;

        while(cur != null){
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }

        return pre;
     
    }
}

尝试过程:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode pre = null;
        ListNode cur = head;
        ListNode temp;

        for(int i = 0; i <= head.size-1; i++){   //这里报错!!
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }

        return pre;

    }
}

错误原因:ListNode类并没有定义size属性。在链表中,我们通常通过遍历来获取链表的长度,而不是直接存储长度。

四、链表总结

1、虚拟头结点:如果想要针对不同位置(头与非头结点)的节点用相同的处理办法,可以考虑虚拟头结点,对所有节点一视同仁。

2、双指针:节省空间

3、注意几个调整动作的先后顺序,防止指针丢失的结果。


http://www.kler.cn/a/500066.html

相关文章:

  • Jina AI/Reader:将 URL 和 PDF 内容自动化提取并转换为 LLM 可处理文本
  • EJB与微服务:Java的秘密武器
  • go oom堆内存分析
  • 论文导读 | 可串行化事务机制
  • LayaAir3.2来了:性能大幅提升、一键发布安装包、支持WebGPU、3D导航寻路、升级为真正的全平台引擎
  • web网页设 web网页设计,html页面制作,div布局 css js
  • 安全运维管理 10.2资产管理
  • Kubernetes 服务发现与负载均衡
  • Kotlin | Android Provider 的实现案例
  • PHP获取局域网ip(192.168)
  • 第三十六章 C++ 多线程
  • 第5天:APP应用微信小程序原生态开发H5+Vue技术封装打包反编译抓包点
  • 转运机器人在物流仓储行业的优势特点
  • 大数据智能选课系统
  • day07_Spark SQL
  • 【技术支持】安卓无线adb调试连接方式
  • RepPoints: Point Set Representation for Object Detection—用于目标检测的点集表示
  • Python的秘密基地--[章节11] Python 性能优化与多线程编程
  • 简单说一下 类
  • 地下苹果(马铃薯)怎么破局?
  • 前端使用Get传递数组形式的数据