2.两数相加
 19.删除链表的倒数第 N 个结点
 21.合并两个有序链表
 23.合并 K 个升序链表
 141.环形链表
 142.环形链表 II
 160.相交链表
 876.链表的中间结点
 25.K 个⼀组翻转链表
 83.删除排序链表中的重复元素
 92.反转链表 II
 234.回⽂链表
2. 两数相加 (中等)

 一次遍历计算完成,需注意:
- 构建空的头结点,不用处理一开始指针指向null的情况
 - 执行加法,考虑进位的情况。新链表要记得移动。
 - 两个链表不等长
 - 读完两个链表后,最后一次进位仍要处理
 
21. 合并两个有序链表 (简单)

 比上一题简单,不用创建新节点。
 while循环的条件是 &&关系
23. 合并K个升序链表 (困难)

难题:JavaScript没有 小顶堆
数组reduce()方法详解
arr.reduce(callback,[initialValue])
 reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
})
console.log(arr, sum);
打印结果:
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10
 
index是从1开始的,第一次的prev的值是数组的第一个值。数组长度是4,但是reduce函数循环3次。
var  arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
},0) //注意这里设置了初始值
console.log(arr, sum);
打印结果:
0 1 0
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10
 
例子index是从0开始的,第一次的prev的值是我们设置的初始值0,数组长度是4,reduce函数循环4次。
23.合并K个排序链表,哈希表排序,击败100%!
 
什么是二叉堆
小顶堆代码:
 代码来源于leetcode
class MinHeap {
    constructor() {
        this.heap = [];
    }
    // 交换节点位置
    swap(i1, i2) {
        [this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
    }
    // 获得父节点
    getParentIndex(i) {
        return Math.floor((i - 1) / 2);
        // return (i - 1) >> 1;
    }
    // 获得左节点
    getleftIndex(i) {
        return 2 * i + 1;
    }
    // 获得右节点
    getrightIndex(i) {
        return 2 * i + 2;
    }
    // 上移
    shiftUp(index) {
        if (index == 0) return;
        const parentIndex = this.getParentIndex(index);
        if (this.heap[parentIndex] && this.heap[parentIndex].val > this.heap[index].val) {
            this.swap(parentIndex, index);
            this.shiftUp(parentIndex);
        }
    }
    // 下移
    shiftDown(index) {
        let leftIndex = this.getleftIndex(index);
        let rightIndex = this.getrightIndex(index);
        if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[leftIndex].val) {
            leftIndex = rightIndex
        }
        if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
            this.swap(leftIndex, index);
            this.shiftDown(leftIndex);
        }
        // if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
        //     this.swap(rightIndex, index);
        //     this.shiftDown(rightIndex);
        // }
    }
    // 插入
    insert(value) {
        this.heap.push(value);
        this.shiftUp(this.heap.length - 1);
    }
    // 删除堆顶
    // 用数组尾部元素替换堆顶(直接删除堆顶会破坏堆结构)
    pop() {
        if(this.size() == 1) return this.heap.shift()
        const top = this.heap[0]
        // pop()方法删除数组最后一个元素并返回,赋值给堆顶
        this.heap[0] = this.heap.pop();
        // 对堆顶重新排序
        this.shiftDown(0);
        return top
    }
    // 获取堆顶
    peek() {
        return this.heap[0];
    }
    // 获取堆的大小
    size() {
        return this.heap.length;
    }
}
 

 最后该题代码:
var mergeKLists = function(lists) {
    let head = p = new ListNode(0)
    const heap = new MinHeap()
    // 插入k个升序链表的头部节点
    for (let k of lists) {
        if(k) heap.insert(k)
    }
    // 不断的地比较最小堆中k个节点的大小
    while (heap.size()) {
        const val = heap.pop()
        p.next = val
        p = p.next
        if (val.next) heap.insert(val.next)
    }
    return head.next;
};
 
160. 相交链表 (简单)

 这题看一幅图就明白了,重点在于怎么简洁实现:一个while循环可解决,条件是p1 == p2
 
复习:剑指 Offer 52. 两个链表的第一个公共节点
细节: 这里把最后None也当成节点
var getIntersectionNode = function(headA, headB) {
    let p1 = headA, p2 = headB
    while (p1 != p2) {
        if(p1) p1 = p1.next;
        else p1 = headB;
        if(p2) p2 = p2.next
        else p2 = headA
    }
    return p1;
};
 
方法二:类似找倒数第K个节点,因为如果有相交节点,最后肯定是一起走的(相同长度)。比较A和B的长度,使两条链表走相同的路程。
 既然「寻找两条链表的交点」的核心在于让 p1 和 p2 两个指针能够同时到达相交节点 c1,那么可以通过预先计算两条链表的长度来做到这一点,
var getIntersectionNode = function(headA, headB) {
    let lenA = 0, lenB = 0;
    for (let p1 = headA; p1 != null; p1 = p1.next) lenA++;
    for (let p2 = headB; p2 != null; p2 = p2.next) lenB++;
    p1 = headA, p2 = headB;
    if (lenA > lenB) {
        for (let i = 0; i < lenA - lenB; i++) p1 = p1.next;
    } else {
        for (let i = 0; i < lenB - lenA; i++) p2 = p2.next;
    }
    // 看两个指针是否会相同,p1 == p2 时有两种情况:
    // 1、要么是两条链表不相交,他俩同时走到尾部空指针
    // 2、要么是两条链表相交,他俩走到两条链表的相交点
    while (p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
};










