24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
个人思路
创建虚拟头结点dHead,从虚拟头结点开始遍历链表。
利用 next 变量来存储当前 node 节点后的第三个节点,将 node 后的第一个节点与第二个节点交换位置,并将next 的值赋值给新的node.next.next的next。
原理易于理解,但是操作可能有一点绕。
var swapPairs = function(head) {
let dHead = new ListNode(0,head)
let node = dHead
while(node.next && node.next.next){
let next = node.next.next.next
let tmp = node.next
node.next = node.next.next
node.next.next = tmp
node.next.next.next = next
node = node.next.next
}
return dHead.next
};
代码随想录题解思路和个人思路基本相同,过。
19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解题思路
思路一,先遍历链表取得链表的长度,然后在从头开始,遍历到倒数第 n 个节点之前,然后通过目标节点前一个节点的next 指向来删除这个节点。
这个思路比较容易想到,也很好操作。
思路二,使用两个指针,分别为快慢指针,让快指针先走n步,然后再让快慢指针同步向前遍历。当快指针到达链表末尾时,慢指针正好到要删除的节点的前面。通过修改慢指针的指向,可以删除节点。
var removeNthFromEnd = function(head, n) {
let slow = new ListNode(0,head)
let ret = slow
let fast = head
while(n--){
fast = fast.next
}
while(fast && slow){
fast = fast.next
slow = slow.next
}
slow.next = slow.next.next
return ret.next
};
面试题 02.07. 链表相交
同:160.链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
解题思路
分别获取两个链表的差值,然后“对齐”两个链表的尾部,通过长度差移动较长的那个链表的指针,与较短的链表的头指针对齐,然后一起向后遍历,遇到相等的情况下返回节点。
var getIntersectionNode = function(headA, headB) {
let nodeA = headA
let lenA=lenB = 0
let nodeB = headB
let diff = 0
while(nodeA || nodeB){
if(nodeA){
lenA ++
nodeA = nodeA.next
}
if(nodeB){
lenB ++
nodeB = nodeB.next
}
}
diff = Math.abs(lenA-lenB)
nodeA = headA
nodeB = headB
if(lenB > lenA){
while(diff--){
nodeB = nodeB.next
}
while(lenA--){
if(nodeB === nodeA){
return nodeB
}
nodeB = nodeB.next
nodeA = nodeA.next
}
}else{
while(diff--){
nodeA = nodeA.next
}
while(lenB--){
if(nodeB === nodeA){
return nodeB
}
nodeB = nodeB.next
nodeA = nodeA.next
}
}
return null
};
上述代码是我没看题解的情况下写的代码,基本的算法思路契合,但是代码有很大的冗余,不简洁。
可以在获取链表长度和移动指针尾对齐链表的时候进行代码复用操作。
var getListLen = function(head) {
let len = 0, cur = head;
while(cur) {
len++;
cur = cur.next;
}
return len;
}
var getIntersectionNode = function(headA, headB) {
let curA = headA,curB = headB,
lenA = getListLen(headA),
lenB = getListLen(headB);
if(lenA < lenB) {
// 下面交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
// 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
}
let i = lenA - lenB;
while(i-- > 0) {
curA = curA.next;
}
while(curA && curA !== curB) {
curA = curA.next;
curB = curB.next;
}
return curA;
};
142.环形链表II
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
解题思路:
这道题的解题思路可以分为两部分:
-
判断链表中是否有环
在判断链表中是否有环的时候,使用快慢指针方法,一开始将slow 与 fast 同时指向头结点, slow节点一次前进一个位置,fast 节点一次前进两个位置,这样的话,如果链表中有环,那么快慢指针一定会相遇。
如果快指针最后指向null,说明遍历到链表的末尾,链表内不存在环,返回 null.
-
判断环的入口
在第一次的解题过程中,我只想到了判断链表是否有环的方法,没有进一步找到环的入口。
实际上看完题解并写完代码后,这个后半部分的思路需要一些简单的数学运算和图解配合:
此时,创建新指针ptr指向头,和slow指针一起按照一步每次的速度进行遍历,最后同步到达环的入口点。
当 ptr === slow, ptr 和 slow 指向的均是入口点,返回该节点即可。
var detectCycle = function(head) {
if(head == null) return null
let fast = head
let slow = head
while(fast){
if(!fast.next) return null
fast = fast.next.next
slow = slow.next
if(fast === slow){
let ptr = head
while(ptr != slow){
slow = slow.next
ptr = ptr.next
}
return ptr
}
}
return null
};