小白都能看懂的力扣算法详解——链表(一)

!!本篇所选题目及解题思路均来自代码随想录 (programmercarl.com)

一 203.移除链表元素

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

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

我们的目标是要寻找val等于目标值的节点,那么我们就要遍历这个链表,找到该节点,之后让该节点的上一个节点指向它的下一个节点,即可实现“删除”。但是我们知道,单向链表只能指向它的下一个节点,那么我们该如何找到它的上一个节点呢?这里我能可以定义一个指针cur,让cur指向该元素的前一个节点,那么cur.next = cur.next.next 即实现了该节点的上一个节点指向它的下一个节点。因此我们可以得出cur.next = head;也就是说cur是头结点的前一个节点。解决了“删除”的问题,接下来思考如何实现寻找这个节点。也就是说,满足val等于目标值这个条件,翻译过来就是cur.next.val == targrt;(别忘了cur是当前节点的前一个节点,cur.next才是该节点)。也就是满足条件时执行cur.next = cur.next.next 操作,不满足条件时cur就移向下一个节点,继续遍历。最后一个问题,如何判断遍历何时结束?不难判断出,当cur指向链表的最后一个节点时,循环就该结束了(此时这个节点的val已经在cur指向它前一个节点时就对比过了),而cur.next为null也无法得到cur.next.val了。也就是说,我们的循环判断条件就是cur.next != null

整体思路结束,接下来来考虑一些特殊点,比如各种临界值。首先是当target==head.val的情况,满足cur.next.val == targrt条件;之后是target等于最后一个节点的值的情况,我们已经谈论过了;第三是链表长度为0的情况,此时head为null,即满足cur.next为null,退出循环;最后是有连续节点满足val等于target的情况,这时我们就会发现,如果有连续值满足条件,那么第二个节点就会被跳过对比,如何解决这个问题呢?我们可以在进行完cur.next = cur.next.next操作后,先不急着将cur向后移动(cur = cur.next),而是让它在原位置不动,这样下一轮比较cur.next.val时,cur.next就是第二个节点啦。

最后瞅一眼返回值,是返回头节点吗?如果直接返回head,那么如果head.val刚好等于target,已经被我们删除了,该怎么办?这时候就要请出我们的老朋友——虚拟头节点了(不熟也没关系,多刷几道链表题目之后就会发现,基本上有链表的地方就有它)。在最开始,设置一个虚拟头节点指向head,即p.next = head;无论头结点被删掉多少个,p.next都会指向它的下一项元素,也就是p始终都是“首个节点”,最后直接返回p.next即可。

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

 代码随想录上提供了另一种不适用虚拟头节点的思路,这里直接附上代码,不再具体讲解。其实思路与上面几乎一致,只是多了一个对头节点是否需要删除的判断,就像上面说过的,如果head.val刚好等于target,已经被我们删除了,该如何找到链表的头部?

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

 关于此方法的具体讲解,以及该题目的视频讲解,其他语言版本代码,大家可以到代码随想录中自行查看。代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

二 206.反转链表

题目描述:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

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

 这道题乍一看很难(反正我是毫无思路),看到有一种暴力解法是将链表中的元素存储在数组中,反转数组,再放回到链表里,感觉可行但是我没具体尝试(也太暴力了一点......).听完卡尔哥的讲解之后我的感受只有两个字:秒啊。

回归正题,翻转链表我们很自然就能想到让下一个元素指向上一个元素,问题在于,在单向链表中,如何获得上一个元素的位置呢?这里又要用到虚拟头节点了。我们定义一个指针pre指向虚拟头节点,也就是head的前一个元素,再定义一个指针cur指向head,让cur.next指向pre,即可完成下一个元素指向上一个元素,之后我们只要向后移动pre和cur,循环上面的操作即可。是不是很完美?错!这种做法存在一个很明显的错误,当cur.next指向pre之后,head后面的节点就失去了指向它的头节点,如图,那么cur如何如我们所愿向后移动呢?

这时候,我们就需要清楚我们的第三个指针temp出场了。我们在执行cur.next = pre之前,先令p指向cur.next,如此一来,就可保留住cur下一个节点的位置,我们就能随意改变cur.next的指向了。当我们完成cur.next = pre的操作后,只要让cur = temp,就可以让cur指向它原先的下一个节点了。

大体思路完成,接下来思考实现细节。

1.什么时候终止循环?

当循环进行到这一步时,整个反转操作就完成了,再往下走(cur = null)也就没有了意义,所以可以得到,临界条件为cur!=null

2.返回值是什么?

