本文思路及更详细解答均来自于:代码随想录 (programmercarl.com)
题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)
我们还是和之前一样设置一个dummy哨兵位虚拟头结点
我们想做的就是把1和2交换,把3和4交换,至于5后面是空指针就没办法交换了,偶数项就是不包含第五个,这时候我们让current指针指向dummy,因为只有这样才能操作后面的两个元素
此时我们就想让dummy指向2,2指向1,1再指向3,那么我们要如何操作呢?
如果我先让current指向2了,那么我就找不到怎么指向1了,同理也找不到指向3了,所以在进行交换操作之前,我们需要先用一系列指针来记录我们需要交换的节点
ListNode temp = current.next; ListNode temp1 = current.next.next; ListNode temp2 = current.next.next.next;
current.next = temp1;
temp1.next = temp;
temp.next = temp2;
current = temp
注意:这里的循环判断条件是current的后面不为空并且后面的后面也不为空,顺序不能颠倒,不然先判断current.next,next时可能current.next就是空指针,有可能会报空指针异常.
/**
* 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) {
ListNode dummy = new ListNode(-1);
ListNode current = dummy;
dummy.next = head;
while(current.next !=null && current.next.next != null)
{
ListNode temp = current.next;
ListNode temp1 = current.next.next;
ListNode temp2 = current.next.next.next;
current.next = temp1;
temp1.next = temp;
temp.next = temp2;
current = temp;
}
return dummy.next;
}
}
题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
使用快慢指针,让快指针和慢指针之间差n个元素,这样就能找到倒数第n个元素了,我们仍然是使用dummy虚拟头节点,让快慢指针都指向虚拟头结点 ,然后先让fast指针走n步,然后快慢指针一起走,直到fast指针先碰上null,这个时候slow指针指向的元素就是我们需要的元素,这个时候因为我们想操作这个元素,所以应该让fast多走一步,这样我们就能找到倒数第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 dummy = new ListNode(0);
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;
while(n>0)
{
fast = fast.next;
n--;
}
while(fast.next != null)
{
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
}
题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)
图片来自于本篇开头的网站
换而言之这题的目的就是求链表相交的时候的指针,这里我们比较的不是数值,而应该是指针,为了简化理解,我们用相同数值来代替相同指针,更方便理解.
如图所示我们先用一个curA和一个curB来指向两个链表的头结点.
首先,我们可以遍历计算出两个链表分别的长度,lenA和lenB,我们求出两个链表长度的差值gap,然后让长链表从头结点移动差值个单位,使得两个链表的末尾对齐,接下来就是一个一个来比较了.遇到相等就return其中任意一个,遇不到就返回空指针.
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while (curA != null) { // 求链表A的长度
lenA++;
curA = curA.next;
}
while (curB != null) { // 求链表B的长度
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
由于这道题在很久以前做过,所以很容易就能想到用快慢指针,这个时候我们定义一个快指针fast指向head节点,定义一个慢指针slow指向head节点,然后让快指针每次向后移动两个单位,慢指针移动一个单位,如果有环那么快慢指针一定会相等,而不会错过,因为快指针先进入环,然后慢指针再进入环这个时候快指针以每次走一步的速度追慢指针,有点像小学初中的追击相遇问题,这里我们不妨画个图来理解一下.
这里如果我们用开始到相遇两个指针走的距离可以列出一个等式
快指针走过的节点数 :x+y+n(y+z)
慢指针走过的节点数:x+y
所以可以得到 2(x+y) = x+y+n(y+z) (n>=1,因为快指针至少在环里多走了一圈两个指针才相遇)
化简可得 x = (n-1)(y+z) + z
我们假设fast从环入口1进入环,slow从环入口2进入环,当slow走向环入口3时,fast已经走了两圈了,所以在slow进来的第一圈一定会相遇.
有人会说大概率会是一般的情况也就是slow进环的时候fast在任意位置,这里我们假设让fast走向下一个环入口,这个时候slow甚至还没走完一圈,我们画图说明.
现在我们找到了相遇的点,那么怎么去找进入环的节点呢,这里我们定义一个index保存相遇的节点,用一个index1保存head节点 ,我们根据之前的公式可以知道,当n = 1时,z =x,当n>1时,z+(n-1)圈,也就是说从头节点到环入口的距离和从两者相遇点到环入口的距离差了(n-1)个圈,这是我们让index和index1同时开始向后走,当他们相等时,就得到了环形链表的入口.
/**
* 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) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null )
{
fast = fast.next.next;
slow = slow.next;
if(slow == fast)
{
ListNode index = fast;
ListNode index1 = head;
while(index1 != index)
{
index = index.next;
index1 = index1.next;
}
return index;
}
}
return null;
}
}
重难点总结:要修改链表的指向或者删除链表,一定要获取被删除节点的前一个节点,这也就是哨兵位存在的意义,要重点分析与图形结合.