前提知识:

1.画图,数据结构相关的题,画图必不可少,只要能画出来,那么后面的代码就很容易能写出来,因为将抽象的数据结构转换为直观的图画

2.引入虚拟头结点,也叫哨兵位,能够避免考虑很多边界情况

3.不要吝啬空间,如果你看到第二点的时候,如果反应是有些题也可以不用虚拟节点就能解决时,其实这就已经是有点吝啬空间的思想了,一个虚拟头结点才几个字节,根本不需要考虑,大胆创建就行了

4.快慢双指针,链表中经常且好用的方法

5.链表常用存在,创建节点,头插,尾插,其中头插是比较重要的,因为通过头插可以直接完成逆序链表的操作,而很多题目中都需要逆序链表,所以头插的操作要掌握,同时又结合虚拟头结点,那么头插起来会非常方便

题目一:

算法原理:

题意很简单,就是模拟加法的操作,只不过链表是逆序的,但是不要看是逆序的就觉得有难度,如果是正序的反而要更难一点,因为加法是从低位开始计算的,而逆序链表刚好就把低位放在了头结点,反而更好操作,而正序链表如果要进行加法,反而要先逆序再操作

思路就是用两个指针指向两个链表的头结点,然后开始往后遍历,将两数的和相加并记录在一个变量t里面,最后该位只取t的个位,然后/=10即可,而循环遍历结束条件是两个指针都为空时,且t也为0才结束,其中为什么t也要为0是解决下面这种情况

 即虽然两个指针为空,但还有一个进位没有进

也是可以用虚拟头结点,直接进行加法操作后,接在虚拟头结点之后就行

代码:

class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//虚拟头结点ListNode newHead=new ListNode(0);//双指针ListNode cur1=l1,cur2=l2,cur=newHead;//记录进位int t=0;//循环遍历while(cur1!=null||cur2!=null||t!=0){//如果链表1还有值if(cur1!=null){t+=cur1.val;cur1=cur1.next;}//如果链表2还有值if(cur2!=null){t+=cur2.val;cur2=cur2.next;}//进位操作cur.next=new ListNode(t%10);cur=cur.next;t/=10;}//返回真正的头结点return newHead.next;}
}

题目二:

算法原理:

题意很简单,注意不能修改原结点的值,只能通过移动结点进行修改

如果还是用之前刚学链表时的思想,那就是不创建虚拟结点,同时也吝啬空间不定义变量,还不画图的话,那就脑袋里面慢慢绕了,一不小心就绕混了

因为如果不创建虚拟结点的话,12这两个结点的操作和34之间的操作是不一样的,34这里需要1这个结点指向4,而12这里没有前面的结点,因此需要自己找头结点,导致12的时候要特殊处理,不能进入循环,而创建虚拟头结点就可以让12操作和后面一样

如果创建虚拟结点后并画图,但吝啬空间的话,那么结点交换和修改就得考虑顺序且非常乱

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

 这循环里面的代码顺序不能乱调,且一眼看过去也很乱

而直接定义变量的话就会这样

那么逻辑就直接是

prev指向next

next指向cur

cur指向nnext

且先后顺序随便,根本不影响

然后再修改变量,这里需要注意顺序,不然会出现覆盖

非常清晰

然后讨论循环终止条件

偶数结点情况下:

可以发现,如果cur==null就终止

奇数结点情况下:

 可以发现,如果next==null就终止

代码:

class Solution {public ListNode swapPairs(ListNode head) {//特殊处理空结点和单结点if(head==null||head.next==null){return head;}ListNode newHead=new ListNode(0,head);ListNode prev=newHead,cur=head,next=cur.next,nnext=next.next;while(cur!=null&&next!=null){//修改结点指向prev.next=next;next.next=cur;cur.next=nnext;//修改变量(注意顺序)prev=cur;cur=nnext;if(cur!=null){next=cur.next;}if(next!=null){nnext=next.next;}}return newHead.next;}
}

题目三:

算法原理:

这道题比较综合,通过模拟可以发现,无非是将前一半链表和后一半经过逆序的链表,进行合并即可,因此就涉及到三个知识点,找链表中间结点,逆序操作,合并链表

找链表中间结点,采用快慢双指针来解决

逆序链表,采用头插来解决

合并链表,模拟操作即可

需要注意的是,找到中间结点后,要将前后两个链表之间进行切断,实现两个独立的链表,才好方便进行后面的操作

代码:

class Solution {public void reorderList(ListNode head) {if(head.next==null){return;}//找到中间结点ListNode slow=head;ListNode fast=head;while(fast!=null&&fast.next!=null){slow=slow.next;fast=fast.next.next;}//拆开链表ListNode cur=head;while(cur.next!=slow){cur=cur.next;}cur.next=null;cur=head;//逆序链表ListNode newHead=new ListNode();while(slow!=null){ListNode slowNext=slow.next;slow.next=newHead.next;newHead.next=slow;slow=slowNext;}//合并链表newHead=newHead.next;while(cur!=null&&newHead!=null){ListNode curNext=cur.next;ListNode newHeadNext=newHead.next;cur.next=newHead;if(curNext!=null){newHead.next=curNext;  }   cur=curNext;newHead=newHeadNext;}}
}

题目四:

