链表常见题及答案

/**
 * 
 * 判断一个链表是否有环 【141】
 * 
 * 
 * 快慢指针,求相遇
*/
var hasCycle = function(head) {
    if(!head) return false
    let pre = head, cur = head;
    while(cur && cur.next) {
        pre = pre.next;
        cur = cur.next.next;
        if(pre === cur) {
            return true
        }
    }

    return false
};
/**
 * 
 * 给定一个链表,返回链表开始入环的第一个节点 【142】
 * 
 * 
 * 快慢指针求相遇
 * 假设入环节点距离 头节点的距离为 a; 快慢指针相遇点距离头节点的距离分别为 b 和 c (两个弧长 , b为慢指针走过的圆弧长度),
 * 快指针走的长度:a + n(b+C) + c
 * 慢指针走的长度:a + b
 * 快指针的速度 = 慢指针的2倍 所以距离也是慢指针的2倍
 * a + n(b+c) + c = 2(a+b) ---> 导出 a = (n-1)(b+c) + c 
 * 因为 b + c 等于 环的长度,所以相当于走了很多圈。 实际导出 a = c  即: 入环位置 距离 头节点和 距离 快慢指针相遇节点的 距离相等
 * 所以,在相遇节点走c步,头节点也走c步,两个节点相遇的位置即为入口位置
*/
var detectCycle = function(head) {
    if (!head) return null;
    let pre = head, cur = head;
    while(cur && cur.next) {
        pre = pre.next;
        cur = cur.next.next
        if (pre === cur) {
            let temp = head;
            while(temp != pre) {
                temp = temp.next
                pre = pre.next
            }
            return temp
        }
    }
    return null
};
/**
 * 
 * 快乐数
 * 
 **/
var getSum = function(n) {
    let sum = 0,cur = 0;
    //  从最高位数加到个位数
    while( n > 0) {
        cur = Math.floor(n%10)
        sum = Math.floor(sum + cur**2)
        // 换下一位数字
        n = Math.floor(n/10)
    }
    return sum
}

let isHappy = function (n) {
    let slow = n;
    let fast = getSum(n);
    while(fast !== 1 && fast !== slow){
        slow = getSum(slow);
        fast = getSum(getSum(fast));
    }
    return fast === 1;
};
console.log(isHappy(4))

/**
 * 
 * 两数相加
 * 
**/

var addTwoNumbers = function(l1, l2) {
    let addOne = 0
    let sum = new ListNode('0')
    let head = sum
    // 如果有进位的话,也是需要运算的
    while (addOne || l1 || l2) {
        // 需要判断值是否存在,因为可能存在位数不同的情况
        let val1 = l1 !== null ? l1.val : 0
        let val2 = l2 !== null ? l2.val : 0
        let r1 = val1 + val2 + addOne
        // 因为数字精度不准确所以需要单独处理
        addOne = r1 >= 10 ? 1 : 0
        // 只存放个位的数字
        sum.next = new ListNode(r1 % 10)
        sum = sum.next 
        if (l1) l1 = l1.next 
        if (l2) l2 = l2.next 
    }
    return head.next
};

/**
 * 
 * 反转链表
 * 
*/
// 方法1 pre 和 cur next
var reverseList = function(head) {
    // 判断长度
    if(head == null || head.next == null) return head
    let pre = null;
    let current = head;
    // 需要声明next变量,来保障反转过后的指向(重新链接起来各个节点)
    while(current) {
      let currenNext = current.next
        current.next = pre
        pre = current
        current = currenNext
    }
    return pre
};
// 方法二递归
// 抽象为 head->next 和head->next->next的反转过程
var reverseList = function(head) {
    // 判断长度
    if(head == null || head.next == null) return head
    let node = reverseList(head.next)
    head.next.next = head
    head.next = null
    return node
};

