目录
24. 两两交换链表中的节点
题目
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
提示:
- 链表中节点的数目在范围
[0, 100]
内 0 <= Node.val <= 100
思路
建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
可以参考下图的操作:
初始时,cur指向虚拟头结点,然后进行如下三步:
操作之后,链表如下:
看这个可能就更直观一些了:
代码如下(JavaScript)
var swapPairs = function(head) {
const dummyHead = new ListNode(0,head) //创建虚拟头节点
let cur =dummyHead //让临时指针指向虚拟头节点
//原链表的节点数为偶数时,所有节点两两交换;节点数为奇数时,最后一个节点不参与交换
//所以判断循环条件时,偶数个节点的链表:cur.next!==null;奇数个节点的链表:cur.next.next!==null
//注意:此处一定要先判断cur.next!==null,再判断cur.next.next!==null,防止当链表为偶数个节点时,cur.next已经为空了,这时判断cur.next.next!==null就相当于null.next!==null,就会报“空指针异常”的错误。
while(cur.next!==null&&cur.next.next!==null){
let temp=cur.next //先用临时指针标记,防止断开部分指针指向后,找不到对应的节点
let temp1=cur.next.next.next //同理
cur.next=cur.next.next //步骤一
cur.next.next=temp //步骤二
temp.next=temp1 //步骤三
cur=cur.next.next //交换完两个节点后,将cur向前移动两个位置,继续执行节点交换
}
return dummyHead.next //最后返回新链表的头节点
};
19.删除链表的倒数第N个节点
题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
思路
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
步骤:
-
推荐使用虚拟头结点
-
定义fast指针和slow指针,初始值为虚拟头结点,如图:
-
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
-
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
-
删除slow指向的下一个节点,如图:
代码如下(JavaScript)
var removeNthFromEnd = function(head, n) {
const dummyHead =new ListNode(0,head) //创建虚拟头节点
let fast=dummyHead //定义一个快指针指向虚拟头节点
let slow=dummyHead //定义一个慢指针指向虚拟头节点
n++ //确保fast指针移动n+1位,这样在两个指针同时移动时,slow最后能停在要删除的节点的上一位,这样方便操作
while(n--&&fast!==null){
fast=fast.next //fast指针先向前移动n+1位
}
while(fast!==null){ //在fast指针还未到达末尾时,俩指针同时逐步向前移动
fast=fast.next
slow=slow.next
}
slow.next=slow.next.next //最后slow指针停留在要删除的目标节点的前一位,执行删除操作
return dummyHead.next //返回新链表的头节点
};
面试题 02.07. 链表相交
题目
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
思路
简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。
为了方便举例,假设节点元素数值相等,则节点指针相等。
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
代码如下(JavaScript)
//先封装一个求链表长度的函数
var getListLen = function (head) {
let cur = head //在头节点定义一个临时指针
let len = 0
while (cur) { //在cur指针还未到达末尾时,len逐步+1
len++
cur = cur.next
}
return len //得到链表的长度
}
var getIntersectionNode = function (headA, headB) {
let curA = headA //在链表A的头节点定义一个临时指针
let curB = headB //在链表B的头节点定义一个临时指针
let lenA = getListLen(headA) //调用getListLen函数获取链表A的长度
let lenB = getListLen(headB) //调用getListLen函数获取链表B的长度
//为了方便操作,要确保链表A的长度大于链表B的长度
/*
注意:
交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
*/
if (lenA < lenB) {
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
}
let i=lenA-lenB //获取两条链表的长度差
while(i-->0){
curA=curA.next //让curA移动到,和curB 末尾对齐的位置
}
//比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
while(curA&&curA!==curB){
curA=curA.next
curB=curB.next
}
return curA
};
142.环形链表II
题目
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
思路
主要考察两知识点:
- 判断链表是否环
- 如果有环,如何找到这个环的入口
代码如下(JavaScript)
var detectCycle = function (head) {
let fast = head //在头节点定义一个fast指针
let slow = head //在头节点定义一个slow指针
/*
在fast指针还未到达末尾时,fast指针每次向前移动两个节点,slow指针向前移动一个节点
因为fast指针是两步两步往前移动的,所以判断条件不仅要为fast != null 同时还要满足fast.next != null
只需要判读fast指针就行,因为fast指针走在前面
*/
while (fast != null && fast.next != null) {
fast = fast.next.next
slow = slow.next
if (fast == slow) { //当fast指针和slow相遇时,则证明链表有环
let index1 = fast //在相遇点设置一个指针index1
let index2 = head //在头节点设置一个指针index2
//在fast指针和slow指针环内相遇的点到入环第一个节点的位置与头节点到入环第一个节点的位置距离相等
//在index1和index2还未相遇时,两个指针依次向前移动一个节点(即两者移动速度相等)
while (index1 != index2) {
index1 = index1.next
index2 = index2.next
}
//当index1==index2时,该节点就是入环的第一个节点
return index1
}
}
return null
};