代码随想录算法训练营第四天 | 24-两两交换链表中的节点、19-删除链表的倒数第N个节点、面试题02.07-链表相交、142-环形链表II
LeetCode24-两两交换链表中的节点
题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/
文章讲解:https://programmercarl.com/0024.两两交换链表中的节点.html
视频讲解:https://www.bilibili.com/video/BV1YT411g7br/?vd_source=b989f2b109eb3b17e8178154a7de7a51
一拿到题目,我的思路是采用虚拟头节点保存Head的初始位置,先单独排除无元素和一个元素的情况,接着再单独处理两个元素的情况,之后如果符合条件再进行循环交换
循环交换的时候,使用pre、cur和after三个指针遍历链表,进行cur和after所指节点的两两交换,关键在于确定何时停止循环和避免操作空指针
总的来说还是要多画图、多分析

class Solution {public ListNode swapPairs(ListNode head) {ListNode dummyHead = head;if(dummyHead == null || dummyHead.next == null){return dummyHead;}ListNode pre = null;ListNode cur = dummyHead;ListNode after = dummyHead.next;cur.next = after.next;after.next = cur;dummyHead = after;pre = cur;cur = cur.next;while(cur != null){after = cur.next;if(after == null){break;}pre.next = after;cur.next = after.next;after.next = cur;pre = cur;cur = cur.next;}return dummyHead;}
}
看完Carl哥的讲解后,发现我的这个解法还能再活用dummyHead,进行逻辑的优化,统一操作链表,代码也更加简洁明了

class Solution {public ListNode swapPairs(ListNode head) {ListNode dummyHead = new ListNode();dummyHead.next = head;ListNode cur = dummyHead;while(cur.next != null && cur.next.next != null){ListNode pre = cur.next;ListNode after = cur.next.next.next;cur.next = cur.next.next;cur.next.next = pre;pre.next = after;cur = cur.next.next;}return dummyHead.next;}
}
LeetCode19 删除链表的倒数第N个节点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/
文章讲解:https://programmercarl.com/0019.删除链表的倒数第N个节点.html
视频讲解:https://www.bilibili.com/video/BV1vW4y1U7Gf/?vd_source=b989f2b109eb3b17e8178154a7de7a51
采用快慢指针的思想解,快指针从dummyHead出发,先往后移动 n+1 步,然后快慢指针一起往后移动,直到快指针指向null,此时慢指针指向的是要删除的节点的前一个节点

class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummyHead = new ListNode();dummyHead.next = head;ListNode fast = dummyHead;ListNode slow = dummyHead;for(int i = 1;i <= n+1;i++){fast = fast.next;}while(fast != null){fast = fast.next;slow = slow.next;}slow.next = slow.next.next;return dummyHead.next;}
}
面试题02.07 链表相交
题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
文章讲解:https://programmercarl.com/面试题02.07.链表相交.html
拿到题第一眼想到的是双重for循环暴力解,注意单个节点的情况,劈里啪啦写完发现居然能过,但这份代码一点也不优雅,而且时间复杂度来到O(n^2),得改!

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA == headB){return headA;}for(ListNode pA = headA; pA != null; pA = pA.next){for(ListNode pB = headB; pB != null; pB = pB.next){if(pA == pB){return pA;}}}return null;}
}
看了题解后自己又写了一遍,大开眼界:可以先求出AB两个链表的长度,然后求出两个链表长度差值,让两个链表的末尾对齐,curA和curB对齐相对短的那一个,然后比较curA和curB是否相同,不同则同时往后移动curA和curB,如果遇到相同的情况则找到了交点,否则返回null。妙呀妙呀!

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {ListNode curA = headA;ListNode curB = headB;int lengthA = 1;int lengthB = 1;while(curA != null){curA = curA.next;lengthA++;}while(curB != null){curB = curB.next;lengthB++;}curA = headA;curB = headB;if(lengthA >= lengthB){for(int i = 1;i <= lengthA - lengthB;i++){curA = curA.next;}}else{for(int i = 1;i <= lengthB - lengthA;i++){curB = curB.next;}}while(curA != null){if(curA == curB){return curA;}curA = curA.next;curB = curB.next;}return null;}
}
LeetCode142 环形链表II
题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/
文章讲解:https://programmercarl.com/0142.环形链表II.html
视频讲解:https://www.bilibili.com/video/BV1if4y1d7ob/?vd_source=b989f2b109eb3b17e8178154a7de7a51
这题解法相当的妙呀,先定义一个快指针和一个慢指针,快指针每次往后走两个节点,慢指针每次往后走一个节点,它们如果相遇则说明链表存在环
接着,由数学推理可以得出,从head到环入口的距离 = (n-1)圈环的距离 + 快慢指针相遇点到环入口的距离
由此可以再定义index1从快慢指针相遇点出发,index2从head出发,它们都每次往后走一个节点,这样index1和index2相遇的节点即为环的入口节点

public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null && fast.next.next != null){fast = fast.next.next;slow = slow.next;if(fast == slow){ListNode index1 = fast;ListNode index2 = head;while(index1 != index2){index1 = index1.next;index2 = index2.next;}return index1;}}return null;}
}