JAVA二刷-Day4 | 24. 两两交换链表中的节点, 19.删除链表的倒数第N个节点, 面试题 02.07. 链表相交, 142.环形链表II
两两交换链表中的节点
LeetCode题目链接:https://leetcode.cn/problems/remove-linked-list-elements/
解题思路
先看题目,因为是要两两进行交换,所以与直接翻转有所不同。需要考虑更多的情况,比如头结点的更换问题。为了避免特殊处理,添加虚拟头结点。之后,因为两两反转后,指向第三个节点的部分也出现了改变,因此第三个节点也需要用额外的临时变量进行存储。
因此,首先初始化一个虚拟头结点来作为当前的头结点,再补充一个临时变量,进行迭代。
代码如下:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(-1, head);
ListNode temp,curr,pre;
ListNode dhead = dummyHead;
while (dhead != null && dhead.next != null && dhead.next.next != null) {
pre = dhead.next;
curr = dhead.next.next;
temp = dhead.next.next.next;
dhead.next = curr;
curr.next = pre;
pre.next = temp;
dhead = pre;
}
return dummyHead.next;
}
}
删除链表的倒数第N个节点
LeetCode题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list
解题思路
删去倒数第n个元素,那么首先要知道整个链表的长度,在已知链表长度的基础上,可以得出倒数第n个值是正数的第几个节点。
此后,设置一个节点用于到达该位置,另一个节点集合该位置之前的点。便完成了删除节点的操作。
具体代码如下:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(-1, head);
ListNode slow = dummyHead, fast = dummyHead;
while (n != 0 && fast!=null) {
fast = fast.next;
n--;
}
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
}
链表相交
LeetCode题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
解题思路
需要判定两个链表的相交部分,主要需要保持两个链表的节点更新始终保持到尾部的距离相同。这样才能同步去判定节点位置是否相交。但是因为两个链表长度不定,因此需要先计算出链表的长度。再使长度不等的链表中较长的链表去除头部元素,使其到尾部节点的距离相同,再进行迭代,就可以轻松判定出是否相交了。
具体代码如下:
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0, lenB = 0, offset = 0;
ListNode temp = headA;
while (temp != null) {
temp = temp.next;
lenA++;
}
temp = headB;
while (temp != null) {
temp = temp.next;
lenB++;
}
offset = Math.abs(lenA - lenB);
if (lenA > lenB) {
while (offset != 0 && headA !=null) {
headA = headA.next;
offset--;
}
}else if (lenB > lenA) {
while (offset != 0 && headB != null) {
headB = headB.next;
offset--;
}
}
while (headA != null && headB != null) {
if (headA == headB) {
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
}
环形链表II
LeetCode题目链接:https://leetcode.cn/problems/linked-list-cycle-ii
解题思路
先看题目,首先需要判断链表是否有环,因此可以进行判断,一旦指针在迭代时出现->next=NULL则说明不存在环形,跳出循环并返回NULL。
之后进行环形判断,当链表存在环形,易得快慢指针一定存在交点。假设从链表起点到环形起点的距离为 x x x,环形起点到交点的距离为 y y y,交点到环形起点的距离为 z z z 。可以得到以下公式:
当快慢指针相遇时有,其中
(
x
+
y
)
(x+y)
(x+y)为慢指针的移动距离,由于快指针是慢指针的二倍,所以乘以2后等式成立:
2
(
x
+
y
)
=
x
+
y
+
n
∗
(
y
+
z
)
2(x + y) = x +y +n*(y+z)
2(x+y)=x+y+n∗(y+z)
化简可以得到:
x
+
y
=
n
∗
(
y
+
z
)
x+y = n*(y+z)
x+y=n∗(y+z)
即可以求得链表起点到环形起点的距离公式,如果得到
x
x
x即可得出环形的起点位置:
x
=
(
n
−
1
)
∗
(
y
+
z
)
+
z
x = (n-1)*(y+z) + z
x=(n−1)∗(y+z)+z
同时,可以观察到
y
+
z
y+z
y+z为一个环形,因此可以看作:从交点位置出发,与从链表头出发,一定会在环形的起点处相遇。
注意,因为fast和slow开始是相同的,所以不能while时候直接判定fast == slow。会导致无法进入循环。
代码如下:
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next !=null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}