LeetCode hard也就这么回事

news/2024/9/20 2:35:17

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[1->4->5,1->3->4,2->6]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

        老样子,先贴我的图,可以说是又快又好,不过这里原题给的是子链表有序(常规写法也能写,不难),如果子链表无序,那各位读者应该是焦头烂额+汗流浃背了

            这个题,其实看完我上一篇分享可以立马写出来了,链表排序?看完你也能手撕 
思路还是一样,先转换成数组存放,再排序,再存回链表中(思路很简单)
 

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        vector<int> s;  // 存储所有链表节点的值的容器

        // 遍历所有链表,将节点的值依次放入容器中
        for (int i = 0; i < lists.size(); i++) {
            ListNode* cur = lists[i]; // 当前链表的头节点
            while (cur != nullptr) {
                s.push_back(cur->val); // 将节点值加入容器
                cur = cur->next; // 指针移动到下一个节点
            }
        }
        
        sort(s.begin(),s.end()); // 将容器中的值进行排序(升序)
        
        ListNode *tt = new ListNode(-1); // 创建一个哑节点 tt,用于构建结果链表
        ListNode *res = tt; // 使用 res 保存结果链表的头节点
        int i = 0; // 用于遍历排序后的容器 s
        
        // 遍历列表
        for (int j = 0; j < lists.size(); j++) {       
            ListNode* cur1 = lists[j]; // 当前链表的头节点
            while(cur1 != nullptr) {
                cur1->val = s[i++]; // 将排序后的值赋给当前节点
                tt->next = cur1; // 连接当前节点到结果链表
                tt = cur1; // tt 指针移动到当前节点
                cur1 = cur1->next; // cur1 指针移动到下一个节点
            }
        }
        
        return res->next; // 返回结果链表的头节点
    }
};

以下是LeetCode官方的各种题解,为大家添加上了注释

方法一:顺序合并

我们可以想到一种最朴素的方法:用一个变量 ans 来维护以及合并的链表,第 i次循环把第 i 个链表和 ans 合并,答案保存到 ans 中。

class Solution {
public:
    // 合并两个有序链表
    ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
        // 如果其中一个链表为空,则直接返回另一个链表
        if ((!a) || (!b)) return a ? a : b;
        
        ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
        while (aPtr && bPtr) {
            if (aPtr->val < bPtr->val) {
                // 当a链表的值小于b链表的值时,将a链表的节点拼接到结果链表后面,并更新a链表的指针
                tail->next = aPtr; aPtr = aPtr->next;
            } else {
                // 当a链表的值大于等于b链表的值时,将b链表的节点拼接到结果链表后面,并更新b链表的指针
                tail->next = bPtr; bPtr = bPtr->next;
            }
            // 更新结果链表的尾指针
            tail = tail->next;
        }
        // 将剩下的未合并的链表直接拼接到结果链表的尾部
        tail->next = (aPtr ? aPtr : bPtr);
        
        // 返回结果链表
        return head.next;
    }

    // 合并K个有序链表
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *ans = nullptr;
        for (size_t i = 0; i < lists.size(); ++i) {
            // 逐个合并链表,将结果保存到ans中
            ans = mergeTwoLists(ans, lists[i]);
        }
        // 返回最终的合并结果
        return ans;
    }
};


方法二:分治合并

考虑优化方法一,用分治的方法进行合并。

class Solution {
public:
    // 合并两个有序链表
    ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
        if ((!a) || (!b)) return a ? a : b; // 如果其中一个链表为空,则直接返回另一个链表
        ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
        while (aPtr && bPtr) {
            if (aPtr->val < bPtr->val) { // 比较两个节点的值,将较小的节点接入结果链表
                tail->next = aPtr; 
                aPtr = aPtr->next; // 更新a链表指针
            } else {
                tail->next = bPtr; 
                bPtr = bPtr->next; // 更新b链表指针
            }
            tail = tail->next; // 更新结果链表的尾节点
        }
        tail->next = (aPtr ? aPtr : bPtr); // 将剩余未合并完成的节点接入结果链表
        return head.next; // 返回结果链表的头节点
    }

    // 递归合并多个有序链表
    ListNode* merge(vector <ListNode*> &lists, int l, int r) {
        if (l == r) return lists[l]; // 当l和r相等时,表示只有一个链表,直接返回该链表
        if (l > r) return nullptr; // 当l大于r时,表示没有待合并的链表,返回空指针
        int mid = (l + r) >> 1; // 计算中间位置
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r)); // 递归合并左右两个部分
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return merge(lists, 0, lists.size() - 1); // 调用递归函数,合并整个链表数组
    }
};


