算法: 链表题目练习

文章目录

  • 链表题目练习
    • 两数相加
    • 两两交换链表中的节点
    • 重排链表
    • 合并 K 个升序链表
    • K 个一组翻转链表
  • 总结


链表题目练习

两数相加

在这里插入图片描述
坑:

  • 两个链表都遍历完后,可能需要进位.
    class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode cur1 = l1;ListNode cur2 = l2;ListNode head = new ListNode(0);ListNode tail = head;int sum = 0;while (cur1 != null || cur2 != null) {ListNode newNode = new ListNode();tail.next = newNode;tail = newNode;if (cur1 != null) {sum += cur1.val;cur1 = cur1.next;}if (cur2 != null) {sum += cur2.val;cur2 = cur2.next;}if (sum >= 10) {newNode.val = sum % 10;sum = 1;} else {newNode.val = sum;sum = 0;}}// 遍历完两个链表 处理一下进位if (sum != 0) {ListNode newNode = new ListNode(sum);tail.next = newNode;}return head.next;}}

题解代码:

    class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode cur1 = l1, cur2 = l2;ListNode head = new ListNode();ListNode tail = head;int sum = 0;while (cur1 != null || cur2 != null || sum != 0) {if (cur1 != null) {sum += cur1.val;cur1 = cur1.next;}if (cur2 != null) {sum += cur2.val;cur2 = cur2.next;}ListNode newNode = new ListNode(sum % 10);tail.next = newNode;tail = newNode;sum /= 10;}return head.next;}}

两两交换链表中的节点

在这里插入图片描述
简简单单,一遍过~

草图 :
在这里插入图片描述

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

重排链表

在这里插入图片描述
没想出来,看题解思路懂哩~

分成三步走:

  1. 找到原链表的中间节点(可以使用快慢指针解决)。
  2. 将原链表的右半部分翻转。
  3. 将原链表的两端合并。
    • 因为两链表的长度相差不超过 1,所以可以直接合并~

磕磕绊绊总算是写出来了~
看似是一道题,其实是三道题~

在合并两个链表时卡了一下.

坑:

  • 前面有指针指向 中间节点 ,链表翻转后指针的指向大概是这样的
    在这里插入图片描述
class Solution {public void reorderList(ListNode head) {// 寻找中间节点ListNode fast = head, slow = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// 此时 slow 为中间节点// 翻转 slow 以及 slow 后面的节点ListNode behind = new ListNode();ListNode cur = slow;while (cur != null) {ListNode next = cur.next;cur.next = behind.next;behind.next = cur;cur = next;}// 合并两个链表ListNode cur1 = head, cur2 = behind.next;while (cur2.next != null && cur1.next != null) {ListNode next1 = cur1.next, next2 = cur2.next;cur2.next = next1;cur1.next = cur2;cur1 = next1;cur2 = next2;}}
}

看了题解之后,发现可以把整个链表拆成两份.
而且,发现从中间节点的后一个开始翻转链表也可以过,这样就可以在中间节点这个位置把链表分成两份:

  • 一份是 中间节点之前(包含中间节点)
  • 另一份是 中间节点之后

题解代码:

