当前位置:首页 > 技术 > 正文内容

合并K个升序链表:分治与优先队列策略

访客 技术 2026年5月25日 3

合并两个已排序的链表是一个基础操作。其核心思想是维护一个新的链表,不断地从两个输入链表中取出值较小的节点添加到新链表末尾,直至其中一个链表为空,然后将另一个链表的剩余部分直接接在新链表之后。

合并两个链表的实现

public ListNode mergeTwoSortedLists(ListNode list1, ListNode list2) {
    if (list1 == null) {
        return list2;
    }
    if (list2 == null) {
        return list1;
    }

    ListNode dummyHead = new ListNode(0); // 哨兵节点,简化头节点处理
    ListNode current = dummyHead;

    while (list1 != null && list2 != null) {
        if (list1.val <= list2.val) {
            current.next = new ListNode(list1.val); // 创建新节点,避免直接引用原节点
            list1 = list1.next;
        } else {
            current.next = new ListNode(list2.val);
            list2 = list2.next;
        }
        current = current.next;
    }

    // 将剩余未合并的节点连接到末尾
    if (list1 != null) {
        current.next = list1;
    } else {
        current.next = list2;
    }

    return dummyHead.next;
}

对于合并 K 个升序链表的问题,有多种有效的方法。

顺序合并策略 最直观的策略是将 K 个链表两两配对进行合并。可以从第一个链表开始,依次与后续的链表进行合并。每次合并的结果都成为下一次合并的输入。

顺序合并 K 个链表的实现

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }

        ListNode mergedList = null;
        for (ListNode list : lists) {
            mergedList = mergeTwoSortedLists(mergedList, list);
        }
        return mergedList;
    }

    // mergeTwoSortedLists 方法同上
    private ListNode mergeTwoSortedLists(ListNode list1, ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        ListNode dummyHead = new ListNode(0);
        ListNode current = dummyHead;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                current.next = new ListNode(list1.val);
                list1 = list1.next;
            } else {
                current.next = new ListNode(list2.val);
                list2 = list2.next;
            }
            current = current.next;
        }
        if (list1 != null) current.next = list1;
        else current.next = list2;
        return dummyHead.next;
    }
}

分治合并策略 分治思想在此问题中同样适用。可以将 K 个链表看作一个数组,递归地将数组分成两半,分别合并左右两半的链表,最后再将合并后的两个链表进行合并。这种方式可以更均衡地分配合并任务,潜在地提高效率。

分治合并 K 个链表的实现

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        return divideAndConquerMerge(lists, 0, lists.length - 1);
    }

    private ListNode divideAndConquerMerge(ListNode[] lists, int start, int end) {
        if (start == end) {
            return lists[start];
        }
        // 处理当 start > end 的情况,例如 lists.length = 0 时
        if (start > end) {
            return null;
        }
        
        int mid = start + ((end - start) >> 1); // 计算中间索引
        ListNode leftMerged = divideAndConquerMerge(lists, start, mid);
        ListNode rightMerged = divideAndConquerMerge(lists, mid + 1, end);
        
        return mergeTwoSortedLists(leftMerged, rightMerged);
    }

    // mergeTwoSortedLists 方法同上
    private ListNode mergeTwoSortedLists(ListNode list1, ListNode list2) {
        if (list1 == null) return list2;
        if (list2 == null) return list1;
        ListNode dummyHead = new ListNode(0);
        ListNode current = dummyHead;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                current.next = new ListNode(list1.val);
                list1 = list1.next;
            } else {
                current.next = new ListNode(list2.val);
                list2 = list2.next;
            }
            current = current.next;
        }
        if (list1 != null) current.next = list1;
        else current.next = list2;
        return dummyHead.next;
    }
}

优先队列合并策略 另一种高效的方法是利用优先队列(最小堆)。将 K 个链表的头节点(或者所有节点)的数值放入优先队列。每次从优先队列中取出最小值,构成新链表的一个节点。如果取出的节点来自某个链表,则将其下一个节点也加入优先队列。

优先队列合并 K 个链表的实现

import java.util.PriorityQueue;
import java.util.Queue;

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }

        // 优先队列存储链表节点,根据 val 排序
        Queue<ListNode> minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);

        // 将所有链表的第一个节点加入优先队列
        for (ListNode head : lists) {
            if (head != null) {
                minHeap.offer(head);
            }
        }

        ListNode dummyHead = new ListNode(0);
        ListNode current = dummyHead;

        while (!minHeap.isEmpty()) {
            ListNode smallestNode = minHeap.poll(); // 取出当前最小值的节点
            current.next = new ListNode(smallestNode.val); // 添加到结果链表
            current = current.next;

            // 如果被取出的节点还有下一个节点,则将其加入优先队列
            if (smallestNode.next != null) {
                minHeap.offer(smallestNode.next);
            }
        }

        return dummyHead.next;
    }
}

// ListNode 定义
class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
标签: 链表

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。