24. 两两交换链表中的节点
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function swapPairs(head: ListNode | null): ListNode | null {
let virtualHead = new ListNode(0, head);
let prePoint = virtualHead;
let leftPoint = head;
let rightPoint = head?.next;
if(!leftPoint || !rightPoint) return head;
while(rightPoint){
prePoint.next = rightPoint;
leftPoint.next = rightPoint.next;
rightPoint.next = leftPoint;
[rightPoint, leftPoint] = [leftPoint, rightPoint];
leftPoint = leftPoint.next?.next;
rightPoint = rightPoint.next?.next;
prePoint = prePoint.next.next;
}
return virtualHead.next;
};
19.删除链表的倒数第N个节点
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
if(!head) return null;
let curNum = 1;
let virtualHead = new ListNode(0, head);
let prePoint = virtualHead;
let curPoint = head;
let postPoint = head;
let i = 1;
while(i !== n){
postPoint = postPoint?.next;
i++;
}
while(postPoint.next){
prePoint = curPoint;
curPoint = curPoint.next;
postPoint = postPoint.next;
}
prePoint.next = curPoint?.next;
return virtualHead.next;
};
多用了一个指针,由于 prePoint 和 curPoint 的关系永远是挨着的,所以完全可以用一个指针代替。
官方
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
let newHead: ListNode | null = new ListNode(0, head);
//根据leetcode题目的定义可推断这里快慢指针均不需要定义为ListNode | null。
let slowNode: ListNode = newHead;
let fastNode: ListNode = newHead;
while(n--) {
fastNode = fastNode.next!; //由虚拟头节点前进n个节点时,fastNode.next可推断不为null。
}
while(fastNode.next) { //遍历直至fastNode.next = null, 即尾部节点。 此时slowNode指向倒数第n个节点。
fastNode = fastNode.next;
slowNode = slowNode.next!;
}
slowNode.next = slowNode.next!.next; //倒数第n个节点可推断其next节点不为空。
return newHead.next;
}
面试题 02.07. 链表相交
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let lengthA = 0;
let lengthB = 0;
let pointA = headA;
let pointB = headB;
while(pointA){
lengthA++;
pointA = pointA.next;
}
while(pointB){
lengthB++;
pointB = pointB.next;
}
pointA = headA;
pointB = headB;
let diffLength = Math.abs(lengthA - lengthB);
if(lengthA > lengthB){
while(diffLength > 0){
pointA = pointA.next;
diffLength--;
}
}
if(lengthA < lengthB){
while(diffLength > 0){
pointB = pointB.next;
diffLength--;
}
}
while(pointA && pointB){
if(pointA === pointB) return pointA;
pointA = pointA.next;
pointB = pointB.next;
}
return null;
};
看了官方题解,首先,获取链表长度可以抽成一个方法,这样可以减少重复代码,第二,如果 lenA < lenB, 就交换两个指针并且交换 lenA 和 lenB,这样可以在后面统一处理。
官方题解:
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;
};
环形链表II
先看题解解的,关键问题是两个:
- 判断链表是否环
- 可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
- 如果有环,如何找到这个环的入口
- 从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点
function detectCycle(head: ListNode | null): ListNode | null {
let slowPoint = head;
let fastPoint = head;
while(fastPoint !== null && fastPoint.next !== null){
slowPoint = slowPoint!.next;
fastPoint = fastPoint.next.next;
if(slowPoint === fastPoint){
slowPoint = head;
while(slowPoint !== fastPoint){
slowPoint = slowPoint.next;
fastPoint = fastPoint.next;
}
return slowPoint;
}
}
return null;
};