一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

目录

一、数据结构与算法简介

1、理论
* 数据结构:栈、队列、集合、链表、字典、树、图、堆
* 进阶算法:冒泡算法、选择算法、插入算法、归并算法、快速算法、顺序算法、二分搜索
* 算法设计思想:分而治之、动态规则、贪心、回溯
* 重点关注:数据结构与算法的特点、应用场景、js实现、时间/空间复杂度
2、刷题
* 刷题网站:leetcode
* 重点关注:通用套路、时间/空间复杂度分析和优化
3、数据结构与算法
* 是什么:
    - 数据结构:计算机存储、组织数据的方式
    - 算法:一系列解决问题的清晰指令
* 程序:数据结构 + 算法
* 关系:数据结构为算法提供服务,算法围绕数据结构操作

二、时间空间复杂度计算

1、时间复杂度计算
  • 是什么
* 一个函数,用大O表示,比如O(1)、O(n)、O(logN)...
* 定性描述该算法的运行时间
  • O(1)
let i = 0
i += 1
  • O(n)
// O(1) + O(n) = O(n)
for (let i = 0; i < n; i++) {
    console.log(i)
}
  • O(n^2)
// O(n) * O(n) = O(n^2)
for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
        console.log(i, j)
    }
}
  • O(logN)
// 如果a的x次方等于N,其中a>0且a不等于1,那么数x叫做以a为底N的对数
// 2的x次方等于N,x等于logN(计算机中2会省略不写,数学中2不可以省略)
let i = 1
while (i < n) {
    console.log(i)
    i *= 2
}
2、空间复杂度计算
  • 是什么
* 一个函数,用大O表示,比如O(1)、O(n)、O(n^2)...
* 算法在运行过程中临时占用存储空间大小的量度
  • O(1)
let i = 0
i += 1
  • O(n)
const list = []
for (let i = 0; i < n; i++) {
    list.push(i)
}
  • O(n^2)
const matrix = []
for (let i = 0; i < n; i++) {
    matrix.push([])
    for (let j = 0; j < n; j++) {
        matrix[i].push(j)
    }
}

三、数据结构-栈

1、栈简介
// 1、栈是一种后进先出的数据结构
// 2、栈常用操作:push、pop、stack[stack.length-1]
const stack = []
stack.push(1)
stack.push(2)
const item1 = stack.pop()
const item2 = stack.pop()
2、栈的应用场景
* 十进制转二进制
* 判断字符串的括号是否有效
* 函数调用堆栈
3、力扣解题(20. 有效的括号)
var isValid = function (s) {
    const lp = "([{"
    if (s.length % 2 === 1 || lp.indexOf(s[0]) === -1) {
        return false
    }
    const stack = []
    const rp = ")]}"
    for (let i = 0; i < s.length; i++) {
        if (lp.indexOf(s[i]) !== -1) {
            stack.push(s[i])
        } else {
            const top = stack.pop()
            if (lp.indexOf(top) !== rp.indexOf(s[i])) {
                return false
            }
        }
    }
    return !stack.length
};
4、力扣解题(144. 二叉树的前序遍历)
// 二叉树前序:根-左-右
// 二叉树中序:左-根-右
// 二叉树后序:左-右-根
var preorderTraversal = function (root) {
    let arr = []
    let preorder = function (node, arr) {
        if (node === null) return arr
        arr.push(node.val)
        preorder(node.left, arr)
        preorder(node.right, arr)
    }
    preorder(root, arr)
    return arr
};

四、数据结构-队列

1、队列简介
// 1、队列是一种先进先出的数据结构
// 2、队列常用操作:push、shift、queue[0]
const queue = []
queue.push(1)
queue.push(2)
const item1 = queue.shift()
const item2 = queue.shift()
2、队列的应用场景
* 食堂排队打饭
* js异步中的任务队列
* 计算最近请求次数
3、力扣解题(933. 最近的请求次数)
var RecentCounter = function () {
    this.q = []
};
RecentCounter.prototype.ping = function (t) {
    this.q.push(t)
    while (this.q[0] < t - 3000) {
        this.q.shift()
    }
    return this.q.length
};

