算法系列--链表问题

一.一些经验总结

  1. 链表天然具有递归性质,单链表可以看做一个单叉树,很多可以应用到二叉树的题目也可以应用到链表的题目之中,下面是一个体现单链表递归性质很好的例子逆序打印链表的值
private void reversePrint(ListNode head) {if(head == null) return;reversePrint(head.next);System.out.println(head.val)
}

不难发现这种打印方式很像二叉树中的后序遍历
2. 对于链表的题目,思路往往很容易想到,只是过程可能有点复杂,一定要多画图,一定要舍得用变量

二.例题讲解

01.删除排序链表中重复的元素
链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/description/
分析

  • 这是链表去重的经典问题,有多种解法
  • 最容易想到的解法就是使用一个去重的数据结构(Set),遍历整个链表
  • 最优秀的解法是一个指针,一次遍历,注意题目条件,数组是有序的,那么重复元素一定是相邻的,每遍历到一个节点,就判断cur.val == cur.next.val,如果相等,就删除cur.next(完成一次删除操作);如果不等,直接让cur向后走一步即可

代码:

/*** 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) {if(head == null) return null;// 原地去重ListNode cur = head;while(cur.next != null) {if(cur.val ==cur.next.val) cur.next = cur.next.next;else cur = cur.next;// 有可能直接走到null}return head;}
}

说明:

  1. 循环的条件往往是根据下面的判断条件决定的,判断条件是cur.val == cur.next.val,如果cur.next ==null,则会触发空指针异常,所以循环的条件是cur.next != null,对于链表的最后一个元素,要么是重复元素,要么不是重复元素,如果是重复元素,则前一个节点的值和当前节点的值相等,在上一步就会执行删除操作;如果不是重复元素,不用删除
  2. 为什么存在重复元素的情况,删除重复元素之后不让cur走一步?因为有可能删除的是最后一个元素,这样cur.next = null,如果让cur向后走一步,在下一步的循环判断中就会触发空指针异常

删除重复元素的进阶版
链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/description/
分析

  • 本题相较于上一题,需要将所有的重复元素都删除,而上一题是只保留重复元素的一个
  • 同样也可以采用一个指针,一次遍历的操作,不过本题要删除所有的重复元素,就不能让指针走到重复元素的位置,应该走到第一个重复元素的前去节点,即判断cur.next.val == cur.next.next.val,如果相等,则一直循环删除所有等于cur.next.val(设为x)的所有节点,直到值不为x
  • 循环条件和判断条件的确立同上一题

代码:

/*** 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) {// 个人经验  使用一个指针更容易理解  反而维护多个指针容易把自己绕晕if(head == null) return null;// 一次遍历的思路ListNode phead = new ListNode(0);phead.next = head;ListNode cur = phead;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)// 一直走到值不等于x的节点cur.next = cur.next.next;}else {cur = cur.next;}}return phead.next;}
}

02.反转链表
链接:https://leetcode.cn/problems/reverse-linked-list/
分析

1.方法一:使用两个指针迭代完成局部的链表的反转

/*** 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 reverseList(ListNode head) {if(head == null) return null;ListNode pre = null, cur = head;while(cur != null) {ListNode tmp = cur.next;cur.next = pre;pre = cur;cur = tmp;}return pre;// pre此时是原链表的最后一个节点  反转链表的头结点}
}

2.方法2:递归写法

  • 你给我反转当前节点(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 reverseList(ListNode head) {if(head == null || head.next == null) return head;ListNode newHead = reverseList(head.next);// 完成一次局部的反转head.next.next = head;head.next = null;return newHead;}
}
  • 为什么返回newHead而不是head?因为newHead是完成反转之后的新的头结点

03.回文链表
链接:https://leetcode.cn/problems/palindrome-linked-list/description/
分析

方法1:栈
遇到对称有关的问题应该先考虑能否使用stack这种数据结构解决,对称问题最大的特点就是从前往后遍历的结果和从后往前遍历的结果相同,从前往后遍历容易,关键在于对于某些问题从后往前遍历很困难(比如单链表),这是就可以使用栈这种数据结构,充分利用栈后进先出的结构特点完成从后往前遍历

/*** 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 boolean isPalindrome(ListNode head) {// 借助栈Stack<Integer> st = new Stack<>();ListNode i1 = head;// 1.将所有元素入栈while(i1 != null) {st.push(i1.val);i1 = i1.next;}// 2.依次出栈进行比较ListNode i2 = head;while(i2 != null) {if(st.peek() != i2.val) return false;else {i2 = i2.next;st.pop();}}return true;}
}

方法2:反转后半部分链表,依次进行比较

  1. 利用快慢指针找到中间节点
  2. 根据fast是否为null判断节点的个数是奇数还是偶数,如果是奇数,向前走一步:如果是偶数,无需移动
  3. 反转后面的所有节点,依次向后比较
/*** 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 boolean isPalindrome(ListNode head) {ListNode fast = head, slow = head;// 通过快慢指针找到中点while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// 如果fast不为空,说明链表的长度是奇数个if (fast != null) {slow = slow.next;}// 反转后半部分链表slow = reverse(slow);fast = head;while (slow != null) {// 然后比较,判断节点值是否相等if (fast.val != slow.val)return false;fast = fast.next;slow = slow.next;}return true;}// 反转链表public ListNode reverse(ListNode head) {if(head == null || head.next == null) return head;ListNode newHead = reverse(head.next);head.next.next = head;head.next = null;return newHead;}}

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

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

相关文章

速盾:cdn节点作用?

CDN&#xff08;Content Delivery Network&#xff09;指的是内容分发网络&#xff0c;是一种通过部署在全球不同地理位置的服务器节点来提供快速、高效的内容传输和分发的技术架构。CDN节点在网络中的作用非常重要&#xff0c;下面就对其作用进行详细解析。 提供高速内容传输&…

《算法笔记》总结No.6——贪心

一.简单贪心 贪心法是求解一类最优化问题的方法&#xff0c;它总是考虑在当前状态下局部最优(或较优)之后&#xff0c;来使全局的结果达到最优(或较优)的策略。显然&#xff0c;如果采取较优而非最优的策略(最优策略可能不存在或是不易想到)&#xff0c;得到的全局结果也无法是…

socketserver和WSGI服务端实现教程

Python socketserver 和 WSGI 服务端实现教程 在本文中&#xff0c;我们将详细解析一个使用 socketserver 模块实现的简单 WSGI 服务器。该服务器能够处理 HTTP 请求&#xff0c;支持 WSGI 应用&#xff0c;并正确处理响应头和错误。 代码概述 这段代码定义了一个 run_wsgi …

【深入理解JVM】关于Object o = new Object()

1. 解释一下对象的创建过程 “半初始化”状态通常指的是对象在内存分配后、但在完全初始化之前的一种状态。在Java中&#xff0c;虽然JVM的规范和设计努力避免对象处于这种不稳定的状态&#xff0c;但在多线程环境下&#xff0c;由于指令重排序等并发问题&#xff0c;仍有可能…

Apache Spark详解

目录 性能优化 银行业务案例&#xff1a; 步骤1&#xff1a;环境准备和数据加载 步骤2&#xff1a;数据探索和预处理 步骤3&#xff1a;特征工程 步骤4&#xff1a;数据转换 步骤5&#xff1a;构建机器学习模型 步骤6&#xff1a;模型评估 步骤7&#xff1a;部署和监控…

Spring JdbcTemplate使用

maven引入Spring JDBC <dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.19</version></dependency> Spring配置中配置 <!-- DataSource配置 --><bean id"…

java代理简单理解

一、什么是代理 举例说明&#xff1a;当我想买一台电脑&#xff0c;国内太贵了。委托好友A在国外帮忙买。 这个情节中我要实现的动作和好友实现的动作一样&#xff0c;都是买电脑。好友帮我完成了这个动作&#xff0c;这就是代理。 类A和类B都实现一个interface接口C&#x…

【LeetCode刷题笔记】LeetCode.24.两两交换链表中的节点

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

新手小白的pytorch学习第一弹-------张量

1 导入pytorch包 import torch2 创建张量&#xff08;tensor&#xff09; scalar标量 scalar torch.tensor(7) scalartensor(7)scalar.ndim查看scalar的维度&#xff0c;因为scalar是标量&#xff0c;所以维度为0 0scalar.shapetorch.Size([])torch.item()7vector&#xf…

Apache功能配置:访问控制、日志分割; 部署AWStats日志分析工具

目录 保持连接 访问控制 只允许指定ip访问 拒绝指定主机其他正常访问 用户授权 日志格式 日志分割 操作步骤 使用第三方工具cronolog分割日志 AWStats日志分析 操作步骤 访问AwStats分析系统 保持连接 Apache通过设置配置文件httpd-default.conf中相关的连接保持参…

基于Java的科大讯飞大模型API调用实现

写在前面&#xff1a;因为现在自己实习的公司新拓展的一个业务是结合AI的低代码平台&#xff0c;我负责后端的开发&#xff0c;之前一直都是直接使用gpt或者文心一言等ui界面来直接使用大模型&#xff0c;从来没有自己调接口过&#xff0c;所以本文记录一下自己第一次使用大模型…

源代码防泄漏的正确方法

为了保护公司的源代码不被泄露&#xff0c;IT企业可以采取一系列严格的安全措施。这些措施涵盖技术手段、管理策略和操作流程&#xff0c;形成多层次的防护体系做到源代码防泄漏工作。 技术手段 1、源代码加密&#xff1a; 采用高级加密标准&#xff08;AES&#xff09;或其他…

【QT】QComboBox允许输入查询,且不区分大小写

目录 0.简介 1.环境 2.详细代码 3.参考 0.简介 项目需求&#xff0c;原本有一个下拉框&#xff0c;但是条目太多&#xff0c;不好搜索&#xff0c;所以用户要求可以输入查找 修改前 &#xff1a; 修改后&#xff1a; 1.环境 windows11 vs-code qt5.12 2.详细代码 QComboB…

中小企业和数智化的距离,只差一块华为IdeaHub

每次谈及中小企业数智化的话题&#xff0c;被提到最多的总是“三不”难题&#xff0c;即不想转、不敢转、不会转。 为了破解这一困局&#xff0c;政府多次在工作报告中提到“深入开展中小企业数字化赋能专项行动”&#xff0c;并在各地为中小企业创新提供政策支持。此外&#…

Android --- Kotlin学习之路:基础语法学习笔记

------>可读可写变量 var name: String "Hello World";------>只读变量 val name: String "Hello World"------>类型推断 val name: String "Hello World" 可以写成 val name "Hello World"------>基本数据类型 1…

MD5加密和注册页面的编写

MD5加密 1.导入包 npm install --save ts-md5 2.使用方式 import { Md5 } from ts-md5; //md5加密后的密码 const md5PwdMd5.hashStr("123456").toUpperCase(); 遇见的问题及用到的技术 注册页面 register.vue代码 <template><div class"wappe…

从零开始学习嵌入式----Linux 命令行,常用命令速记指南

目录 一、文件操作 二、文本操作 三、系统管理 四、网络操作 五、其他常用命令 六、学习建议 在 Linux 世界里&#xff0c;命令行就像一把瑞士军刀&#xff0c;掌握了它&#xff0c;你就能游刃有余地操控整个系统。但面对茫茫多的命令&#xff0c;新手往往会感到无所适从…

关于Python中的字典你所不知道的七个技巧

01 引言 Python是我最喜欢的编程语言之一&#xff0c;它向来以其简单性、多功能性和可读性而闻名。 字典作为Python中最常使用的数据类型&#xff0c;大家几乎每个人都或多或少在项目中使用过字典&#xff0c;但是字典里有一些潜在的技巧可能并不是每个同学都会用到。 在本文…

相同含义但不同类型字段作为join条件时注意事项

假设表A和表B中都有表示学号的stu_id字段&#xff0c;但该字段在表A和表B中类型分别为bigint和string。当直接通过该字段进行join时&#xff0c;一般情况下可以得到我们预期的结果。 select a.stu_id from a as r join b as l on r.stu_id l.stu_id 但是如果学号长度较长的…

【UE5.1 角色练习】16-枪械射击——瞄准

目录 效果 步骤 一、瞄准时拉近摄像机位置 二、瞄准偏移 三、向指定方向射击 四、连发 效果 步骤 一、瞄准时拉近摄像机位置 打开角色蓝图&#xff0c;在事件图表中添加如下节点&#xff0c;当进入射击状态时设置目标臂长度为300&#xff0c;从而拉近视角。 但是这样切…