当前位置: 首页 > article >正文

【算法训练营Day04】链表part2

文章目录

  • 两两交换链表中的节点
  • 删除链表的倒数第 N 个结点
  • 链表相交
  • 环形链表 II
  • 链表总结

两两交换链表中的节点

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

算法逻辑:

  • 添加一个虚拟头节点
  • 初始化一个交换指针,代表每次交换指针的后两个节点,该指针从虚拟头节点开始移动
  • 接下来就是交换步骤
    • 先将交换指针的后面第一个节点(简称后1节点)以及交换指针的后3节点存起来
    • 将交换指针的后2节点与交换指针节点进行缝合
    • 将存起来的后1节点与后2节点进行缝合
    • 在将存起来的后3节点与后1节点进行缝合
  • 如此交换直到交换指针节点为null,或者后1节点为null,或者后2节点为null,因为必须要有两个节点才能交换
/*** 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 swapPairs(ListNode head) {if(head == null || head.next == null) return head;ListNode virtualHead = new ListNode(-1,head);ListNode exchangePointer = virtualHead;while(exchangePointer != null && exchangePointer.next != null && exchangePointer.next.next != null) {ListNode temp1 = exchangePointer.next;ListNode temp3 = temp1.next.next;exchangePointer.next = exchangePointer.next.next;exchangePointer.next.next = temp1;exchangePointer.next.next.next = temp3;exchangePointer = exchangePointer.next.next;}return virtualHead.next;}
}

删除链表的倒数第 N 个结点

题目链接:19. 删除链表的倒数第 N 个结点

逻辑思路:
这道题既然是删除倒数的第n个节点,那么我们可以将链表反转,然后再删除第n个,最后再把链表反转回去即可。我们前几天的训练营中做过移除链表元素、翻转链表等算法题,所以这里直接把相关方法移植过来即可。

代码:

/*** 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 removeNthFromEnd(ListNode head, int n) {ListNode reverseList = reverseList(head);ListNode deleteList = deleteAtCount(reverseList,n);return reverseList(deleteList);}public ListNode reverseList(ListNode head) {ListNode reverseResultVirtualHead = new ListNode();ListNode pointer = head;while(pointer != null) {ListNode currentNode = new ListNode(pointer.val,null);currentNode.next = reverseResultVirtualHead.next;reverseResultVirtualHead.next = currentNode;pointer = pointer.next;}return reverseResultVirtualHead.next;}public ListNode deleteAtCount(ListNode head,int count) {ListNode virtualHead = new ListNode(-1,head);ListNode pointer = virtualHead;while(count > 0 && pointer.next != null) {if(count == 1) {pointer.next = pointer.next.next;}count -= 1;pointer = pointer.next;}return virtualHead.next;}
}

链表相交

题目链接:面试题 02.07. 链表相交

算法逻辑:
读完题目之后可以发现一个特点那就是若链表相交,那么必有公共后缀!我们若想找到两个链表的公共后缀那么第一想法肯定就是反向遍历。但是这是一个单向链表,直接反向遍历无法实现,这个时候我们就可以借助于栈结构来完成公共后缀的比对。

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA == null || headB == null) return null;ListNode pointerA = headA;ListNode pointerB = headB;Stack<ListNode> a = new Stack<ListNode>();Stack<ListNode> b = new Stack<ListNode>();while(pointerA != null) {a.push(pointerA);pointerA = pointerA.next;}while(pointerB != null) {b.push(pointerB);pointerB = pointerB.next;}ListNode nodeA = a.pop();ListNode nodeB = b.pop();ListNode result = null;while(nodeA == nodeB) {result = nodeA;if (a.empty() || b.empty()) break;nodeA = a.pop();nodeB = b.pop();}return result;}
}

环形链表 II

题目链接: 142. 环形链表 II

解题思路:

判断是否成环,换一句话说也就是在遍历链表的时候看是否有重复的节点,第一个重复的节点就是成环的起点。我们可以利用set数据结构,每遍历到一个新节点就看set中是否有相同节点,如果没有就添加到set中,如果有则说明该节点就是成环节点。遍历完了都没有重复节点,则说明不成环。

代码:

/*** Definition for singly-linked list.* class ListNode {*     int val;*     ListNode next;*     ListNode(int x) {*         val = x;*         next = null;*     }* }*/
public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> nodeSet = new HashSet();ListNode pointer = head;while(pointer != null) {if(nodeSet.contains(pointer)) return pointer;nodeSet.add(pointer);pointer = pointer.next;}return null;}
}

链表总结

链表的关键点就在于:

  • 增加、删除的时候可以引入虚拟头节点,让逻辑更加统一,不用单独考虑一些边界情况
  • 如果是遍历等不会修改链表结构的操作则无需引入虚拟头节点
  • 增加删除的重点就在于增加、删除元素的前后链要是可见的,才能完成相关的节点缝合工作。这就需要我们引入一些中间变量来存储这些前后链.
http://www.lryc.cn/news/2398368.html

相关文章:

  • 【ROS2】各种相关概念汇总解释
  • 解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)
  • Flowise 本地部署文档及 MCP 使用说明
  • YOLO学习笔记 | 一种用于海面目标检测的多尺度YOLO算法
  • 鸿蒙5.0项目开发——横竖屏切换开发
  • Triton推理服务器部署YOLOv8(onnxruntime后端和TensorRT后端)
  • TDengine 的 AI 应用实战——电力需求预测
  • NLP学习路线图(二十一): 词向量可视化与分析
  • 【分布式技术】KeepAlived高可用架构科普
  • 如何配置mvn镜像源为华为云
  • Linux平台排查CPU占用高的进程和线程指南
  • 多模态大语言模型arxiv论文略读(105)
  • 简述MySQL 超大分页怎么处理 ?
  • Pyhton中的命名空间包(Namespace Package)您了解吗?
  • Java设计模式之备忘录模式详解
  • Azure DevOps Server 2022.2 补丁(Patch 5)
  • 手摸手还原vue3中reactive的get陷阱以及receiver的作用
  • 小明的Java面试奇遇之互联网保险系统架构与性能优化
  • C++学习-入门到精通【13】标准库的容器和迭代器
  • C# 面向对象特性
  • ElasticStack技术之logstash介绍
  • 前端与后端
  • CI/CD 持续集成、持续交付、持续部署
  • 代码随想录60期day54
  • 关于easyx头文件
  • Java 中执行命令并使用指定配置文件的最佳实践
  • django入门-orm数据库操作
  • ​​食品电商突围战!品融电商全平台代运营,助您抢占天猫京东抖音红利!
  • Termux下如何使用MATLAB
  • STM32外部中断(EXTI)以及旋转编码器的简介