五、数据结构-链表

1、链表简介
/**
 * 一、链表是什么?
 *     - 多个元素组成的列表
 *     - 元素存储不连续,用next指针连在一起
 * 二、数组vs链表
 *     - 数组:增删非首尾元素时往往需要移动元素
 *     - 链表:增删非首尾元素,不需要移动元素,只需要更改next的指向即可
 * 三、链表常用操作:修改next、遍历链表
 */
const a = {val: "a"}
const b = {val: "b"}
const c = {val: "c"}
a.next = b
b.next = c
// 遍历
let p = a
while (p) {
    // console.log(p)
    p = p.next
}
// 添加
const d = {val: "d"}
b.next = d
d.next = c
console.log(a)
// 删除
// b.next = c
// console.log(a)
2、力扣解题(237. 删除链表中的节点)
var deleteNode = function (node) {
    node.val = node.next.val
    node.next = node.next.next
};
3、力扣解题(206. 反转链表)
var reverseList = function(head) {
    let p = null
    while (head) {
        head.prev = head.next
        head.next = p
        p = head
        head = head.prev
    }
    return p
};
4、力扣解题(2. 两数相加)
var addTwoNumbers = function (l1, l2) {
    const l3 = new ListNode(0)
    let p = l3
    let carry = 0
    while (l1 || l2) {
        const v1 = l1 ? l1.val : 0
        const v2 = l2 ? l2.val : 0
        p.next = new ListNode((v1 + v2 + carry) % 10)
        carry = Math.floor((v1 + v2 + carry) / 10)
        if (l1) {
            l1 = l1.next
        }
        if (l2) {
            l2 = l2.next
        }
        p = p.next
    }
    if (carry) {
        p.next = new ListNode(carry)
    }
    return l3.next
};
5、力扣解题(83. 删除排序链表中的重复元素)
var deleteDuplicates = function (head) {
    let p = head
    while (p && p.next) {
        if (p.val === p.next.val) {
            p.next = p.next.next
        } else {
            p = p.next
        }
    }
    return head
};
6、力扣解题(141. 环形链表)
var hasCycle = function (head) {
    while (head) {
        if (head.visited) {
            return true
        }
        head.visited = true
        head = head.next
    }
    return false
};

六、数据结构-集合

1、集合简介
/**
 * 一、什么是集合?
 *     - 一种无序且唯一的数据结构
 *     - es6中有集合,名为Set
 *     - 集合的常用操作:去重、判断某元素是否在集合中、求交集
 */
// 去重
const arr = [1, 1, 2, 2]
const arr2 = [...new Set(arr)]

// 判断元素是否在集合中
const set = new Set(arr)
const has = set.has(3)

// 求交集
const set2 = new Set([2, 3])
const set3 = new Set([...set].filter(item => set2.has(item)))
2、力扣解题(349. 两个数组的交集)
var intersection = function (nums1, nums2) {
    return [...new Set(nums1)].filter(item => nums2.includes(item))
};

七、数据结构-字典

1、字典简介
/**
 * 一、什么是字典?
 *     - 与集合类似,字典也是一种存储唯一值的数据结构,但它是以键值对的形式来存储
 *     - es6中有字典,名为Map
 *     - 字典的常用操作:键值对的增删改查
 */
const m = new Map();

// 增
m.set("a", "aaa")
m.set("b", "bbb")

// 删
m.delete("b")
// m.clear()