/**
 * 
 * 反转指定位置【m,n】的链表
 * 
*/
var reverseBetween = function(head, left, right) {
    // 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
    const dummyNode = new ListNode(-1);
    dummyNode.next = head;

    let pre = dummyNode;
    // 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
    // 建议写在 for 循环里,语义清晰
    for (let i = 0; i < left - 1; i++) {
        pre = pre.next;
    }

    // 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
    let rightNode = pre;
    for (let i = 0; i < right - left + 1; i++) {
        rightNode = rightNode.next;
    }

    // 第 3 步:切断出一个子链表(截取链表)
    let leftNode = pre.next;
    let curr = rightNode.next;

    // 注意:切断链接
    pre.next = null;
    rightNode.next = null;

    // 第 4 步:同第 206 题,反转链表的子区间
    reverseLinkedList(leftNode);

    // 第 5 步:接回到原来的链表中
    pre.next = rightNode;
    leftNode.next = curr;
    return dummyNode.next;
};

const reverseLinkedList = (head) => {
    let pre = null;
    let cur = head;

    while (cur) {
        const next = cur.next;
        cur.next = pre;
        pre = cur;
        cur = next;
    }
}
// 穿针引线
var reverseBetween = function(head, left, right) {
    const retNode = new ListNode(-1)
    retNode.next = head
    let leftNodePre = retNode
    // leftNode 的前一个节点
    for(let i = 0; i < left -1; i++) {
        leftNodePre = leftNodePre.next
    }
    /***
    * @name 穿针引线
    * @目标 让需要反转的每一个节点都插入到左侧启动的右边
    * @param leftNodePre 始终表示开始反转链表前的节点
    * @param rightNode   反转链表的头
    * @param rightNodeNext   下一个需要反转的节点
    *
    **/
    
    const rightNode = leftNodePre.next
    for(let i = 0; i < right - left; ++i) {
        const rightNodeNext = rightNode.next
        rightNode.next = rightNodeNext.next
        rightNodeNext.next = leftNodePre.next   
        leftNodePre.next = rightNodeNext
    }
    return retNode.next
 };
 /**
  * 
  * 按照k组反转
  * 
 */
 const myReverse = (head, tail) => {
    let prev = tail.next;
    let p = head;
    while (prev !== tail) {
        const nex = p.next;
        p.next = prev;
        prev = p;
        p = nex;
    }
    return [tail, head];
}
var reverseKGroup = function(head, k) {
    const hair = new ListNode(0);
    hair.next = head;
    let pre = hair;

    while (head) {
        let tail = pre;
        // 查看剩余部分长度是否大于等于 k
        for (let i = 0; i < k; ++i) {
            tail = tail.next;
            if (!tail) {
                return hair.next;
            }
        }
        const nex = tail.next;
        [head, tail] = myReverse(head, tail);
        // 把子链表重新接回原链表
        pre.next = head;
        tail.next = nex;
        pre = tail;
        head = tail.next;
    }
    return hair.next;
};

/**
 * 
 * 旋转链表
 * 
*/

var rotateRight = function(head, k) {
    if( k=== 0 || !head || !head.next) return head
    // 环长度
    let listLenth = 1;
    // 尾接单
    let tail = head;
    while(tail.next) {
        listLenth++;
        tail = tail.next
    }
    // 成环
    let sum = listLenth - k % listLenth
    if( sum === listLenth) {
        return head
    }
    tail.next = head
    while(sum) {
        tail = tail.next
        sum--
    }
    head = tail.next
    tail.next = null
    return head
};

/**
 * 
 * 从倒数第n个位置删除接单
 * 
*/

// 强撸法
var removeNthFromEnd = function(head, n) {
    let preHead = new ListNode(-1);
    preHead.next = head;
    let len = 0;
    let first = head;
    while(first){
        len++;
        first = first.next;
    }
    len -= n;
    first = preHead;
    while(len != 0){
        len--;
        first = first.next;
    }
    first.next = first.next.next;
    return preHead.next;
};

// 快慢指针
var removeNthFromEnd = function(head, n) {
    let preHead = new ListNode(-1);
    preHead.next = head;
    let fastNode = head
    let slowNode = preHead
    while(n) {
        n = n - 1
        fastNode = fastNode.next
    }
    while(fastNode!=null) {
        slowNode = slowNode.next
        fastNode = fastNode.next
    }
    slowNode.next = slowNode.next.next
    return preHead.next
};


原文地址:https://www.cnblogs.com/Rembang/p/15038780.html