目录
24 两两交换链表中的节点
看到题目的第一想法:看到题目之后,发现是链表两两交换,总在考虑元素数量是奇数还是偶数的问题,并没有清晰的解题思路。
看完代码随想录之后的想法:卡哥的方法,一步一步实现,元素从0开始,首先保存第2个元素的值temp,而后dummynode(最开始为-1)指向第1个元素,而第1个元素指向第0个元素,最后第0个元素指向temp;而后pre与head各步进一位,即pre = head(此时为第1个元素),head = head.next(此时为第2个元素),相当于pre,head从-1,0变成了1,2;最后返回了dummynode.next;
代码实现:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
while (pre.next != null && pre.next.next != null) {
//临时指针存放head.next.next
ListNode temp = head.next.next;
//pre指向head.next
pre.next = head.next;
//head.next的下一个(即head.next.next)是head
head.next.next = head;
//head指向temp
head.next = temp;
//pre与head各步进一位
pre = head;
head = head.next;
}
return dummy.next;
}
}
实现过程中遇到哪些困难:
- 在head.next元素要指向head的时候,总是误以为要写成head.next = head,而正确的写法应当为head.next.next = head!
- pre与head在各需要步进的时候,只步进一位够吗?
- 如上面所说:元素从-1 dummynode开始,首先保存第2个元素(head.next.next)的值temp,而后dummynode(最开始为-1)指向第1个(head.next)元素,而第1个元素指向第0个(head)元素,最后第0个元素指向temp;而后pre与head各步进一位,即pre = head(此时pre为第1个元素),head = head.next(此时head为第2个元素),相当于pre,head从-1,0变成了1,2,这就相当于两节点平移了两位,刚好满足题目需要。
19 删除链表的倒数第 N 个结点
看到题目的第一想法:这题之前刷过,要删除倒数第n个元素(n从1开始,最后一个即为1),只需要找到前一个元素即可,pre其实位置为-1,移动len - n步之后刚好到待删除元素的前一位。
看完代码随想录之后的想法:卡哥的方法也很巧妙,双指针法,fast从-1开始,先移动n,而后两者同时移动,待fast移动至最后一个节点时,fast共移动len个,slow共移动len - n个(还剩n个未移动),此时为待删除节点的前一个节点。
代码实现:
- 我的思路
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int len = 0;
ListNode dummy = new ListNode(-1);
ListNode pre = dummy;
dummy.next = head;
while (pre.next != null) {
len++;
pre = pre.next;
}
pre = dummy;
//pre移动len - n - 1 步之后刚好到待删除元素的前一位
for (int i = 0; i < len - n; i++) {
pre = pre.next;
}
ListNode temp = pre.next.next;
pre.next = temp;
return dummy.next;
}
- 卡哥的解法
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//卡哥的方法,双指针法,fast从-1开始,先移动n,而后两者同时移动,待fast移动至最后一个节点时
//fast共移动len个,slow共移动len - n个,此时为待删除节点的前一个
ListNode dummy = new ListNode(-1);
ListNode pre = dummy;
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;
for (int i = 0; i < n; i++) {
fast = fast.next;
}
//fast指向最后一个时,slow指向待删除节点前一个
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
}
实现过程中遇到哪些困难:
- 自己的思路与卡哥的解法中,需要移动多少个位置才能到待删除元素的前一个位置?
- 都是移动了length - n个位置,前者从0开始,移动到了length - n - 1,后者slow也移动了length - n个位置,还有n个位置未移动,都能到待删除元素的前一个位置。
面试题 02.07 链表相交
看到题目的第一想法:看到题目后没有思路,有无从下手的感觉
看完代码随想录之后的想法:卡哥的解法直接明确了若链表相交,从相交位置开始两链表完全是相等的,取较长的链表,而后将链表前部超出锻炼表的部分,相当于去掉(通过不断next的方式);两个链表一样长之后,比较是否相等,不相等则同时进行步进,相等则返回。
代码实现:
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//自己想法比较乱,卡哥的解法看到后就感觉肯定可以解出题,很清晰
int lenA = 0, lenB = 0;
ListNode curA = headA;
ListNode curB = headB;
//要判断使用next的节点是否为null!!!!!!!!!!
//别马虎大意了!
while (curA != null) {
lenA++;
curA = curA.next;
}
while (curB != null) {
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
//使A是较长的链表
if(lenA < lenB) {
ListNode temp = curA;
curA = curB;
curB = temp;
int t = lenA;
lenA = lenB;
lenB = t;
}
int n = lenA - lenB;
while (n-- > 0) {
curA = curA.next;
}
//遍历新链表,相等则返回,不相等则向后移动
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
实现过程中遇到哪些困难:
- while循环的条件总是写错,经常未判断curA != null,而去直接判断curA.next != null,这是不可取的!!
- 使用到next的时候要做非空判定~
142 环形链表 II
看到题目的第一想法:看到题目之后没有思路,不知道如何模拟环形链表的情况。
看完代码随想录之后的想法:卡哥的方法通过模拟找出相遇的位置。卡哥是通过数学计算,来得到x = (n-1)(y+z) + z,若n = 1,即快指针在环里走了一圈多之后即遇到了满指针。所以有x = z,即从相遇位置每次步进一步,同时head每次步进一步,交点即为环的入口。
-
fast == slow时表示有环,fast从相遇的位置开始,每次前进一步;
slow从head每次前进一步,相遇的位置即为环的入口
代码实现:
public class Solution {
public ListNode detectCycle(ListNode head) {
//无思路,卡哥的方法通过模拟找出相遇的位置每次步进一步,head每次步进一步,交点即为环的入口
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
//fast == slow时表示有环,fast从相遇的位置开始,每次前进一步;
//slow从head每次前进一步,相遇的位置即为环的入口
if (fast == slow) {
ListNode index1 = fast;
ListNode index2 = head;
//此处即包含了快指针走>=1圈的情况 x = (n-1)(y+z) + z
//n:快指针在环内走的圈数,n >= 1
//x:起始点到环入口的距离
//y:入口至相遇点距离
//z:相遇点至下一入口的距离
while(index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
实现过程中遇到哪些困难:
- 如何表示快慢指针的相遇,fast == slow;
- 如何表示fast在环中遇到slow指针,x = (n-1)(y+z) + z,n=1时,说明fast在链表中移动一圈多就和slow指针相遇了,n>=2时,下面代码也可以表示:
-
if (fast == slow) { ListNode index1 = fast; ListNode index2 = head; //此处即包含了快指针走>=1圈的情况 x = (n-1)(y+z) + z //n:快指针在环内走的圈数,n >= 1 //x:起始点到环入口的距离 //y:入口至相遇点距离 //z:相遇点至下一入口的距离 while(index1 != index2) { index1 = index1.next; index2 = index2.next; } return index1; }
-
- 为什么第一次在环中相遇,slow的 步数 是 x+y 而不是 x + 若干环的长度 + y 呢?
- 见链接,
-
首先slow进环的时候,fast一定是先进环来了。
如果slow进环入口,fast也在环入口;可以看出如果slow 和 fast同时在环入口开始走,一定会在环入口3相遇,slow走了一圈,fast走了两圈。重点来了,slow进环的时候,fast一定是在环的任意一个位置,如图
今日收获,记录一下自己的学习时长
- 算法处理约3h,博客编写约2h
- 熟悉了链表的交换,删除,相交与环~
- 贵在坚持,加油!
链表总结:
- 虚拟头节点,链表基本操作,链表移除,反转,相交,环形都很重要~
- 链表相对于数组来说不太直观,需要投入更多精力,要多复习~