// 改
m.set("a", "aaaaaa")
2、力扣解题(1. 两数之和)
var twoSum = function (nums, target) {
    const map = new Map()
    for (let i = 0; i < nums.length; i++) {
        const n = nums[i]
        const n2 = target - n
        if (map.has(n2)) {
            return [map.get(n2), i]
        } else {
            map.set(n, i)
        }
    }
};
3、力扣解题(3. 无重复字符的最长子串)
var lengthOfLongestSubstring = function (s) {
    let l = 0
    let res = 0
    const map = new Map();
    for (let i = 0; i < s.length; i++) {
        if (map.has(s[i]) && map.get(s[i]) >= l) {
            l = map.get(s[i]) + 1
        }
        res = Math.max(res, i - l + 1)
        map.set(s[i], i)
    }
    return res
};
4、力扣解题(76. 最小覆盖子串)
var minWindow = function (s, t) {
    let l = 0
    let r = 0
    const need = new Map()
    for (const c of t) {
        need.set(c, need.has(c) ? need.get(c) + 1 : 1)
    }
    let needType = need.size
    let res = ""
    while (r < s.length) {
        const c = s[r]
        if (need.has(c)) {
            need.set(c, need.get(c) - 1)
            if (need.get(c) === 0) needType -= 1
        }
        while (needType === 0) {
            const newRes = s.substring(l, r + 1)
            if (!res || newRes.length < res.length) res = newRes
            const c2 = s[l]
            if (need.has(c2)) {
                need.set(c2, need.get(c2) + 1)
                if (need.get(c2) === 1) needType += 1
            }
            l += 1
        }
        r += 1
    }
    return res
};

八、数据结构-树

1、树简介
/**
 * 一、什么是树?
 *     - 一种分层数据的抽象模型
 *     - 前端工作中常见的树包括:dom树、级联选择、树形控件
 *     - 树的常用操作:深度/广度优先遍历、先中后序遍历
 * 二、什么是深度/广度优先遍历
 *     - 深度优先遍历:尽可能深的搜索树的分支
 *     - 广度优先遍历:先访问离根节点最近的节点
 */
const tree = {
    val: "a",
    children: [{
        val: "b",
        children: [{
            val: "d",
            children: []
        }, {
            val: "e",
            children: []
        }]
    }, {
        val: "c",
        children: [{
            val: "f",
            children: []
        }, {
            val: "g",
            children: []
        }]
    }]
}

/**
 * 三、深度优先遍历算法口诀
 *     - 访问根节点
 *     - 对根节点的children挨个进行深度优先遍历
 */
const dfs = (root) => {
    console.log(root.val)
    root.children.forEach(dfs)
}
dfs(tree)

/**
 * 四、广度优先遍历算法口诀
 *     - 新建一个队列,把根节点入队
 *     - 把对头出队并访问
 *     - 把对头的children挨个入队
 *     - 重复第二、三步,直到队列为空
 */
const bfs = (root) => {
    const q = [root]
    while (q.length > 0) {
        const n = q.shift()
        console.log(n.val)
        n.children.forEach(child => {
            q.push(child)
        })
    }
}
bfs(tree)
2、二叉树的先中后序遍历
/**
 * 一、二叉树是什么?
 *     - 树中每个节点最多只能有两个子节点
 */
const bt = {
    val: 1,
    left: {
        val: 2,
        left: {
            val: 4,
            left: null,
            right: null,
        },
        right: {
            val: 5,
            left: null,
            right: null,
        },
    },
    right: {
        val: 3,
        left: {
            val: 6,
            left: null,
            right: null,
        },
        right: {
            val: 7,
            left: null,
            right: null,
        },
    },
}

/**
 * 二、先序遍历算法口诀
 *     - 访问根节点
 *     - 对根节点的左子树进行先序遍历
 *     - 对根节点的右子树进行先序遍历
 */
const preorder = (root) => {
    if (root) {
        console.log(root.val)
        preorder(root.left)
        preorder(root.right)
    }
}
preorder(bt)

/**
 * 三、中序遍历算法口诀
 *     - 对根节点的左子树进行中序遍历
 *     - 访问根节点
 *     - 对根节点的右子树进行中序遍历
 */
const inorder = (root) => {
    if (root) {
        inorder(root.left)
        console.log(root.val)
        inorder(root.right)
    }
}
inorder(bt)

/**
 * 四、后序遍历算法口诀
 *     - 对根节点的左子树进行后序遍历
 *     - 对根节点的右子树进行后序遍历
 *     - 访问根节点
 */
const postorder = (root) => {
    if (root) {
        postorder(root.left)
        postorder(root.right)
        console.log(root.val)
    }
}
postorder(bt)
3、二叉树的先中后序遍历(非递归版)
const preorder = (root) => {
    if (root) {
        const stack = [root]
        while (stack.length) {
            const n = stack.pop()
            console.log(n.val)
            if (n.right) stack.push(n.right)
            if (n.left) stack.push(n.left)
        }
    }
}
preorder(bt)

