文章目录
- 一、删除链表中的倒数第n个节点
- 二、链表相交
- 三、环形链表
一、删除链表中的倒数第n个节点
题目链接:19.删除链表中的倒数第n个节点
/**
* <pre>
* 最简单的方法显然是先遍历一遍链表,知道长度后重新遍历一次就可以找到指定节点了,由此方法我们不难延伸到另一种解法
* 既然是删除倒数第n个节点,那么也就是一旦遇到最后一个节点那么它前面的第n个节点就是要删除的节点
* 我们只要使用快慢指针,慢指针在快指针前n位,那么当快指针到达链表尾时慢指针所指的就是需要删除的节点
* </pre>
*
* @author <a href="https://github.com/kil1ua">Ken-Chy129</a>
* @date 2023/1/5 14:52
*/
public class 删除链表的倒数第N个结点19 {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode pos1=new ListNode(), pos2=head, res = pos1;
pos1.next = head;
int i=0;
while (pos2 != null) {
if (i < n) { // 找的是链尾的前第n+1个,这样才能删除链尾的第n个
i++;
} else {
pos1 = pos1.next;
}
pos2 = pos2.next;
}
pos1.next = pos1.next.next;
return res.next;
}
public ListNode removeNthFromEnd2(ListNode head, int n) {
ListNode p = new ListNode(), res = p;
p.next = head;
Deque<ListNode> stack = new LinkedList<>();
while (p != null) {
stack.push(p);
p = p.next;
}
for (int i=0; i<n; i++) {
stack.pop();
}
p = stack.peek();
p.next = p.next.next;
return res.next;
}
}
二、链表相交
题目链接:160.链表相交
/**
* <pre>
* 计算出两个链表的长度,让长的链表先移动到和短的链表一样长(因为交点不可能在前面部分),随后两个链表同时移动,相等时即为交点
* 或者使用哈希表将第一个链表的元素进行存储,然后遍历第二个链表判断当前节点是否存在在哈希表中
* 两者时间复杂度相同,但是哈希表需要额外的空间开销(哈希表查找元素是需要O(1)的时间复杂度,所以总的时间开销相同)
* </pre>
*
* @author <a href="https://github.com/kil1ua">Ken-Chy129</a>
* @date 2023/1/6 13:49
*/
public class 链表相交160 {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pa = headA, pb = headB;
int lena=1, lenb=1;
while (pa != null) {
pa = pa.next;
lena++;
}
while (pb != null) {
pb = pb.next;
lenb++;
}
pa = headA;
pb = headB;
if (lena > lenb) {
for (int i=1; i<=lena-lenb; i++) {
pa = pa.next;
}
} else if (lena < lenb) {
for (int i = 1; i <= lenb - lena; i++) {
pb = pb.next;
}
}
while (pa != null) {
if (pa == pb) {
return pa;
}
pa = pa.next;
pb = pb.next;
}
return null;
}
public ListNode getIntersectionNode2(ListNode headA, ListNode headB) {
Set<ListNode> visited = new HashSet<ListNode>();
ListNode temp = headA;
while (temp != null) {
visited.add(temp);
temp = temp.next;
}
temp = headB;
while (temp != null) {
if (visited.contains(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
}
三、环形链表
题目链接:142.环形链表
/**
* <pre>
* 可以使用快慢指针的方式,让快指针每次走两格,慢指针每次走一格,快指针先入环而且一定会在环中与慢指针相遇
* 因为在环中对于慢指针来说,其实快指针是以每次1的速度向他逼近,最终一定会碰上
* </pre>
*
* @author <a href="https://github.com/kil1ua">Ken-Chy129</a>
* @date 2023/1/6 14:26
*/
public class 环形链表II142 {
public ListNode detectCycle(ListNode head) {
ListNode pos1 = head, pos2 = head;
while (pos2 != null) {
if (pos2.next == null) {
return null;
}
pos1 = pos1.next;
pos2 = pos2.next.next;
if (pos2 == pos1) {
ListNode p = head;
while (p != pos1) {
p = p.next;
pos1 = pos1.next;
}
return p;
}
}
return null;
}
}
注释中已经分析了如果有环为什么两个指针一定会在环中相遇,接下来分析一下如何寻找环的起点。
途中紫色点为快慢指针的相遇点,假设快指针走了n圈后与慢指针相遇,那么相遇时有:
- 快指针走的距离:a+n(b+c)+b
- 快指针走的距离是慢指针的两倍
- 慢指针走的距离:a+b
- 为什么慢指针一定会在第一圈跟快指针相遇?
- 其实在注释中也写到了,每次循环可以理解为快指针在以每次为1的速度追赶慢指针,而在慢指针进入环的时候快指针距离它的距离最多也是少于1圈(快指针在慢指针前一位时为最长距离),那么追赶到的时候慢指针必然没有进入下一圈
- 那么就有a+n(b+c)+b=2(a+b),化简得a=c+(n-1)(b+c),(n-1)(b+c)表示n-1圈,那么也就是说只要这时让一个指针从链表起点出发,另一个指针从快慢指针相遇点出发,那它们会正好在环的起点相遇(因为a的长度等于从快慢指针相交点绕环n-1圈后再走c的长度,而再走c的长度正好就是到环的起点)