✅✅代码随想录算法训练营Day4 | | 24. 两两交换链表中的节点 ,19.删除链表的倒数第N个节点 , 面试题 02.07. 链表相交
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第8篇文章 点击查看文章详情 🚀🚀
24. 两两交换链表中的节点 - 力扣(LeetCode)
难点:🔥🔥🔥
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。
你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入: head = [1,2,3,4]
输出: [2,1,4,3]
交换链表
if(!head) return head;
let dummy = new ListNode(0,head);
let cur = dummy;
while(cur.next && cur.next.next){
let temp1 = cur.next;
let temp2 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = temp1;
temp1.next = temp2;
cur = cur.next.next;
}
return dummy.next;
难点
两两交换链表和翻转链表
一上来审题,发现两两交换,好,这不就是小范围的翻转链表吗?
这有啥难?
一顿操作猛如虎,仔细一看原地杵~
❌❌❌
反转链表
反转的操作是
连贯的
,我们刚开始设置的pre
指针可以随着cur
的遍历一直来到尾节点。然后直接将尾节点
作为头节点
遍历,就实现反转链表的操作了。
翻转链表
反转的操作是小范围,
断断续续的
,故思路肯定不会和反转链表一致,所以我们要从头节点
处想办法
实现的细节
要实现两两交换的效果,我们也可以从反转链表中找到思路
找上一个节点来实现
(在头部节点时即创建一个
虚拟头节点
)
四个节点
在对 1
,2 节点
进行两两交换的过程中,实际上和四个节点有关
虚拟头节点,1节点,2节点,3节点
注意
在这里我们因为首先要将虚拟头
节点指向2节点
cur.next = cur.next.next;
会直接改变原链表中的指向,为了能够正常取到1节点
和3节点
,可以先将其保存起来
let temp1 = cur.next;
let temp2 = cur.next.next.next;
如何实现两两交换?
在看到两两交换时,我下意识想到的是创建一个能够实现两两交换的函数,因为这个操作是重复的。并且要实现两两操作,尝试了一下用count去计数判断。
🙅🙅🙅
要命~
哪有那么复杂,慢慢遍历过去不就好了
while(cur.next && cur.next.next){
}
还能处理链表长度为奇数的时候,不进入循环,直接返回
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
难度: 🔥 🔥
给你一个链表,删除链表的倒数第 n个结点,并且返回链表的头结点。
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]
快慢指针
if(!head) return head;
let dummy = new ListNode(0,head);
let fast = dummy;
let slow = dummy;
while(n--){
fast = fast.next;
}
while(fast.next){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
感觉这种写法有点像固定公式,直接给我形成肌肉记忆了。
难点
最后改变指向时
自己写的时候是
slow.next = fast;
但最后运行时发现,一般情况下是可以,但要是删除的是头结点就寄了~
面试题 02.07. 链表相交 - 力扣(LeetCode)
难度:🔥🔥🔥
找到相同的位置开始遍历
const getlen = (head) => {
let len = 0
while(head){
head = head.next;
len++;
}
return len;
}
var getIntersectionNode = function(headA, headB) {
let curA = headA;
let curB = headB;
let lenA = getlen(headA)
let lenB = getlen(headB)
if(lenA > lenB){
[lenB,lenA] = [lenA,lenB];
[curB,curA] = [curA,curB];
}
len = lenB - lenA;
while(len--){
curB = curB.next;
}
console.log(curB)
while(curB && curB != curA){
curA = curA.next;
curB = curB.next;
}
if(curB == curA){
return curB
}
};
难点
交换两个数组
// 下面交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
// 如果不加分号,下面两条代码等同于一条代码
: [curA, curB] = [lenB, lenA]
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
142. 环形链表 II - 力扣(LeetCode)
难度:🔥🔥
环形链表这类题我没用卡哥的思路,在这里我用的是修言老师小册的思路~
修言老师的思路
何为链表成环?
抛开那些看起来复杂的高大上的解法,让我们回归本质。
👇 👇 👇
假如现实中有一个长跑爱好者李雷,这货很狂,他立了一个 flag,说要徒步环游世界:
地球的周长围出来的这个圆,它就是一个“环”。李雷现在就想围着这个环跑上一圈,说他狂,他也没那么狂——他觉得自己最多跑一圈,为了防止自己跑过界,他决定在出发的地方立一个 flag
这样,不管李雷走完这个环用了多少年,世事如何变迁,只要他的 flag 还没有倒,那么李雷就一定能回到自己梦开始的地方:)。
换个角度看:只要李雷在闷头前进的过程中,发现了 flag 的存在,那么就意味着,李雷确实走了一个环。毕竟若这是一条线,他将永远无法回到起点。
回到链表的世界里,也是一个道理。一个环形链表的基本修养,是能够让遍历它的游标回到原点:
从 flag 出发,只要我能够再回到 flag 处,那么就意味着,我正在遍历一个环形链表。
这样像这类问题就变简单了~
var detectCycle = function(head) {
if(!head) return head;
let cur = head;
// 设置flag
cur.flag = 0;
while(cur){
// 如果该flag存在,说明这个地方之前已经来过了 => 成环!!
if(cur.flag){
return cur;
}
// 没来过,就打上记号
cur.flag = 1;
cur = cur.next;
}
return null;
};
收获
因为链表的题目在半个月前已经刷过一次了,所以这次刷起来还是比较快的。
要论收获的话,还是对链表一些更细节的操作有了更多的了解。
- 这次因为算是不会再把两两交换链表和翻转链表搞混了,之前以为这两哥们都是一种解法。。。
- 删除节点还是按照删除的节点去做,换汤不换药
- 链表的相交,主要对链表的操作更加熟练。再碰到类似流程比较多的题目时不会畏惧了
- 环形链表直接采用比较简单,也比较好理解的方式~
期待明天的任务啦~
⭐⭐⭐