const inorder = (root) => {
    if (root) {
        const stack = []
        let p = root
        while (stack.length || p) {
            while (p) {
                stack.push(p)
                p = p.left
            }
            const n = stack.pop()
            console.log(n.val)
            p = n.right
        }
    }
}
inorder(bt)

const postorder = (root) => {
    if (root) {
        const outputStack = []
        const stack = [root]
        while (stack.length) {
            const n = stack.pop()
            outputStack.push(n)
            if (n.left) stack.push(n.left)
            if (n.right) stack.push(n.right)
        }
        while (outputStack.length) {
            const n = outputStack.pop()
            console.log(n.val)
        }
    }
}
postorder(bt)
4、力扣解题(104. 二叉树的最大深度)
var maxDepth = function (root) {
    let res = 0
    const dfs = (n, l) => {
        if (!n) {
            return;
        }
        if (!n.left && !n.right) {
            res = Math.max(res, l)
        }
        dfs(n.left, l + 1)
        dfs(n.right, l + 1)
    }
    dfs(root, 1)
    return res
};
5、力扣解题(111. 二叉树的最小深度)
var minDepth = function (root) {
    if (!root) {
        return 0
    }
    const q = [[root, 1]]
    while (q.length) {
        const [n, l] = q.shift()
        if (!n.left && !n.right) {
            return l
        }
        if (n.left) q.push([n.left, l + 1])
        if (n.right) q.push([n.right, l + 1])
    }
};
6、力扣解题(102. 二叉树的层序遍历)
var levelOrder = function (root) {
    if (!root) return []
    const q = [root]
    const res = []
    while (q.length) {
        let len = q.length
        res.push([])
        while (len--) {
            const n = q.shift()
            res[res.length - 1].push(n.val)
            if (n.left) q.push(n.left)
            if (n.right) q.push(n.right)
        }
    }
    return res
};
7、力扣解题(94. 二叉树的中序遍历)
var inorderTraversal = function (root) {
    const res = []
    const stack = []
    let p = root
    while (stack.length || p) {
        while (p) {
            stack.push(p)
            p = p.left
        }
        const n = stack.pop()
        res.push(n.val)
        p = n.right
    }
    return res
};
8、力扣解题(112. 路径总和)
var hasPathSum = function (root, targetSum) {
    if (!root) return false
    let res = false
    const dfs = (n, s) => {
        if (!n.left && !n.right && s === targetSum) {
            res = true
        }
        if (n.left) dfs(n.left, s + n.left.val)
        if (n.right) dfs(n.right, s + n.right.val)
    }
    dfs(root, root.val)
    return res
};

九、数据结构-图

1、图简介
/**
 * 一、图是什么?
 *     - 图是网络结构的抽象模型,是一组由边连接的节点
 *     - 图可以表示任何二元关系,比如道路、航班
 *     - 图的表示法:领接矩阵,领接表、关联矩阵
 * 二、图的常用操作
 *     - 深度优先遍历:尽可能深的搜索图的分支
 *     - 广度优先遍历:先访问离根节点最近的节点
 */
const graph = {
    0: [1, 2],
    1: [2],
    2: [0, 3],
    3: [3],
}

/**
 * 三、深度优先遍历算法口诀
 *     - 访问根节点
 *     - 对根节点的没访问过的相邻节点挨个进行深度优先遍历
 */
/*
const visited = new Set()
const dfs = (n) => {
    console.log(n)
    visited.add(n)
    graph[n].forEach(c => {
        if (!visited.has(c)) {
            dfs(c)
        }
    })
}
dfs(2)*/

/**
 * 四、广度优先遍历算法口诀
 *     - 新建一个队列,把根节点入队
 *     - 把队头出队并访问
 *     - 把队头的没访问过的相邻节点入队
 *     - 重复第二、三步,直到队列为空
 */
