两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表入口
一、两两交换链表中的节点
力扣连接:24. 两两交换链表中的节点(中等)
通过画图,再使用虚拟头结点,能比较快的理解
1.图解步骤
关键点:
- 首先需要临时保存两个结点,如图所示
- 判断循环条件是否继续的条件 cur.next!=null && cur.next.next!=null
- cur每次都指向下一次待换的两结点的前一个结点
2.代码
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode cur = dummyHead;
while(cur.next!=null&&cur.next.next!=null){
//临时保存1结点
ListNode tmp1 = cur.next;
//临时保存3结点
ListNode tmp3 = cur.next.next.next;
//cur指向2结点
cur.next = cur.next.next;
//2结点指向1结点
cur.next.next = tmp1;
//1结点指向3结点
tmp1.next = tmp3;
//cur指向下一次待换的两结点的前一个结点
cur = cur.next.next;
}
return dummyHead.next;
}
删除链表的倒数第N个节点
1.图解步骤
关键点:
- 首先需要先移动front,移动的步幅为n-1,如图所示步骤1
- 再整体将prev和front同步逐步往后移,front到达链尾即停
2.创建虚拟头结点dummyHead
//返回所查数在数组中的索引,不存在返回-1
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode prev = dummyHead;
ListNode front = dummyHead.next;
//先移动front
for(int i=0;i<n-1;i++){
front = front.next;
}
//整体移动prev和front,直到front指向链表尾结点
while(front.next!=null){
prev = prev.next;
front = front.next;
}
prev.next = prev.next.next;
return dummyHead.next;
}
链表相交
力扣连接:面试题 02.07. 链表相交(简单)
1.图解步骤
关键点:
- 首先需要分别统计链表长度
- 链尾对齐
- 再将指针pointA、pointB同步逐步往后移,pointA=pointB 即停
2.代码
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode pointA = headA;
Integer lenA = 0;
ListNode pointB = headB;
Integer lenB = 0;
//获取A的长度
while(pointA!=null){
pointA = pointA.next;
lenA++;
}
//获取B的长度
while(pointB!=null){
pointB = pointB.next;
lenB++;
}
Integer gap = Math.abs(lenA-lenB);
if(lenA>=lenB){
for(int i = 0;i<gap;i++){
headA = headA.next;
}
}else{
for(int i = 0;i<gap;i++){
headB = headB.next;
}
}
while(headA!=null&&headB!=null){
if(headA==headB){
return headA;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
}
环形链表II(获取环的入口)
力扣连接:142. 环形链表 II(中等)
1.环的基础
1) 判断链表是否有环:
- slow = slow.next;
- fast = fast.next.next;
- slow = fast 的时候 有环
slow每次走一步,fast每次走两步。fast相对于slow来说,fast每次以一步的步伐相slow靠近。
注意:如果是fast = fast.next.next.next,走三步或以上 就有可能会错过。
2) 判断有环链表的入口:
- slow = slow.next;
- fast = fast.next.next;
- slow = fast 的时候 为交点
- 交点.next=start.next 的时候 为入口
- 公式如下:
- slow = x+y 每次走1个结点
- fast = x+y+n*(y+z) 每次走2个结点,走了n圈
- 2*(slow) = fast,即 2*(x+y) = x+y+n*(y+z)
- 约 x+y = n*(y+z),再约 x = n*(y+z) - y
- x = (n-1)*(y+z) + z ,若n=1,即 x = z,在环的入口处相遇
- 注n无论等于多少都不影响,比如n = 100,就是多转了100圈而已
2.快慢指针(slow、fast)+ 起始指针(start)
图解:
代码:
public ListNode detectCycle(ListNode head) {
ListNode start = head;
ListNode slow = head;
ListNode fast = head;
while(true){
if(fast==null||fast.next==null)return null;
slow = slow.next;
fast = fast.next.next;
if(slow==fast){
break;
}
}
while(start!=slow){
start = start.next;
slow = slow.next;
}
return start;
}
总结
链表的解法使用创建虚拟头结点的方式比较通俗易懂。
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
链表的next改变时需要考虑 是否需要临时保存当前next的结点。
不会的时候,动手画图能解决很多问题。