24. 两两交换链表中的结点
思路:用虚拟头结点会方便很多。交换的重点:交换的时候同时涉及到4个结点,我们要让cur指向第一个结点,才能方便得完成交换工作。
操作之后,链表如下:
//双指针版本
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null){
ListNode temp1 = cur.next;
ListNode temp2 = cur.next.next;
ListNode temp3 = cur.next.next.next;
cur.next = temp2;
temp2.next = temp1;
temp1.next = temp3;
cur = temp1;
}
return dummy.next;
}
// 递归版本
class Solution {
public ListNode swapPairs(ListNode head) {
// base case 退出提交
if(head == null || head.next == null) return head;
// 获取当前节点的下一个节点
ListNode next = head.next;
// 进行递归
ListNode newNode = swapPairs(next.next);
// 这里进行交换
next.next = head;
head.next = newNode;
return next;
}
}
19. 删除链表的倒数第N个节点
思路:采用虚拟头结点加双指针轻松拿下,快指针和慢指针相差n个结点,当快指针的next为空时,说明慢指针已经走到待删除结点的前一个结点了,执行删除操作即可。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode fastIndex = dummy;
ListNode slowIndex = dummy;
//快指针相差n个结点
for (int i = 0; i < n; i++) {
fastIndex = fastIndex.next;
}
while (fastIndex.next != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
slowIndex.next = slowIndex.next.next;
return dummy.next;
}
}
面试题 160. 链表相交
思路:这道题求的是两个链表的相交部分的头结点,注意是指针相同的结点而不是数值相同。有一个隐藏内容就是指针相同的话,那意味着该结点的后面部分应该都相同,所以长度也应该相同,我们就可以把两个链表从后往前对齐,然后从相交的第一个结点开始往后遍历,直到找到那个指针相同的结点即可。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengthA = 0;
int lengthB = 0;
ListNode curA = headA;
ListNode curB = headB;
while (curA != null){
curA = curA.next;
lengthA++;
}
while (curB != null){
curB = curB.next;
lengthB++;
}
curA = headA;
curB = headB;
if (lengthA > lengthB){
for (int i = 0; i < lengthA - lengthB; i++) {
curA = curA.next;
}
}else if (lengthA < lengthB){
for (int i = 0; i < lengthB - lengthA; i++) {
curB = curB.next;
}
}
while (curA != null){
if (curA == curB){
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
142. 环形链表II
思路:这道题看了视频才恍然大悟。主要是判断是否有环和如何找到环的入口两个问题。
- 判断是否有环:我们定义快慢两个指针,快指针每次走两个结点,慢指针每次走一个结点,因为快指针相对于慢指针是在一个一个结点地追慢指针,所以有环闭相遇。
- 如何找到环的入口:找到快慢指针相遇的结点,这个相遇结点和头结点离入口的距离是相等的。再定义两个指针分别从相遇和头结点出发,找到入口即可。至于为什么相等,需要画图进行数学证明,可以再看视频研究。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
ListNode index1, index2;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if (slow == fast){
index1 = head;
index2 = fast;
while (index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
链表总结
链表的相关问题,通用的方法有虚拟头结点,可以很大程度方便了我们对于头结点的处理。
双指针是必须掌握的,熟练用快慢指针,进行题目的拆解分析。
递归也是常用的,相较于双指针比较难理解。
还有链表的一些基本操作,核心要领:对于单链表而言,如果要添加或者删除某个结点,我们的指针一定要指向这个结点的前一个结点。