const visited = new Set()
visited.add(2)
const q = [2]
while (q.length) {
    const n = q.shift()
    console.log(n)
    graph[n].forEach(c => {
        if (!visited.has(c)) {
            q.push(c)
            visited.add(c)
        }
    })
}
2、力扣解题(65. 有效数字)
var isNumber = function (s) {
    const graph = {
        0: {"blank": 0, "sign": 1, ".": 2, "digit": 6,},
        1: {"digit": 6, ".": 2,},
        2: {"digit": 3,},
        3: {"digit": 3, "e": 4,},
        4: {"digit": 5, "sign": 7,},
        5: {"digit": 5,},
        6: {"digit": 6, ".": 3, "e": 4,},
        7: {"digit": 5,},
    }
    let state = 0
    for (let c of s.trim().toLowerCase()) {
        if (c >= "0" && c <= "9") {
            c = "digit"
        } else if (c === " ") {
            c = "blank"
        } else if (c === "+" || c === "-") {
            c = "sign"
        }
        state = graph[state][c]
        if (state === undefined) {
            return false
        }
    }
    if (state === 3 || state === 5 || state === 6) {
        return true
    }
    return false
};
3、力扣解题(417. 太平洋大西洋水流问题)
var pacificAtlantic = function (heights) {
    if (!heights || !heights[0]) {
        return []
    }
    const m = heights.length
    const n = heights[0].length
    const flow1 = Array.from({length: m}, () => new Array(n).fill(false))
    const flow2 = Array.from({length: m}, () => new Array(n).fill(false))
    const dfs = (r, c, flow) => {
        flow[r][c] = true;
        [[r - 1, c], [r + 1, c], [r, c - 1], [r, c + 1]].forEach(([nr, nc]) => {
            if (
                // 保证在矩阵中
                nr >= 0 && nr < m &&
                nc >= 0 && nc < n &&
                // 防止死循环
                !flow[nr][nc] &&
                // 保证逆流而上
                heights[nr][nc] >= heights[r][c]
            ) {
                dfs(nr, nc, flow)
            }
        })
    }
    // 沿着海岸线逆流而上
    for (let r = 0; r < m; r++) {
        dfs(r, 0, flow1)
        dfs(r, n - 1, flow2)
    }
    for (let c = 0; c < n; c++) {
        dfs(0, c, flow1)
        dfs(m - 1, c, flow2)
    }
    // 收集能流到两个大洋里的坐标
    const res = []
    for (let r = 0; r < m; r++) {
        for (let c = 0; c < n; c++) {
            if (flow1[r][c] && flow2[r][c]) {
                res.push([r, c])
            }
        }
    }
    return res
};
4、力扣解题(133. 克隆图)
var cloneGraph = function (node) {
    if (!node) return
    const visited = new Map()
    visited.set(node, new Node(node.val))
    const q = [node]
    while (q.length) {
        const n = q.shift();
        (n.neighbors || []).forEach(ne => {
            if (!visited.has(ne)) {
                q.push(ne)
                visited.set(ne, new Node(ne.val))
            }
            visited.get(n).neighbors.push(visited.get(ne))
        })
    }
    return visited.get(node);
};

十、数据结构-堆

1、堆简介
/**
 * 一、堆是什么?
 *     - 堆是一种特殊的完全二叉树
 *     - 所有的节点都大于等于(最大堆)或小于等于(最小堆)它的子节点
 * 二、js中的堆
 *     - js中通常用数组表示堆
 *     - 左侧子节点的位置是2*index+1
 *     - 右侧子节点的位置是2*index+2
 *     - 父节点位置是(index-1)/2
 * 三、堆的应用
 *     - 堆能高效、快速地找出最大值和最小值,时间复杂度:O(1)
 *     - 找出第k个最大(小)元素
 * 四、第k个最大元素
 *     - 构建一个最小堆,并将元素依次插入堆中
 *     - 当堆的容量超过k,就删除堆顶
 *     - 插入结束后,堆顶就是第k个最大元素
 */
2、javascript实现:最小堆类
/**
 * 一、实现步骤
 *     - 在类里,声明一个数组,用来装元素
 *     - 主要方法:插入、删除堆顶、获取堆顶、获取堆大小
 */
class MinHeap {
    constructor() {
        this.heap = []
    }

