第四天
24. 两两交换链表中的节点
用虚拟头结点,这样会方便很多。
本题链表操作就比较复杂了,建议大家先看视频,视频里我讲解了注意事项,为什么需要temp保存临时节点。
题目链接/文章讲解/视频讲解: https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html
思路:这道题还是比较困难的,学习了之后。
递归方法:主要思想就是不断的交替一对相邻的节点,闯入一对节点的第一位,然后返回一对节点的第二位。
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode nextNode = head.next;
ListNode newNode = swapPairs(nextNode.next);
nextNode.next = head;
head.next = newNode;
return nextNode;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNtXA6e2-1688446214920)(学习记录.assets/image-20230703232727880.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-whve9173-1688446214921)(学习记录.assets/image-20230703232734444.png)]
if(head == null || head.next == null){
return head;
}
ListNode dummyHead = new ListNode(0,head);
ListNode pre = dummyHead;
while(pre.next != null && pre.next.next != null){
ListNode temp = pre.next;
pre.next = pre.next.next;
temp.next = pre.next.next;
pre.next.next = temp;
pre = pre.next.next;
}
return dummyHead.next;
19.删除链表的倒数第N个节点
双指针的操作,要注意,删除第N个节点,那么我们当前遍历的指针一定要指向 第N个节点的前一个节点,建议先看视频。
思路:暴力解法,先遍历一遍链表,看链表中的节点有多少个,然后再遍历一遍,找到倒数第N个节点的前一个节点,进行删除,这样就得遍历两遍。
双指针,也叫快慢指针,先把快指针移动n+1步,为什么是n+1,因为删除第n个节点,要操作第n个节点的前一个节点,然后再同时移动慢指针,直到快指针到链表的末尾,操作慢指针删除节点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUgZyjzY-1688446214921)(学习记录.assets/image-20230704065536007.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsOwVNAH-1688446214921)(学习记录.assets/image-20230704065654832.png)]
ListNode dummyNode = new ListNode(0,head);
ListNode slowNode = dummyNode;
ListNode fastNode = dummyNode;
int slowCount = 0;
int fastCount = 0;
while (fastNode.next != null){
fastCount ++ ;
fastNode = fastNode.next;
if (fastCount - slowCount > n){
slowCount ++ ;
slowNode = slowNode.next;
}
}
slowNode.next = slowNode.next.next;
return dummyNode.next;
优化代码。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode low = dummyNode;
ListNode first = dummyNode;
for(int i = 0; i<n;i++){
first = first.next;
}
while(first.next != null){
first = first.next;
low = low.next;
}
low.next = low.next.next;
return dummyNode.next;
}
面试题 02.07. 链表相交
本题没有视频讲解,大家注意 数值相同,不代表指针相同。
思路:想了好一会,没有想出来,想复杂了。
简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。
为了方便举例,假设节点元素数值相等,则节点指针相等。
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XUQdhh5-1688446214921)(学习记录.assets/面试题02.07.链表相交_1.png)]
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4DGcjXoG-1688446214921)(学习记录.assets/面试题02.07.链表相交_2.png)]
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
ListNode curA = headA;
ListNode curB = headB;
int lenA=0,lenB = 0;
//求A的长度
while(curA != null){
lenA++;
curA = curA.next;
}
//求B的长度
while(curB != null){
lenB ++;
curB = curB.next;
}
//让CurA是代表的是最长的那一条
curA = headA;
curB = headB;
if(lenB > lenA){
int temp = lenB;
lenB = lenA;
lenA = temp;
curA = headB;
curB = headA;
}
// 求长度差
int gap = lenA -lenB;
//让curA和curB在同一起点上(末尾位置对齐)
while(gap-- > 0){
curA = curA.next;
}
//
while(curA != null){
if(curA == curB){
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
142.环形链表II
算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口,建议先看视频。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html
思路:这道题挺难的,主要两个点,如果有环的话,快慢指针必定会在环内相遇,而且是在慢指针第一轮就会相遇。第一次相遇的点到环入口的距离和头节点到环入口的节点的距离是一致的,具体看文章,理解数学问题。
这道题目,不仅考察对链表的操作,而且还需要一些数学运算。
主要考察两知识点:
- 判断链表是否环
- 如果有环,如何找到这个环的入口
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
//有环
ListNode tempNode1 = head;
ListNode tempNode2 = slow;
while (tempNode1 != tempNode2){
tempNode1 = tempNode1.next;
tempNode2 = tempNode2.next;
}
return tempNode1;
}
}
return null;
}