算法——链表(1)

news2024/11/19 18:32:09

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享链表专题的第一部分
如果有不足的或者错误的请您指出!

1.链表常用技巧总结

1.1引入虚拟头结点

在力扣上,基本提供的链表题目都是"无头的",但是针对无头链表,我们最怕的就是参数链表是一个空链表,就容易出现对null的访问.当我们引入虚拟头结点后,我们并不关心这个"头结点"里面放的是什么值,而这个节点只是起到一个哨兵的左右
举一个例子,如果我们要合并两个链表,引入第三链表将两个链表合并
在没有引入虚拟头结点的情况下:
在这里插入图片描述
那么我们的第三个链表就要先判断是否已经有节点了??如果有,直接插在后面即可;如果没有就要将插入的节点作为头结点
但是如果我们引入虚拟头结点
在这里插入图片描述
此时我们就不需要判断了,只需要将两个链表的节点依次插入到newHead后面即可
引入虚拟头结点的优势还有很多,我们在后面的题目中更能感受到

1.2不要吝啬空间

在链表专题,如果我们吝啬空间,有时候会给我们带来很大麻烦
举个例子:
我们通常会做到这样一种题目:在prve和prev.next节点之间插入cur节点
在这里插入图片描述
我们通常会这样做:
①cur.next = prev.next
②prev.next.prev = cur
③prev.next = cur
④cur.prev = prev
这四步操作中,前两步是一定要在后两个之前的
但是如果我们直接将prev.next用用一个临时节点存储起来:
在这里插入图片描述
这是就没必要去担心什么顺序问题了,直接针对三个节点进行操作即可

1.3快慢双指针的利用

快慢双指针在链表操作中经常遇到,而最常用的是我们下面这三种操作,最重要的是这三种操作往往只是作为一道题目里面的一小部分存在

1.3.1判断链表是否有环

题目:判断链表是否有环
在这道题中,我们定义一个fast指针和slow指针,fast指针一次走两步,slow指针一次走两步,那么如果链表没环,那么fast指针一定会走到null,如果链表没环,那么fast指针和slow指针一定会在环的某一点相遇
代码呈现:

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }
}

1.3.2找到链表环的入口

题目:找到环的入口
在上一题的基础上,如果链表有环,我们需要找到环的入口
实际上这道题有一个数学推导出来的结论:在fast指针和slow指针相遇点,此时这个相遇点距离环的入口,与起跑点距离环的入口,距离是一样的
在这里插入图片描述
即蓝色路程和红色路程是一样的
那么我们再定义一个节点node在起跑点位置,slow和node同时移动,二者相遇点就是环的入口
代码呈现:

public class Solution {
    private ListNode isCircle(ListNode head){
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return slow;
            }
        }
        return null;
    }
    public ListNode detectCycle(ListNode head) {
        ListNode node = isCircle(head);
        if(node == null || node.next == null){
            return null;
        }
        ListNode cur = head;
        while(cur != node){
            cur = cur.next;
            node = node.next;
        }
        return cur;
    }
}

1.3.3删除链表的倒数第k个节点

同样利用双指针,我们让fast指针先走k步,此时fast和slow之间的距离就是k;接着让fast和slow指针同时走,直到fast为null,此时slow和空节点的距离就是k,即为倒数第k个节点
代码呈现:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null || head.next == null){
            return null;
        }
        ListNode node = new ListNode(-1,head);//解决头结点被删的情况
        ListNode fast = node;
        ListNode slow = node;
        while(n > 0){
            n--;
            fast = fast.next;
        }
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return node.next;
    }
}

1.3.4寻找中间节点

题目:链表的中间节点
固定套路:定义fast指针和slow指针,fast一次走两步,slow一次走一步,当fast为空或者fast.next 为空时,slow所在位置就是中间节点的位置
代码:

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head.next;
        ListNode slow = head;
        while(fast != null){
            if(fast.next != null){
            fast = fast.next.next;
            }else{
                fast = fast.next;
            }
            slow = slow.next;
        }
        return slow;
    }
}

1.4反转链表

题目:反转链表
这道题如果引入"虚拟头结点",那么在时间复杂度为O(n)的情况下就能完成
即直接往虚拟头结点后面进行头插即可

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

2.两数相加

题目:两数相加

2.1解析

思路比较简单,用两个指针遍历两个链表,每次将两个节点里面的值相加,将得到的数的个位数作为新节点的值,把新节点插入到引入的虚拟头结点,同时记录下此时两数相加的进位,在下次计算的时候要加上进位

2.2题解

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        int tmp = 0;
        ListNode newHead = new ListNode(0);
        ListNode cur = newHead;//记录新链表的最后一个节点
        while(cur1 != null || cur2 != null || tmp != 0){//tmp != 0 是为了解决最后计算完还有进位的情况
          if(cur1 != null){
              tmp += cur1.val;
              cur1 = cur1.next;
          }
          if(cur2 != null){
              tmp += cur2.val;
              cur2 = cur2.next;
          }
          ListNode node = new ListNode(tmp % 10);
          tmp /= 10;
          cur.next = node;
          cur = cur.next;
        }
        return newHead.next;
    }
}