    /**
     * 二、插入
     *     - 将值插入堆的底部,即数组的尾部
     *     - 然后上移:将这个值和它的父节点进行交换,直到父节点小于等于这个插入的值
     *     - 大小为k的堆中插入元素的时间复杂度为O(logk)
     */
    insert(value) {
        this.heap.push(value)
        this.shiftUp(this.heap.length - 1)
    }

    shiftUp(index) {
        if (index === 0) {
            return
        }
        const parentIndex = this.getParentIndex(index)
        if (this.heap[parentIndex] > this.heap[index]) {
            this.swap(parentIndex, index)
            this.shiftUp(parentIndex)
        }
    }

    getParentIndex(i) {
        // 等价:Math.floor((i - 1) / 2)
        return (i - 1) >> 1
    }

    swap(i1, i2) {
        const temp = this.heap[i1]
        this.heap[i1] = this.heap[i2]
        this.heap[i2] = temp
    }

    /**
     * 三、删除堆顶
     *     - 用数组尾部元素替换堆顶(直接删除堆顶会破坏堆结构)
     *     - 然后下移:将新堆顶和它的子节点进行交换,直到子节点大于等于这个新堆顶
     *     - 大小为k的堆中删除堆顶的时间复杂度为O(logk)
     */
    pop() {
        this.heap[0] = this.heap.pop()
        this.shiftDown(0)
    }

    shiftDown(index) {
        const leftIndex = this.getLeftIndex(index)
        const rightIndex = this.getRightIndex(index)
        if (this.heap[leftIndex] < this.heap[index]) {
            this.swap(leftIndex, index)
            this.shiftDown(leftIndex)
        }
        if (this.heap[rightIndex] < this.heap[index]) {
            this.swap(rightIndex, index)
            this.shiftDown(rightIndex)
        }
    }

    getLeftIndex(i) {
        return i * 2 + 1
    }

    getRightIndex(i) {
        return i * 2 + 2
    }

    /**
     * 四、获取堆顶和堆的大小
     *     - 获取堆顶:返回数组的头部
     *     - 获取堆的大小:返回数组的长度
     */
    peek() {
        return this.heap[0]
    }

    size() {
        return this.heap.length
    }
}

const h = new MinHeap()
h.insert(3)
h.insert(2)
h.insert(1)
h.pop()
3、力扣解题(215. 数组中的第K个最大元素)
var findKthLargest = function (nums, k) {
    const h = new MinHeap()
    nums.forEach(n => {
        h.insert(n)
        if (h.size() > k) {
            h.pop()
        }
    })
    return h.peek()
};
4、力扣解题(347. 前K个高频元素)
// 最小堆类需做改动的方法
class MinHeap {
    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) {
        const leftIndex = this.getLeftIndex(index)
        const rightIndex = this.getRightIndex(index)
        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)
        }
    }
}
var topKFrequent = function (nums, k) {
    const map = new Map()
    nums.forEach(n => {
        map.set(n, map.has(n) ? map.get(n) + 1 : 1)
    })
    const h = new MinHeap()
    map.forEach((value, key) => {
        h.insert({value, key})
        if (h.size() > k) {
            h.pop()
        }
    })
    return h.heap.map(a => a.key)
};
5、力扣解题(23. 合并K个升序链表)
// 最小堆类需做改动的方法
class MinHeap {
    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)
        }
    }

    pop() {
        if (this.size() === 1) return this.heap.shift()
        const top = this.heap[0]
        this.heap[0] = this.heap.pop()
        this.shiftDown(0)
        return top
    }

    shiftDown(index) {
        const leftIndex = this.getLeftIndex(index)
        const rightIndex = this.getRightIndex(index)
        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)
        }
    }
}
var mergeKLists = function (lists) {
    const res = new ListNode(0)
    let p = res
    const h = new MinHeap()
    lists.forEach(l => {
        if (l) h.insert(l)
    })
    while (h.size()) {
        const n = h.pop()
        p.next = n
        p = p.next
        if (n.next) h.insert(n.next)
    }
    return res.next
};
posted on 2022-06-23 00:42  一路繁花似锦绣前程  阅读(32)  评论(0编辑  收藏  举报