前端实现合并两个已排序链表

前端实现合并两个链表,通常指的是将两个已排序的链表合并成一个新的已排序链表。以下是一个使用 JavaScript 实现此功能的示例,并附带解释。

链表节点定义

首先,我们需要定义一个链表节点的结构。

js 复制代码
class ListNode {
    constructor(val, next = null) {
        this.val = val;
        this.next = next;
    }
}

合并两个链表函数

接下来,我们实现 mergeTwoLists 函数。这个函数会创建一个虚拟头节点(dummy head),然后遍历两个输入链表,将较小值的节点添加到新链表中。

js 复制代码
/**
 * 合并两个已排序的链表
 * @param {ListNode} l1 第一个链表的头节点
 * @param {ListNode} l2 第二个链表的头节点
 * @return {ListNode} 合并后链表的头节点
 */
function mergeTwoLists(l1, l2) {
    // 创建一个虚拟头节点,便于处理合并后链表的头部
    const dummyHead = new ListNode(0);
    let current = dummyHead; // current 指向当前构建的链表的最后一个节点

    // 当两个链表都还有节点时,比较它们的值
    while (l1 !== null && l2 !== null) {
        if (l1.val <= l2.val) {
            current.next = l1; // 将 l1 的当前节点添加到新链表
            l1 = l1.next;      // 移动 l1 到下一个节点
        } else {
            current.next = l2; // 将 l2 的当前节点添加到新链表
            l2 = l2.next;      // 移动 l2 到下一个节点
        }
        current = current.next; // 移动 current 到新添加的节点
    }

    // 如果其中一个链表还有剩余节点,直接将剩余部分连接到新链表
    if (l1 !== null) {
        current.next = l1;
    } else if (l2 !== null) {
        current.next = l2;
    }

    // 返回虚拟头节点的下一个节点,即合并后链表的真正头节点
    return dummyHead.next;
}

示例用法

现在,我们来创建您提供的链表 ab,并使用 mergeTwoLists 函数进行合并。

js 复制代码
// 辅助函数:从数组创建链表
function createLinkedList(arr) {
    if (arr.length === 0) {
        return null;
    }
    let head = new ListNode(arr[0]);
    let current = head;
    for (let i = 1; i < arr.length; i++) {
        current.next = new ListNode(arr[i]);
        current = current.next;
    }
    return head;
}

// 辅助函数:将链表转换为数组(方便打印和验证)
function linkedListToArray(head) {
    const arr = [];
    let current = head;
    while (current !== null) {
        arr.push(current.val);
        current = current.next;
    }
    return arr;
}

// 创建链表 a: 1 -> 3 -> 5 -> 7 -> 9
const listA = createLinkedList([1, 3, 5, 7, 9]);
console.log("链表 a:", linkedListToArray(listA)); // 链表 a: [1, 3, 5, 7, 9]

// 创建链表 b: 2 -> 4 -> 6 -> 8 -> 10
const listB = createLinkedList([2, 4, 6, 8, 10]);
console.log("链表 b:", linkedListToArray(listB)); // 链表 b: [2, 4, 6, 8, 10]

// 合并链表 a 和 b
const mergedList = mergeTwoLists(listA, listB);

// 打印合并后的链表
console.log("合并后的链表:", linkedListToArray(mergedList)); // 合并后的链表: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

代码解释

  1. ListNode:

    • 这是链表的基本组成单元。每个 ListNode 对象包含一个 val(节点的值)和一个 next(指向下一个节点的引用,如果当前是最后一个节点则为 null)。
  2. mergeTwoLists(l1, l2) 函数:

    • 虚拟头节点 (dummyHead) : 我们创建一个值为 0 的虚拟头节点。它的作用是简化代码逻辑,尤其是在处理新链表的第一个节点时。最终,我们将返回 dummyHead.next,它才是合并后链表的真正头节点。

    • current 指针 : current 指针始终指向新合并链表的最后一个节点。通过移动 current,我们可以在新链表的末尾添加节点。

    • while (l1 !== null && l2 !== null) 循环:

      • 这个循环是合并过程的核心。它会一直运行,直到其中一个链表遍历完毕。
      • 在每次迭代中,它比较 l1l2 当前节点的值。
      • 如果 l1.val 小于或等于 l2.val,则将 l1 的当前节点连接到 current.next,然后 l1 移动到它的下一个节点。
      • 否则,将 l2 的当前节点连接到 current.next,然后 l2 移动到它的下一个节点。
      • 无论哪种情况,current 都会移动到刚刚添加的新节点,以便为下一个节点做好准备。
    • 处理剩余节点:

      • while 循环结束时,意味着 l1l2 中至少有一个已经到达末尾 (null)。
      • 如果 l1 还有剩余节点,说明 l2 已经全部合并完毕,直接将 l1 的剩余部分连接到 current.next
      • 同理,如果 l2 还有剩余节点,则将其连接到 current.next。由于两个输入链表都是已排序的,所以剩余的部分也必然是有序的,可以直接连接。
    • 返回值 : 最后,函数返回 dummyHead.next,这是合并后链表的实际头节点。

这个实现方法是合并两个已排序链表的经典且高效的算法,时间复杂度为 O(m+n),其中 m 和 n 分别是两个链表的长度,因为它只需要遍历每个链表一次。

相关推荐
麦兜*1 小时前
Spring Boot 集成Reactive Web 性能优化全栈技术方案,包含底层原理、压测方法论、参数调优
java·前端·spring boot·spring·spring cloud·性能优化·maven
知了一笑1 小时前
独立开发第二周:构建、执行、规划
java·前端·后端
UI前端开发工作室2 小时前
数字孪生技术为UI前端提供新视角:产品性能的实时模拟与预测
大数据·前端
Sapphire~2 小时前
重学前端004 --- html 表单
前端·html
遇到困难睡大觉哈哈3 小时前
CSS中的Element语法
前端·css
Real_man3 小时前
新物种与新法则:AI重塑开发与产品未来
前端·后端·面试
小彭努力中3 小时前
147.在 Vue3 中使用 OpenLayers 地图上 ECharts 模拟飞机循环飞行
前端·javascript·vue.js·ecmascript·echarts
老马聊技术3 小时前
日历插件-FullCalendar的详细使用
前端·javascript
咔咔一顿操作3 小时前
Cesium实战:交互式多边形绘制与编辑功能完全指南(最终修复版)
前端·javascript·3d·vue
LuckyLay4 小时前
使用 Docker 搭建 Rust Web 应用开发环境——AI教你学Docker
前端·docker·rust