3.两两交换链表中的节点

题目:两两交换链表中的节点

3.1解析

这道题就能体现出我们之前说过的"不要吝啬变量"的好处
为了方便,我们还是引入"虚拟头节点"
在这里插入图片描述
对此链表进行两两翻转操作
要翻转两个节点,不仅需要记录这两个节点,还要记录前一个节点和后一个节点
因此我们直接引入这些变量:
在这里插入图片描述
就是对cur和next进行翻转操作,就很简单明了了,直接让prev.next = next; next.next = cur; cur.next = nnext;
在这里插入图片描述
此时前两个就翻转完了
接下来就要让prev指向当前cur的位置,继续进行刚才的操作
在这里插入图片描述
那么循环什么时候结束呢??会有两种情况
(1)就像我们上面这一组一样,当我们再次把prev指向cur的时候
在这里插入图片描述
此时,没翻转的节点就一个,那么就不用翻转了,即当next == null的时候是循环结束
(2)假设我们没有最后一个节点
在这里插入图片描述
那么此时已经没有节点了,自然就不需要翻转.因此cur == null也是循环结束的标志

3.2题解

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode newHead = new ListNode(0,head);
        ListNode prev = newHead;
        ListNode cur = prev.next;
        ListNode next = cur.next;
        ListNode nnext = next.next;
        while(cur != null &&  next != null){
            prev.next = next;
            next.next = cur;
            cur.next = nnext;
            prev = cur;
            cur = prev.next;
            if(cur != null){
                next = cur.next;
                if(next != null){
                    nnext = next.next;
                }
            }
        }
        return newHead.next;
    }
}

4.重排链表

题目:重排链表

4.1解析

此题最简单的思路就是将数组划分为两部分,并将右边的部分逆置,随后依次插入新的链表中
在这里插入图片描述
而其中的操作就是寻找中间节点、链表逆置、合并链表,我们在一开始都是讲过的,因此我们直接代码呈现\

