一、24 两两交换链表中的节点
思路:加入虚拟头结点,然后cur指针每次移动两格,将前后两个节点交换。很简单。时间复杂度O(n)。
代码:
var swapPairs = function(head) {
if (!head) {
return head;
}
var preHead = new ListNode(-1, head);
var cur = preHead;
while (cur.next && cur.next.next) {
var left = cur.next;
var right = cur.next.next;
var temp = right.next;
cur.next = right;
right.next = left;
left.next = temp;
cur = cur.next.next;
}
return preHead.next;
};
二、19 删除链表的倒数第N个结点
思路:一开始想到的是先算一下一共有几个节点,然后再重新从头开始遍历,这是两趟扫描;第二种方法想到的是借助一个数组,将所有的遍历节点存进去,然后取出对应的哪一个节点数据;卡哥的算法思路是快慢指针法,让fast先走n+1步,然后再让slow和fast一起走,这样fast走到末尾时slow就在倒数第n个节点上了。
思路1代码:
var removeNthFromEnd = function(head, n) {
var preHead = new ListNode(-1, head);
var len = 0;
var cur = preHead;
while (cur.next) {
len += 1;
cur = cur.next;
}
var result = len - n;
var pointer = preHead;
for (result; result>0; result--) {
pointer = pointer.next;
}
if (pointer.next) {
pointer.next = pointer.next.next;
}
else {
pointer.next = null;
}
return preHead.next;
};
思路2代码:
var removeNthFromEnd = function(head, n) {
var preHead = new ListNode(-1, head);
var stack = [];
var cur = preHead;
while (cur) {
stack.push(cur);
cur = cur.next;
}
var node = stack[stack.length - n - 1];
if (node.next) {
node.next = node.next.next;
}
else {
node.next = null;
}
return preHead.next;
};
卡哥思路代码:
var removeNthFromEnd = function(head, n) {
var preHead = new ListNode(-1, head);
var fast = preHead;
var slow = preHead;
// 此处循环为n+1,是为了方便slow停在删除节点的前一个节点上
for (let i = n+1; i > 0; i--) {
fast = fast.next;
}
while (fast) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next ? slow.next.next : null;
return preHead.next;
};
三、160 链表相交
思路:首先想到的是借助栈来做,将两个链表分别压入两个栈中,然后出栈,依次比较,直至不相同的地方,时间复杂度O(3m/2+3n/2),空间复杂度O(m+n),这种就不写了;其次还想到一种递归的方式,首先需要判断一个链表是否是另一个链表的子链表,如果是的话返回这一个链表,而如果不是的话递归这个链表的next和另外那个链表,不过可见时间复杂度会较高。但是题目要求时间复杂度O(m+n)、空间复杂度O(1),卡哥的方式是求出两个链表的长度,以及其差值,然后让curA移动到两个链表的尾部对齐的部位,再依次前进,直至相等。
递归代码:(未解决)
var getIntersectionNode = function(headA, headB) {
if (isSonLink(headA, headB)) {
return headA;
}
else {
getIntersectionNode(headA.next, headB);
}
};
// 求A是否为B的子链表
var isSonLink = function(A, B) {
var cur = B;
// 空指针是所有指针的子链表
if (!A) {
return true;
}
// B遍历到末尾,判断是否有一个指针会等于A
while (cur) {
if (cur === A) {
return true;
}
cur = cur.next;
}
return false;
}
卡哥思路:
var getIntersectionNode = function(headA, headB) {
var curA = headA;
var curB = headB;
var lenA = 0;
var lenB = 0;
while (curA) {
lenA += 1;
curA = curA.next;
}
while (curB) {
lenB += 1;
curB = curB.next;
}
curA = headA;
curB = headB;
if (lenA >= lenB) {
for (let i=lenA-lenB; i>0; i--) {
curA = curA.next;
}
}
else {
for (let i=lenB-lenA; i>0; i--) {
curB = curB.next;
}
}
while (curA && curB) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.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), // 求链表A的长度
lenB = getListLen(headB);
if(lenA < lenB) { // 让curA为最长链表的头,lenA为其长度
// 交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
// 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
[curA, curB] = [curB, curA];
[lenA, lenB] = [lenB, lenA];
}
let i = lenA - lenB; // 求长度差
while(i-- > 0) { // 让curA和curB在同一起点上(末尾位置对齐)
curA = curA.next;
}
while(curA && curA !== curB) { // 遍历curA 和 curB,遇到相同则直接返回
curA = curA.next;
curB = curB.next;
}
return curA;
};
四、142 环形链表II
思路:这道题真是几遍都记不住,直接看提示吧。。。
代码:
var detectCycle = function(head) {
// 快指针每次走两步,慢指针每次走一步
var slow = head, fast = head;
while (fast && fast.next) {
fast = fast.next.next;
slow = slow.next;
// 快慢指针相遇
if (fast == slow) {
// 从相遇点和头部分别走,他俩相遇了就一定是环的入口
var index1 = fast;
var index2 = head;
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
};
今日学习时间:2h左右
总结:今天前两道题做的还是比较顺利的,但后两道题相对来讲比较难,不过我认为这两道题的泛用度也没有那么高,都是针对链表的特殊处理,因此没有太过深入研究,特别是142,除了记住我感觉别无他法。。。链表相交我用了一种递归的方式,但还没有看出问题在哪。下次再看的时候再来记一下142题。