方法三:使用优先队列合并

这个方法和前两种方法的思路有所不同,我们需要维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。
 

class Solution {
public:
    // 定义结构体Status,用于在优先队列中存储节点信息
    struct Status {
        int val; // 节点的值
        ListNode *ptr; // 节点指针
        bool operator < (const Status &rhs) const {
            return val > rhs.val; // 优先队列按照节点值的大小进行排序
        }
    };

    priority_queue<Status> q; // 创建优先队列q,按照节点值的大小进行排序

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 将每个链表的第一个节点放入优先队列
        for (auto node : lists) {
            if (node)
                q.push({node->val, node});
        }

        ListNode head, *tail = &head; // 创建结果链表的头节点head和尾节点tail
        while (!q.empty()) {
            auto f = q.top();
            q.pop();
            tail->next = f.ptr; // 将当前队列中最小的节点连接到结果链表的尾部
            tail = tail->next; // 更新尾节点为当前节点

            if (f.ptr->next)
                q.push({f.ptr->next->val, f.ptr->next}); // 如果当前节点还有下一个节点,则将下一个节点放入优先队列继续排序
        }

        return head.next; // 返回结果链表的头节点
    }
};


http://lihuaxi.xjx100.cn/news/2108840.html

相关文章

python coding with ChatGPT 打卡第23天| 回溯算法:理论基础

文章目录 视频讲解回溯法的效率解决的问题如何理解回溯法回溯框架 视频讲解 回溯算法理论篇 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 回溯法的效率 回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&#xff0c;如果想让回溯法…

Json格式解析

文章目录 Json格式介绍python中json模块的使用 Json格式介绍 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它基于 ECMAScript&#xff08;欧洲计算机协会制定的js规范&#xff09;的一个子集&#xff0c;采用完全独立于语言…

WebSocket相关知识

WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。【应用场景&#xff1a;视频弹幕、网页聊天、体育实况更新、股票基金报价】 …

sde Geodatabase tool to upgrade system tables

新浪博客 ArcGIS Desktop Esri | There has been a problem Win7ArcSDE10 Oracle11g&#xff0c;均为64位。安装了sde10 的sp1补丁后发现SDE连接无法启动&#xff0c;查看日志&#xff0c;提示信息&#xff1a; ------------------------------------------------------- Arc…

【Python多线程】的进阶讲解

Python多线程 1. 前言2. threading 模块的基本用法3. Thread类4. 锁&#xff08;Locks&#xff09;5. 守护线程&#xff08;Daemon Threads&#xff09;6. 运用场景7. 弊端 1. 前言 Python中的多线程通过threading模块来实现&#xff0c;它允许你并发执行多个线程&#xff0c;…

力扣_动态规划4—最大正方形

题目 在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵 m a t r i x matrix matrix 内&#xff0c;找到只包含 ‘1’ 的最大正方形&#xff0c;并返回其面积。 方法 创建二维 d p dp dp 数组&#xff0c; d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 ( i , j ) (i,j) (i,j) 为右下角…

深入浅出前端本地储存

引言 2021 年&#xff0c;如果你的前端应用&#xff0c;需要在浏览器上保存数据&#xff0c;有三个主流方案&#xff1a; CookieWeb Storage (LocalStorage)IndexedDB 这些方案就是如今应用最广、浏览器兼容性最高的三种前端储存方案 今天这篇文章就聊一聊这三种方案的历史…

linux 命令笔记:gpustat

1 命令介绍 gpustat是一个基于Python的命令行工具&#xff0c;它提供了一种快速、简洁的方式来查看GPU的状态和使用情况它是nvidia-smi工具的一个封装&#xff0c;旨在以更友好和易于阅读的格式显示GPU信息。gpustat不仅显示基本的GPU状态&#xff08;如温度、GPU利用率和内存…