搞不明白就画图,在最后一步操作执行完时,指针情况是这样的,此时cur!=null条件不满足,结束循环。原先的最后一个节点就是现在的头节点,也就是pre指针所指向的节点,因此我们返回pre。

3.链表为空或只有一个节点时,是否需要特殊讨论?

链表为空时,head也为空,此时自然满足cur=null的条件,不进入循环,返回pre,为空。符合。

链表只有头节点时,cur != null,完成第一轮交换,之后pre指向了head,cur执行null,退出循环,返回pre(head),满足。

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = new ListNode();
        ListNode pre = new ListNode();
        cur = head;
        pre= null; 
        while(cur != null ) {
            ListNode temp = new ListNode();
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

关于该题目的视频讲解,其他语言版本代码,大家可以到代码随想录中自行查看。

代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html

三 707.设计链表

题目描述:

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

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

 这道题非常非常非常麻烦,但是其实难度不是很高,建议大家可以做完其他链表题目后再翻回来做这道题,可能更容易一些。这里直接给出代码及相应注释,不再具体讲解,关于该题目的视频讲解以及其他语言版本代码,大家可以到代码随想录中自行查看。代码随想录 (programmercarl.com)icon-default.png?t=N7T8https://www.programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

 // 设计链表结构
 class ListNode {
    int val;
    ListNode next;
    ListNode(){}
    ListNode(int val) {
        this.val=val;
    }
}
// 初始化
class MyLinkedList {
    //size存储链表元素的个数
    int size;
    //虚拟头结点
    ListNode head;

    //初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }

    //获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
    public int get(int index) {
        //如果index非法,返回-1
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode currentNode = head;
        //包含一个虚拟头节点,所以查找第 index+1 个节点
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }

    //在链表最前面插入一个节点,等价于在第0个元素前添加
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    //在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        if (index < 0) {
            index = 0;
        }
        size++;
        //找到要插入节点的前驱
        ListNode pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        ListNode toAdd = new ListNode(val);
        toAdd.next = pred.next;
        pred.next = toAdd;
    }

    //删除第index个节点
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        size--;
        if (index == 0) {
            head = head.next;
	    return;
        }
        ListNode pred = head;
        for (int i = 0; i < index ; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/378583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java数据结构】ArrayList和LinkedList的遍历

一&#xff1a;ArrayList的遍历 import java.util.ArrayList; import java.util.Iterator; import java.util.List;/*** ArrayList的遍历*/ public class Test {public static void main(String[] args) {List<Integer> list new ArrayList<>();list.add(5);list…

探索C语言中的联合体与枚举:数据多面手的完美组合!

​ ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 联合体的定义 联合体又叫共用体&#xff0c;它是一种特殊的数据类型&…

【网页设计期末】茶文化网站

本文资源&#xff1a;https://download.csdn.net/download/weixin_47040861/88818886 1.题目要求 设计要求&#xff1a; &#xff08;1&#xff09;网站页面数量不少于4个&#xff0c;文件命名规范&#xff0c;网站结构要求层次清楚&#xff0c;目录结构清晰&#xff0c;代码…

TCP的连接和断开详解

目录 1.TCP基础知识 1.1.TCP 头格式 1.2.TCP协议介绍 1.3.UDP协议介绍 1.4.TCP 和 UDP 区别 1.5.TCP 和 UDP 应用场景 1.6.计算机网络相关术语&#xff08;缩写&#xff09; 2.TCP 连接建立&#xff1a;三次握手 2.1.TCP 三次握手过程 2.2.三次握手原理 2.3.异常分析…

浏览器F12调试

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

java实现栈功能

1.使用数组方式 public static void main(String[] args) throws Exception {BufferedReader br new BufferedReader(new InputStreamReader(System.in));int operateNum Integer.parseInt(br.readLine());//操作次数String inputInfo;//输入信息StringBuilder outputSb new…

湿度计算方法

湿度计算方法 &#xff08;1&#xff09;绝对湿度&#xff1a; 绝对湿度是指一定体积的空气中含有的水蒸气的质量&#xff0c;一般其单位是克/立方米。 其中的符号分别是&#xff1a; e–蒸汽压&#xff0c;单位是帕斯卡&#xff08;Pa) Rw–水的气体常数461.52J/&#xff…

1899_野火FreeRTOS教程阅读笔记_任务创建

1899_野火FreeRTOS教程阅读笔记_任务创建 全部学习汇总&#xff1a; g_FreeRTOS: FreeRTOS学习笔记 (gitee.com) 关于这部分&#xff0c;从一般前后台程序到RTOS的任务描述了很多。但是我觉得这本书的这部分描述没有描述到关键的信息点。其实&#xff0c;RTOS存在的一个主要的目…

UML 2.5图形库

UML 2.5图形库 drawio是一款强大的图表绘制软件&#xff0c;支持在线云端版本以及windows, macOS, linux安装版。 如果想在线直接使用&#xff0c;则直接输入网址drawon.cn或者使用drawon(桌案), drawon.cn内部完整的集成了drawio的所有功能&#xff0c;并实现了云端存储&#…

一文读懂:MybatisPlus从入门到进阶

快速入门 简介 在项目开发中&#xff0c;Mybatis已经为我们简化了代码编写。 但是我们仍需要编写很多单表CURD语句&#xff0c;MybatisPlus可以进一步简化Mybatis。 MybatisPlus官方文档&#xff1a;https://www.baomidou.com/&#xff0c;感谢苞米豆和黑马程序员。 Mybat…

Spring Boot的打包方式:JAR vs. WAR 打包方式

Spring Boot的打包方式&#xff1a;JAR vs. WAR 打包方式 Spring Boot是一个流行的Java开发框架&#xff0c;提供了快速、便捷的应用程序开发和部署方式。本文将介绍Spring Boot的两种常见打包方式&#xff1a;JAR和WAR。我们将深入探讨它们的特点、适用场景和部署方式&#xf…

使用命令修复windows 7/8引导,解决GHO映像恢复后不能进入系统的问题

背景&#xff1a; 最近使用ghost恢复windows7的GHO系统映像&#xff0c;重启后找不到引导系统。原因是没有激活系统分区。而之前安装在系统上的PE系统已经被删除。此时手里只有一个windows 10的启动u盘。可谓是绝望。 解决办法&#xff1a; 启动windows10系统u盘&#xff0c;点…

【python】绘制春节烟花

一、Pygame库春节烟花示例 下面是一个使用Pygame实现的简单春节烟花效果的示例代码。请注意&#xff0c;运行下面的代码之前&#xff0c;请确保计算机上已经安装了Pygame库。 import pygame import random import math from pygame.locals import *# 初始化pygame pygame.ini…

4.4 特效优化注意点

一、特效模型制作标准和注意事项 1.特效模型面数最大500&#xff08;面数可以加到1000&#xff0c;但是要分Lod等级&#xff09; &#xff08;模型对打面熟需要根据项目需求做好规范&#xff0c;例如Lod0 1000Tris&#xff0c; Lod1...Lod3 100Tris&#xff0c;最好以引擎内的三…

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)

原文&#xff1a;Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十八章&#xff1a;强化学习 强化学习&#xff08;RL&#xff09;是当今最激动人心的机器学习领域之一&#xff0c;也是最古老…

VMwawre配置静态ip

1、查看当前虚拟机网关&#xff08;记住这个网关&#xff0c;后面使用&#xff09; 2、进入目录命令&#xff1a;cd /etc/sysconfig/network-scripts/ 3、编辑网卡配置文件命令&#xff1a;vim ifcfg-ens33 4、配置静态IP&#xff0c;修改和增加如下信息&#xff1a; 修改的内…

编码技巧——在项目中使用Alibaba Cloud Toolkit远程部署

背景 在新公司项目开发&#xff0c;当前项目为自建项目&#xff0c;意思是从开发到运维都需要自己负责&#xff0c;远程的服务器也是自己搭建的win操作系统&#xff1b; 之前在大厂工作时&#xff0c;一般提交代码之后&#xff0c;CICD流水线会自动的执行最新代码的拉取、构建打…

神经网络 | 常见的激活函数

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要介绍神经网络中必要的激活函数的定义、分类、作用以及常见的激活函数的功能。 目录 一、激活函数定义 二、激活函数分类 三、常见的几种激活函数 1. Sigmoid 函数 &#xff08;1&#xff09;公式 &#xff08;2&a…

Linux笔记之expect和bash脚本监听输出并在匹配到指定字符串时发送中断信号

Linux笔记之expect和bash脚本监听输出并在匹配到指定字符串时发送中断信号 code review! 文章目录 Linux笔记之expect和bash脚本监听输出并在匹配到指定字符串时发送中断信号1.expect2.bash 1.expect 在Expect脚本中&#xff0c;你可以使用expect来监听程序输出&#xff0c;…

Redis——高级主题

介绍Redis的高级主题&#xff0c;包括服务器配置、Redis事务、Redis发布和订阅、Pipeline批量发送请求、数据备份与恢复等。 1、服务器配置 在Windows和Linux的Redis服务器里面&#xff0c;都有一个配置文件。Redis配置文件位于Redis安装目录下&#xff0c;在不同操作系统下&…