 算法原理:

题意很简单,就是按照升序合并k个链表,而且还比较好心的帮我们把k个链表进行了升序操作

我们之前学过合并2个链表,所以最容易想到的就是暴力解法,即遍历数组,第1个链表和第2个链表合并之后,再和第3个链表合并……

时间复杂度上,假设有k个链表,每个链表长度为n

合并的时间复杂度是n(k-1)+n(k-2)……+n=O(nk^2)=O(N^3)

效率非常低,所以要换一个方法

方法一:

既然是比较大小进行排序,那么就可以借助优先级队列来实现,将每个链表的头结点都扔进去,取出堆顶元素,就是所有头结点中最小的,然后对应的那个链表就扔下一个结点进去,然后再取堆顶元素,循环往复,直到对应链表为空,则停止添加,最后当堆为空时,则合并完成

时间复杂度上,堆的操作为log级别,有k个链表,所以要比较k次,又总共有n个节点,所以时间复杂度为O(n k logk)

代码一(优先级队列):

class Solution {public ListNode mergeKLists(ListNode[] lists) {//如果数组为空或者数组中的链表为空if(lists==null||lists.length==0){return null;}//创建一个小根堆PriorityQueue<ListNode> priorityQueue=new PriorityQueue<>((o1,o2)->o1.val-o2.val);//取出所有的头结点放进去for(ListNode node:lists){if(node!=null){priorityQueue.offer(node);}}//创建一个虚拟节点ListNode newHead=new ListNode();ListNode prev=newHead;//合并链表while(!priorityQueue.isEmpty()){ListNode cur=priorityQueue.poll();prev.next=cur;prev=cur;//如果该链表为空则不添加if(cur.next!=null){priorityQueue.offer(cur.next);}}//返回头结点return newHead.next;}
}

方法二:

既然合并k个链表可以拆分为n次合并2个链表,那么子问题是一样的,就可以采用分治递归的思想,以归并排序来解决,套模板即可

代码二(分治递归):

class Solution {public ListNode mergeKLists(ListNode[] lists) {//要求对lists数组从下标0开始到lists.length-1之间的链表进行合并并返回头结点return merge(lists,0,lists.length-1);}public ListNode merge(ListNode[] lists,int left,int right){//如果为空区间if(left>right){return null;}//如果只有一个链表,不用合并if(left==right){return lists[left];}//找到中间值,分为[left,mid],[mid+1,right]两个区间int mid=(left+right)/2;//递归处理左右两部分ListNode l1=merge(lists,left,mid);ListNode l2=merge(lists,mid+1,right);//合并两个链表ListNode newHead=new ListNode();ListNode prev=newHead;while(l1!=null&&l2!=null){if(l1.val>=l2.val){prev.next=l2;prev=l2;l2=l2.next;}else{prev.next=l1;prev=l1;l1=l1.next;}}if(l1!=null){prev.next=l1;}if(l2!=null){prev.next=l2;}//返回头结点return newHead.next;}
}

题目五:

算法原理:

题意很简单,就是不停翻转长度为k的链表,直到全部翻转完或者剩余长度不够k则停止

虽然是困难题,但是模拟的操作并不难

首先我们先遍历一遍链表,统计出链表的长度,然后再除k看看有多少组需要被翻转,假设为n组

然后循环n次,每次循环代表每一组,循环里面再嵌套循环k次,表示将k个结点进行翻转

最后拼接上后面未被翻转的结点

翻转也就是逆序,所以我们采用之前用的头插法

其中需要注意的是

每次头插翻转完一组后,后面结点跟的是第一次头插的结点后面

代码:

class Solution {public ListNode reverseKGroup(ListNode head, int k) {//如果翻转长度为1,则不用翻转if(k==1){return head;}//遍历链表统计长度ListNode cur=head;int len=0;while(cur!=null){cur=cur.next;len++;}//如果长度不够k,直接返回if(len<k){return head;}int n=len/k;//头插翻转cur=head;ListNode newHead=new ListNode(0);ListNode prev=newHead;ListNode tmp=null;//翻转n组for(int i=0;i<n;i++){//记录下一组的前驱结点tmp=cur; for(int j=0;j<k;j++){   ListNode next=cur.next;                  cur.next=prev.next;prev.next=cur;cur=next;}//更新前驱节点prev=tmp;}//拼接剩余节点prev.next=cur;//返回头结点return newHead.next;}
}

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

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

相关文章

零基础学后端-PHP语言(第一期-PHP环境配置)

从本期开始&#xff0c;我们学习PHP&#xff0c;但是我们要先配置PHP环境 PHP官网链接&#xff1a;PHP For Windows: Binaries and sources Releases 我们可以看到有以下资源 可以看到有很多php的版本&#xff0c;有Non Thread Safe和Thread Safe&#xff0c;还有zip&#xf…

C++ primer知识点总结

《C Primer》系统学习指南&#xff1a;从C到C的平滑过渡根据你提供的《C Primer》目录和你的需求&#xff08;C语言背景转C&#xff0c;侧重网络编程&#xff09;&#xff0c;我将为你制定一个全面的学习计划&#xff0c;包含知识点详解、C/C对比、实战案例和分阶段项目练习。第…

异构融合 4A:重构高性能计算与复杂场景分析的安全与效率边界

当全球数据量以每两年翻一番的速度爆炸式增长&#xff0c;高性能计算&#xff08;HPC&#xff09;与复杂场景分析正成为破解气候预测、基因测序、金融风控等世界级难题的关键引擎。但异构计算环境的碎片化、多系统协同的复杂性、数据流动的安全风险&#xff0c;正在形成制约行业…

【华为机试】240. 搜索二维矩阵 II

文章目录240. 搜索二维矩阵 II描述示例 1示例 2提示解题思路核心分析问题转化算法实现方法1&#xff1a;右上角开始搜索&#xff08;推荐&#xff09;方法2&#xff1a;逐行二分查找方法3&#xff1a;分治法方法4&#xff1a;左下角开始搜索复杂度分析核心要点数学证明右上角搜…

疯狂星期四文案网第16天运营日记

网站运营第16天&#xff0c;点击观站&#xff1a; 疯狂星期四 crazy-thursday.com 全网最全的疯狂星期四文案网站 运营报告 昨日访问量 昨日30多ip, 今天也差不多&#xff0c;同步上周下降了一些&#xff0c;感觉明天疯狂星期四要少很多了&#xff0c;记得上周四700多ip&…

Linux系统基础入门与配置指南

Linux基本概述与配置 一、我们为什么使用Linux&#xff08;Linux的优点&#xff09;开源与自由 免费&#xff1a; 无需支付许可费用&#xff0c;任何人都可以自由下载、安装和使用。源代码开放&#xff1a; 任何人都可以查看、修改和分发源代码。这带来了极高的透明度、安全性和…

如何删除VSCode Marketplace中的publisher

网页上并没有提供删除的按钮&#xff0c;需要通过命令的形式删除。 vsce delete-publisher [要删除的名字]# 键入token # y 确认这里的token是之前在Azure DevOps中创建的token&#xff0c;忘了的话可以重建一个 刷新网页看一下 成功删除了。

Windows安装git教程(图文版)

Git 是一个分布式版本控制系统&#xff0c;用于跟踪文件的变化&#xff0c;特别是在软件开发中。它使得多个开发者可以在不同的机器上并行工作&#xff0c;然后将他们的改动合并在一起。是在开发过程中&#xff0c;经常会用到的一个工具。本章教程&#xff0c;主要介绍Windows上…

Remote Framebuffer Protocol (RFB) 详解

RFC 6143 规范文档&#xff1a;The Remote Framebuffer Protocol 文章目录1. 引言2. 初始连接流程2.1 TCP连接建立2.2 协议版本协商2.3 安全握手3. 显示协议机制3.1 核心概念3.2 像素格式4. 输入协议4.1 键盘事件(KeyEvent)4.2 鼠标事件(PointerEvent)5. 协议消息详解5.1 握手消…

从 DeepSeek-V3 到 Kimi K2:八种现代大语言模型架构设计

编译&#xff1a;青稞社区Kimi 原文&#xff1a;https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison 首发&#xff1a;https://mp.weixin.qq.com/s/lSM2jk1UxJVz1WllWYQ4aQ 自原始 GPT 架构开发以来已经过去了七年。乍一看&#xff0c;从 2019 年的…

linux驱动开发笔记--GPIO驱动开发

目录 前言 一、设备树配置 二、驱动编写 三、用户空间测试 总结 前言 开发平台&#xff1a;全志A133&#xff0c;开发环境&#xff1a;linux4.9andrio10&#xff0c;开发板&#xff1a;HelperBoard A133_V2.5。 一、设备树配置 打开板级设备树配置文件&#xff0c;路径&a…

腾讯iOA:企业软件合规与安全的免费守护者

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 目录 一、为什么要使用腾讯iOA&#xff1f; 二、中小企业软件合规痛点 三、腾讯iOA解决方案 3.1 核心技…

C#定时任务实战指南:从基础Timer到Hangfire高级应用

高效管理后台作业&#xff0c;让定时任务成为应用的可靠引擎 在C#应用开发中&#xff0c;定时任务是实现数据同步、报表生成、系统维护等后台作业的核心技术。本文将深入探讨C#生态中主流的定时任务解决方案&#xff0c;从基础的内置Timer到强大的Quartz.NET和Hangfire框架&…

软件开发、项目开发基本步骤

• 立项阶段&#xff1a;项目定义、需求收集与分析、可行性分析、风险评估与规划、项目团队组建、制定项目计划、获取批准与支持。• 需求评审与分析&#xff1a;◦ 项目团队&#xff08;包括产品经理、开发人员、测试人员等&#xff09;共同参与&#xff0c;明确项目的目标、功…

慢 SQL接口性能优化实战

在对某电商项目进行接口性能压测时&#xff0c;发现 /product/search 接口响应缓慢&#xff0c;存在明显性能瓶颈。通过慢查询日志排查和 SQL 优化&#xff0c;最终实现了接口响应速度的显著提升。本文完整还原此次优化过程&#xff0c;特别强调操作步骤和问题分析过程&#xf…

【C#】在WinForms中实现控件跨TabPage共享的优雅方案

文章目录一、问题背景二、基本实现方案1. 通过修改Parent属性实现控件移动三、进阶优化方案1. 创建控件共享管理类2. 使用用户控件封装共享内容四、方案对比与选择建议五、最佳实践建议六、完整示例代码一、问题背景 在Windows窗体应用程序开发中&#xff0c;我们经常遇到需要…

Android Camera openCamera

由头 今日调休&#xff0c;终于终于闲下来了&#xff0c;可以写一下博客了&#xff0c;刚好打开自己电脑&#xff0c;就有四年前下的谷歌Android 12源码&#xff0c;不是很旧&#xff0c;刚好够用&#xff0c;不用再另外下载新源码了&#xff0c;不得不感慨这时间过得真快啊~废…

神经网络——池化层

目录 池化层 最大池化层 MaxPool2d 最大池化操作图示 最大池化操作代码演示 综合代码案例 池化层 池化层&#xff08;Pooling Layer&#xff09; 核心作用&#xff1a;通过降采样减少特征图尺寸&#xff0c;降低计算量&#xff0c;增强特征鲁棒性。 1. 常见类型 …

Android 默认图库播放视频没有自动循环功能,如何添加2

Android 默认图库播放视频没有自动循环功能, 如何添加 按如下方式修改可以添加 开发云 - 一站式云服务平台 --- a/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java +++ b/packages/apps/Gallery2/src/com/android/gallery3d/app/MovieActivity.java…

数字孪生赋能智慧能源电力传输管理新模式

在“双碳”战略和能源数字化转型的双重驱动下&#xff0c;智慧能源系统亟需更高效、精细和智能的管理手段。数字孪生技术作为融合物理世界与数字空间的桥梁&#xff0c;为电力传输系统的全生命周期管理提供了强有力的技术支撑。本文聚焦数字孪生在智慧能源电力传输中的应用&…