5.<tag-链表和快慢针>lt.19-删除链表的倒数第 N 个结点 + lt.160-相交链表 + 剑指 Offer 22. 链表中倒数第k个节点 1
<tag-数组和二分查找_15>-lt.xx-xxxxxx + lt.xx-xxxxxx
lt.19-删除链表的倒数第 N 个结点
[案例需求]
[思路分析一, 学弱解法]
- 学弱解法: 先遍历一遍链表得到链表的结点数量count , 再去遍历链表到倒数第n个(整数第count -n 个的下一个)结点的前驱结点(count - n)的位置, 删除待删除的结点即可.
- 注意: 删除链表时最重要的是设置头结点 dummyNode, 这样使得首元结点(head)的删除跟其他结点的删除操作保持一致(都有前驱结点, 能够使用 temp.next = temp.next.next)
- 第一次遍历可以得知链表的长度L,第二次遍历从头找到L-n位置的链表元素,删除即可。两次遍历,时间复杂度为O(n), 空间复杂度O(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 removeNthFromEnd(ListNode head, int n) {
int size = listSize(head);
int count = size - n;
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode temp = dummyNode;
while(true){
if(temp.next == null)return null;
if(count == 0)break;
temp = temp.next;
--count;
}
temp.next = temp.next.next;
//System.out.println(size);
return dummyNode.next;
}
public int listSize(ListNode head){
int count = 0;
ListNode temp = head;
while(true){
if(temp == null)return count;
++count;
temp = temp.next;
}
}
}
[思路分析, 解法二, 快慢针]
- 我们设置两个指针,
- 两个指针初始状态都指向dummyNode, 指针fast先走n步, slow不动(让fast 与 slow的步长等于 n)
- 然后指针fast和指针slow同步向前继续遍历链表, 直到fast 的和后续结点为空, 此时slow到达被删除结点的前驱结点
- 对slow后的结点实行删除结点即可.
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode fast = dummyNode;
ListNode slow = dummyNode;
int count = 0;
while(count < n){
fast = fast.next;
++count;
}
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyNode.next;
}
}
160. 相交链表
[案例需求]
[思路分析一, 学弱解法]
- 把其中一条链表放入集合中, 需要对这一条链表进行遍历
- 然后遍历第二条链表, 遍历的同时跟集合中结点进行比较, 在遍历到第二条链表末尾之前, 如果集合包含了第二条链表中的某个节点, 则返回这个节点, 否则就返回空.
///1. 普通解法, hashset存储. 比较
/**
* 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) {
Set<ListNode> listA = new HashSet<>();
ListNode tempA = headA;
while(true){
if(tempA == null)break;
listA.add(tempA);
tempA = tempA.next;
}
//遍历链表B
// ListNode dummyNode = new ListNode(-1);
// dummyNode.next = headB;
ListNode tempB = headB;
while(true){
if(tempB == null)return null;
if(listA.contains(tempB))return tempB;
tempB = tempB.next;
}
}
}
- 时空复杂度
[思路分析二, 两条链表交替遍历]
pA走过的路径为A链+B链
pB走过的路径为B链+A链
pA和pB走过的长度都相同,都是A链和B链的长度之和,相当于将两条链从尾端对齐,如果相交,则会提前在相交点相遇,如果没有相交点,则会在最后相遇。
若相交,链表A: a+c, 链表B : b+c.
a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。
若不相交,a +b = b+a 。因此相遇处是NULL
pA:1->2->3->4->5->6->null->9->5->6->null
pB:9->5->6->null->1->2->3->4->5->6->null
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pA = headA;
ListNode pB = headB;
while(pA != pB){
pA = pA != null ? pA.next : headB;
pB = pB != null ? pB.next : headA;
}
return pA;
}
}
剑指 Offer 22. 链表中倒数第k个节点
[案例需求]
[思路分析]
起始先让快指针 fast 先走 k 步,此时 fast 和 slow 之间距离为 k,之后让 fast 和 slow 指针一起走(始终维持相对距离为 k),当 fast 到达链表尾部,slow 即停在倒数第 k 个节点处。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
//2. 快慢针
ListNode fast = head;
ListNode slow = head;
while(k > 0){
fast = fast.next;
--k;
}
while(fast != null){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
[思路分析二, 栈]
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
Deque<ListNode> stack = new LinkedList<>();
ListNode temp = head;
while(true){
if(temp == null)break;
stack.push(temp);
temp = temp.next;
}
for(int i = 0; i < k; i++){
if(i == k - 1)return stack.pop();
stack.pop();
}
return null;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)