Day4: 链表基础 II

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;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值