4.2

    private ListNode findCenter(ListNode head){
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
    private ListNode reverse(ListNode head){
        ListNode newHead = new ListNode(1);
        ListNode cur = head;
        while(cur != null){
            ListNode tmp = cur.next;
            cur.next = newHead.next;
            newHead.next = cur;
            cur = tmp;
        }
        return newHead.next;
    }
    public void reorderList(ListNode head) {
        ListNode center = findCenter(head);
        ListNode node = reverse(center.next);
        //让中心节点的后面部分逆置
        center.next = null;//断开两部分
        ListNode cur1 = head;//遍历前一部分
        ListNode newHead = new ListNode(0);
        ListNode cur = newHead;//遍历新链表

        //合并
        while(cur1 != null){//前面的部分一定比后面的部分长
            cur.next = cur1;
            cur1 = cur1.next;
            cur = cur.next;
            if(node != null){
                cur.next = node;
                node = node.next;
                cur = cur.next;
            }
        }
        head = newHead.next;
    }

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言-预定义符号

编译和链接(基础速通版)-CSDN博客https://blog.csdn.net/Jason_from_China/article/details/137182220 预定义符号 包含 C语⾔设置了⼀些预定义符号,可以直接使⽤,预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的…

C++从入门到精通——初步认识面向对象及类的引入

初步认识面向对象及类的引入 前言一、面向过程和面向对象初步认识C语言C 二、类的引入C的类名代表什么示例 C与C语言的struct的比较成员函数访问权限继承默认构造函数默认成员初始化结构体大小 总结 前言 面向过程注重任务的流程和控制,适合简单任务和流程固定的场…

R155/R156:汽车网络安全新法规快速指南

R155/R156:汽车网络安全新法规快速指南 随着国际机构进入网络安全领域,制定法规、标准和指南,公众在其使用的车辆中享受更好的网络安全保护指日可待。随着时间的推移,车辆网络安全将不断发展以应对不断变化的网络威胁。 欧洲经济委员会 (ECE) 的 R155 和 R156 法规旨在应…

Day30 线程安全之窗口售票问题(含代码)

Day30 线程安全之窗口售票问题(含代码) 一、需求: 铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售, 请编写多线程程序来模拟这个效果( 注意:使用线程类的方式…

数据库 06-04 恢复

01 一.事务故障 二.系统 三.磁盘 02. 重点是稳定存储器 组成

【IEEE STD 1801】——Introduction

Introduction 本标准的目的是提供便携、低功耗的设计规范,这些规范可以在整个电子系统设计、分析、验证和实现流程中与各种商业产品一起使用。 当电子设计自动化(EDA)行业在20世纪80年代开始创建用于指定、仿真和实现数字电子电路的功能规范的标准时,主…

当下鸿蒙开发热潮,大前端开发有没有必要搞!!!

鸿蒙到底好不好?要不要搞? Android开发反正目前工作感觉也不好找,即便是上海这样的大城市也难搞,人员挺饱和的。而且年前裁员的公司那么多,再看自己,年龄不是优势;技术不是优势,想想…

DHCP-PXE

Dynamic Host Configuration Protocol 动态主机配置协议 1.Selinux 调试为Permission 防火墙配置 搭建DHCP的主机必须有一个静态地址,提前配置好 安装DHCP软件 服务名为dhcpd DHCP地址分配四次会话, DISCOVERY发现 OFFER 提供 REQUEST 回应 A…

PHP实现nginxPhp错误日志提取统计工具(路径+错误行+报错信息+次数排序)

粘贴PHP错误日志内容(NGINX 下PHP网站错误日志)。 作用:提取PHP Warning/Notice:路径错误行报错信息按出现次数排序。 以上已满足本人自己LNMP环境的调试需求&#xff0c;其他环境自己评估是否可用。 <?php //整理与分享&#xff1a;yujianyue<15058593138qq.com> $…

国家开放大学电大《钢结构》形考任务答案

电大搜题 多的用不完的题库&#xff0c;支持文字、图片搜题&#xff0c;包含国家开放大学、广东开放大学、超星等等多个平台题库&#xff0c;考试作业必备神器。 公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#x…

腾讯云-域名注册购买实名流程

1&#xff0c;注册并完成实名腾讯云账号 账号实名认证和后续要完成的域名实名认证应为同一主体单位。否则域名实名会不通过 参考链接 腾讯云账号实名 – 易极赞自助建站 2&#xff0c;进入腾讯云域名注册购买地址 推荐购买com&#xff0c;cn&#xff0c;net主流域名&#xf…

LabVIEW深度学习

目录 一、配置环境1.1、显卡选择1.2、下载显卡驱动1.3、下载并安装Anaconda1.4、配置Anaconda软件包下载服务器1.5、配置虚拟环境tf_gpu1.6、安装vscode1.7、安装tensorflow1.8、下载安装Git1.9、安装TensorFlow Object Detection API框架1.10、安装依赖的python软件包1.11、配…

Centos7下docker安装jenkins【使用docker-compose图文教程】

个人记录 前置条件&#xff1a;安装Docker与Docker-compose Centos7安装Docker与Docker-compose【图文教程】 查看jenkins最新的版本 https://www.jenkins.io/download/ 配置docker-compose.yml vim docker-compose.yml按i进行编辑模式&#xff0c;粘贴如下内容。把image里…

After Effects 2024:创意无界,想象成真mac/win版

在数字艺术飞速发展的今天&#xff0c;After Effects 2024已经站在了视觉特效和动态图形设计的最前沿。这款软件不仅继承了之前版本的强大功能&#xff0c;更在细节和效率上进行了全方位的优化和升级。 After Effects 2024软件获取 After Effects 2024为用户提供了更加丰富的特…

A - Penalty Kick

A - Penalty Kick 我又又又回来啦 题目&#xff1a; Problem Statement Takahashi will have N N N penalty kicks in a soccer match. For the i i i-the enalty kick, he will fail if i i i is a multiple of 3 3 3, and succeed otherwise . . . Print the resul…

拦截器抛出异常无法被全局异常处理器捕获问题

文章目录 基本说明问题描述问题原因解决方法前端执行的所有请求都通过Controller&#xff0c;而不是直接访问html定义一个/error路径的方法 总结 基本说明 我的前后端项目是放在一起的&#xff0c;前后端都是由springMVC进行控制&#xff0c;但是现在我在拦截器的preHandle方法…

c# wpf LiveCharts 简单试验2

1.概要 1.1 说明 1.2 要点 1.2.1 添加命名控件 xmlns:lvc"clr-namespace:LiveCharts.Wpf;assemblyLiveCharts.Wpf" 1.2.2 图片控件 <lvc:CartesianChart Name"chart" LegendLocation"Right"/> 1.3 代码文件引用 using LiveCharts…

循环神经网络:揭秘长期记忆的魔法之源

在人工智能和机器学习领域&#xff0c;循环神经网络&#xff08;Recurrent Neural Networks&#xff0c;简称RNN&#xff09;以其独特的架构和机制&#xff0c;在处理序列数据方面展现出了强大的能力。特别值得一提的是&#xff0c;RNN能够学习到长期的记忆&#xff0c;这使得它…

综测仪MT8862A控制方法

实现自动化控制&#xff0c;本次为大家讲解综测仪MT8862A的控制逻辑。 新建底层控制逻辑 在文件basis_contorl.py中写入仪器控制底层代码&#xff0c;代码如下&#xff1a; import tkinter.messagebox import pyvisaclass InstrumentControl(object):inst Nonedef __init__(…

利用Flutter混淆工具提高应用的反编译难度

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…