    class Solution {public void reorderList(ListNode head) {// 1.寻找中间节点ListNode slow = head, fast = slow;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}ListNode head2 = new ListNode(-1);// 2.逆序 head2 链表ListNode cur = slow.next;// 拆分成两个链表slow.next = null;while (cur != null) {ListNode next = cur.next;cur.next = head2.next;head2.next = cur;cur = next;}// 3. 合并两个链表ListNode head3 = new ListNode(-1);ListNode cur2 = head;ListNode cur3 = head2.next;ListNode prev = head3;while (cur2 != null) {prev.next = cur2;prev = cur2;cur2 = cur2.next;if (cur3 != null) {prev.next = cur3;prev = cur3;cur3 = cur3.next;}}}}

合并 K 个升序链表

在这里插入图片描述
解法一: 不断合并两个链表

/*** 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 {private ListNode mergeLists(ListNode l1, ListNode l2) {if (l1 == null)return l2;if (l2 == null)return l1;ListNode head = new ListNode(-1);ListNode cur1 = l1, cur2 = l2, tail = head;while (cur1 != null && cur2 != null) {if (cur1.val <= cur2.val) {tail.next = cur1;tail = cur1;cur1 = cur1.next;} else {tail.next = cur2;tail = cur2;cur2 = cur2.next;}}while (cur1 != null) {tail.next = cur1;tail = cur1;cur1 = cur1.next;}while (cur2 != null) {tail.next = cur2;tail = cur2;cur2 = cur2.next;}return head.next;}public ListNode mergeKLists(ListNode[] lists) {int n = lists.length;if (n <= 0)return null;ListNode head = lists[0];for (int i = 1; i < n; i++) {head = mergeLists(head, lists[i]);}return head;}
}

方法二: 使用优先级队列优化.

  • 给每个链表都指定一个指针(用来遍历链表),把每一个指针指向的节点放到优先级队列里.不断取出值最小的那个节点,尾插到结果链表中.

忘了怎么在java中自定义排序优先级队列了。

/*** 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 mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0)return null;// 指针数组ListNode[] arr = new ListNode[n];// 默认是小根堆PriorityQueue<ListNode> heap = new PriorityQueue<>(new Comparator<ListNode>() {@Overridepublic int compare(ListNode o1, ListNode o2) {return o1.val - o2.val;}});// 结果ListNode ret = new ListNode(-1);ListNode tail = ret;// 把指针对应起来for (int i = 0; i < n; i++) {arr[i] = lists[i];}for (int i = 0; i < n; i++) {if (arr[i] != null) {heap.add(arr[i]);}}while (!heap.isEmpty()) {// 最小的出堆ListNode min = heap.poll();// 拼到结果后面tail.next = min;tail = tail.next;if (min.next != null) {// 不为空,入堆heap.add(min.next);}}return ret.next;}
}

看了题解代码后,发现自己写的代码浪费了很多空间,我为什么要 new 一个指针数组???

题解代码:

        /*** 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 mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0)return null;// 1. 创建一个小根堆PriorityQueue<ListNode> heap = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);// 2. 把所有的头结点放进小根堆中for (ListNode head : lists) {if (head != null)heap.offer(head);}// 3.合并链表ListNode ret = new ListNode(-1);ListNode tail = ret;while (!heap.isEmpty()) {// 最小的出堆ListNode min = heap.poll();// 拼到结果后面tail.next = min;tail = tail.next;if (min.next != null) {// 不为空,入堆heap.add(min.next);}}return ret.next;}}

方法三:使用 分治 - 递归 解决

好难想到。

代码:

        /*** 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 mergeLists(ListNode l1, ListNode l2) {if (l1 == null)return l2;if (l2 == null)return l1;ListNode head = new ListNode(-1);ListNode tail = head;while (l1 != null && l2 != null) {if (l1.val <= l2.val) {tail.next = l1;l1 = l1.next;} else {tail.next = l2;l2 = l2.next;}tail = tail.next;}while (l1 != null) {tail.next = l1;l1 = l1.next;tail = tail.next;}while (l2 != null) {tail.next = l2;l2 = l2.next;tail = tail.next;}return head.next;}// 递归public ListNode merge(ListNode[] lists, int start, int end) {if (start >= end)return lists[start];int mid = start + (end - start) / 2;ListNode l1 = merge(lists, start, mid);ListNode l2 = merge(lists, mid + 1, end);return mergeLists(l1, l2);}public ListNode mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0)return null;return merge(lists, 0, n - 1);}}

自己的代码中的合并两个有序链表的代码写的不是很好,最后的 while 可以换成 if 来写的,这是链表,不是数组,不用循环那么多次。。

题解代码:

        /*** 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 mergeLists(ListNode l1, ListNode l2) {if (l1 == null)return l2;if (l2 == null)return l1;ListNode head = new ListNode(-1);ListNode tail = head;while (l1 != null && l2 != null) {if (l1.val <= l2.val) {tail.next = l1;l1 = l1.next;} else {tail.next = l2;l2 = l2.next;}tail = tail.next;}if (l1 != null)tail.next = l1;if (l2 != null)tail.next = l2;return head.next;}// 递归public ListNode merge(ListNode[] lists, int start, int end) {if (start >= end)return lists[start];// 1. 平分数组int mid = start + (end - start) / 2;// 2. 递归处理左右两个部分ListNode l1 = merge(lists, start, mid);ListNode l2 = merge(lists, mid + 1, end);// 3. 合并两个有序链表return mergeLists(l1, l2);}public ListNode mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0)return null;return merge(lists, 0, n - 1);}}

K 个一组翻转链表

在这里插入图片描述

自己写的代码:

/*** 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 reverse(ListNode head, int k) {ListNode phead = new ListNode(-1);ListNode cur = head, next = cur.next;while (k-- > 0) {cur.next = phead.next;phead.next = cur;cur = next;if (next != null)next = next.next;elsebreak;}return phead.next;}public ListNode reverseKGroup(ListNode head, int k) {ListNode phead = new ListNode(-1);phead.next = head;ListNode slow = phead, fast = head;while (fast != null) {int tmp = k;while (tmp > 0) {if (fast == null) {break;}fast = fast.next;tmp--;}if (tmp > 0)break;slow.next = reverse(slow.next, k);tmp = k;// 写成这样你要清楚:// 等出循环的时候 tmp = -1// 因为在最后一次的判断时 tmp 也要 --while (tmp-- > 0) {slow = slow.next;}slow.next = fast;}return phead.next;}
}

题解代码:

/*** 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 reverseKGroup(ListNode head, int k) {// 1. 先求出要逆序多少组int n = 0;ListNode cur = head;while (cur != null) {cur = cur.next;n++;}n /= k;// 2. 重复 n 次,长度为 k 的链表的逆序ListNode newHead = new ListNode(-1);ListNode prev = newHead;cur = head;for (int i = 0; i < n; i++) {// 标记当前逆序后的最后一个节点ListNode tmp = cur;for (int j = 0; j < k; j++) {ListNode next = cur.next;cur.next = prev.next;prev.next = cur;cur = next;}prev = tmp;}// 处理剩下的节点不够 k 个的情况prev.next = cur;return newHead.next;}
}

总结

链表常用技巧 :

  1. 画图是个好东西(感觉好像已经说过好几遍了).
  2. 可以引入一个头结点
    • 便于处理边界情况
    • 方便我们对链表操作
  3. 在插入新节点时,可以先把新节点的指针指向都调整好.然后再去调整前一个节点和后一个节点.
    或者直接新建一个指针,指向后一个节点,这样更容易操作~
  4. 有时候会用到快慢双指针

本文到这里就结束啦~

在这里插入图片描述

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

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

相关文章

js WebAPI黑马笔记(万字速通)

此笔记来自于黑马程序员&#xff0c;pink老师yyds 复习&#xff1a; splice() 方法用于添加或删除数组中的元素。 注意&#xff1a; 这种方法会改变原始数组。 删除数组&#xff1a; splice(起始位置&#xff0c; 删除的个数) 比如&#xff1a;1 let arr [red, green, b…

【Pikachu靶场:XSS系列】xss之过滤,xss之htmlspecialchars,xss之herf输出,xss之js输出通关啦

一、xss之过滤 <svg onloadalert("过关啦")> 二、xss之htmlspecialchars javascript:alert(123) 原理&#xff1a;输入测试文本为herf的属性值和内容值&#xff0c;所以转换思路直接变为js代码OK了 三、xss之href输出 JavaScript:alert(假客套) 原理&#x…

JS装备智能化储备管理体系优化改革

现代化的JS仓储管理方案&#xff0c;通过整合先进的RFID技术与三维模拟技术&#xff0c;为JS物流领域开创了新颖的改革浪潮。以下是对这两项尖端技术融合并用于战备物资管理的应用概述&#xff1a; 一、RFID技术在JS物资管理中的实践 RFID技术依靠无线电波实现无需直接接触的数…

缓存淘汰策略:Redis中的内存管理艺术

在现代应用架构中&#xff0c;缓存是提升性能的关键组件。 Redis&#xff0c;作为一个高性能的键值存储系统&#xff0c;因其快速的数据访问能力而被广泛使用。然而&#xff0c;由于物理内存的限制&#xff0c;Redis必须在存储空间和性能之间找到平衡&#xff0c;这就引出了缓…

AUTOSAR COM 与 LargeDataCOM 模块解析及 C++ 实现示例

AUTOSAR COM 和 LargeDataCOM 模块在功能和使用场景上有一些显著的区别。以下是它们的主要区别及具体的应用示例,最后用 C++ 源代码来解析说明。 AUTOSAR COM 模块 • 功能:主要用于处理标准大小的信号和 I-PDU(协议数据单元),提供了信号打包、解包、数据传输和接收等功能…

JavaWeb复习

在网络应用程序中有两种基本的结构&#xff0c;即C/S和B/S&#xff0c;对于c/s程序分为客户机和服务器两层&#xff0c;把应用软件按照在客户机端(通常由客户端维护困难)&#xff0c;通过网络与服务器进行相互通信。B/S结构却不用通知客户端安装某个软件&#xff0c;内容修改了…

qt获取本机IP和定位

前言&#xff1a; 在写一个天气预报模块时&#xff0c;需要一个定位功能&#xff0c;在网上翻来翻去才找着&#xff0c;放在这里留着回顾下&#xff0c;也帮下有需要的人 正文&#xff1a; 一开始我想着直接调用百度地图的API来定位&#xff0c; 然后我就想先获取本机IP的方…

python爬取旅游攻略(1)

参考网址&#xff1a; https://blog.csdn.net/m0_61981943/article/details/131262987 导入相关库&#xff0c;用get请求方式请求网页方式&#xff1a; import requests import parsel import csv import time import random url fhttps://travel.qunar.com/travelbook/list.…

Oracle OCP认证考试考点详解082系列12

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 56. 第56题&#xff1a; 题目 解析及答案&#xff1a; 关于企业管理器&#xff08;EM&#xff09;Express&#xff0c;以下哪两个陈述是…

Postgresql源码(137)执行器参数传递与使用

参考 《Postgresql源码&#xff08;127&#xff09;投影ExecProject的表达式执行分析》 0 总结速查 prepare p_04(int,int) as select b from tbl_01 where a $1 and b $2为例。 custom计划中&#xff0c;在表达式计算中使用参数的值&#xff0c;因为custom计划会带参数值&…

SPI通信详解-学习笔记

参考原文地址 SPI&#xff1a;高速、全双工&#xff0c;同步、通信总线 SPI主从模式 SPI分为主、从两种模式&#xff0c;一个SPI通讯系统需要包含一个&#xff08;且只能是一个&#xff09;主设备&#xff0c;一个或多个从设备。提供时钟的为主设备&#xff08;Master&#xff…

Day102漏洞发现-漏扫项目篇Poc开发Yaml语法插件一键生成匹配结果交互提取

知识点&#xff1a; 1、Nuclei-Poc开发-环境配置&编写流程 2、Nuclei-Poc开发-Yaml语法&匹配提取 3、Nuclei-Poc开发-BurpSuite一键生成插件 Nuclei-Poc开发-环境配置&编写流程 1、开发环境&#xff1a;VscodeYaml插件 Visual Studio Code - Code Editing. R…

Node.js 入门指南:从零开始构建全栈应用

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;node.js篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来node.js篇专栏内容:node.js-入门指南&#xff1a;从零开始构建全栈应用 前言 大家好&#xff0c;我是青山。作…

WordPress网站添加嵌入B站视频,自适应屏幕大小,取消自动播放

结合bv号 改成以下嵌入式代码&#xff08;自适应屏幕大小,取消自动播放&#xff09; <iframe style"width: 100%; aspect-ratio: 16/9;" src"//player.bilibili.com/player.html?isOutsidetrue&bvidBV13CSVYREpr&p1&autoplay0" scrolling…

大模型应用系列:Query 变换的示例浅析

【引】NLP中的经典组件在大模型应用中还有效么&#xff1f;大模型对自然语言处理中的典型任务有什么影响么&#xff1f; RAG应用通过分割文档、嵌入向量化并检索高语义相似性的块来响应用户问题&#xff0c;但面临文档块不相关、用户用词不当及结构化查询需求等问题。若RAG无法…

【Oracle】空格单字符通配符查询匹配失败

问题 在进行模糊查询的时候&#xff0c;通过全局任意字符串匹配出含有两个字刘姓的人&#xff0c;但是通过刘_不能匹配出结果。 解决 检查后发现&#xff0c;姓名中包含空格 SELECT * FROM student WHERE TRIM(sname) LIKE 刘_;第一种解决方案就是查询的时候进行去空格处理&a…

讲讲⾼并发的原则?

大家好&#xff0c;我是锋哥。今天分享关于【讲讲⾼并发的原则&#xff1f;】面试题。希望对大家有帮助&#xff1b; 讲讲⾼并发的原则&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 高并发是指系统在同一时间内能够处理大量请求的能力。要有效地管理…

鸿蒙进阶-AlphabetIndexer组件

大家好&#xff0c;这里是鸿蒙开天组&#xff0c;今天我们来学习AlphabetIndexer组件&#xff0c;喜欢就点点关注吧&#xff01; 通过 AlphabetIndexer 组件可以与容器组件结合&#xff0c;实现导航联动&#xff0c;以及快速定位的效果 核心用法 AlphabetIndexer不是容器组件…

ubuntu交叉编译expat库给arm平台使用

1.下载expat库源码: https://github.com/libexpat/libexpat/release?page=2 wget https://github.com/libexpat/libexpat/release/download/R_2_3_0/expat-2.3.0.tar.bz2 下载成功: 2.解压expat库,并进入解压后的目录: tar xjf expat-2.3.0.tar.bz2 cd expat-2.3.0 <…

【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、锁分类

目录 一、问题综述 1. 进程和线程的区别&#xff1f; 2. 进程的状态有哪些&#xff1f; 3. 进程之间的通信方式? &#xff08;1&#xff09;管道 &#xff08;2&#xff09;消息队列 &#xff08;3&#xff09;共享内存 &#xff08;4&#xff09;信号量 &#xff08…