LeetCode - Merge k Sorted Lists

本文介绍了两种解决合并K个已排序链表问题的方法:一种是分而治之的策略,通过递归合并两个链表直到只剩一个;另一种是利用优先队列(最小堆)进行节点的高效排序与合并。详细分析了每种方法的时间和空间复杂度。

https://leetcode.com/problems/merge-k-sorted-lists/

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

优快云坑爹博客,之前明明写过这篇,居然找不到了!

之前用JAVA写的代码,现在是用C++写的,JAVA的就不重写了。。。。代码跟这里差不多:

http://blog.youkuaiyun.com/linhuanmars/article/details/19899259

首先第一种方法是divide and conquer,就是先merge两个list,再依次往上。

假设每个链表最长长度为n,链表数为k,因此T(k) = 2*T(k/2) + O(nk), 因此时间复杂度是O(nk*logk),空间复杂度是递归栈的深度,O(logk)

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        return helper(lists, 0, lists.size()-1);
    }
    
    ListNode *helper(vector<ListNode *> &lists, int left, int right){
        if(left>right) return NULL;
        if(left==right) return lists[left];
        int mid = (left+right)/2;
        return merge(helper(lists, left, mid), helper(lists, mid+1, right));
    }
    ListNode *merge(ListNode* left, ListNode* right){
        ListNode* dummy = new ListNode(0);
        ListNode* trv = dummy;
        while(left!=NULL && right!=NULL){
            if(left->val < right->val){
                trv->next = left;
                left = left->next;
            }
            else{
                trv->next = right;
                right = right->next;
            }
            trv = trv->next;
        }
        if(left!=NULL) trv->next = left;
        else trv->next = right;
        ListNode * head = dummy->next;
        delete dummy;
        return head;
    }
};

另一种方式是使用priority queue,即min_heap来排序,先放k个node到min_heap里面去,再从heap中取出来最小的,再把最小的的下一个放到heap中去。

时间复杂度是O(nk*logk),因为入栈深度是logk,总共nk个节点。空间复杂度O(k),注意,写priority queue的时候一定要记得覆盖比较函数,另外需要注意的是,C++的每个类定义后面都有一个分号,一定不能忘了,而且C++空指针是NULL,JAVA是null,不要弄混了:

class Comparison{
    public:
    bool operator()(ListNode* n1, ListNode * n2){
        return n1->val > n2->val;
    }
};
class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        priority_queue<ListNode *, vector<ListNode *>, Comparison> pq;
        for(int i=0; i<lists.size(); i++){
            if(lists[i] != NULL) pq.push(lists[i]);
        }
        ListNode *dummy = new ListNode(0);
        ListNode *trv = dummy;
        while(pq.size()>0){
            trv->next = pq.top();
            pq.pop();
            trv = trv->next;
            if(trv->next != NULL) pq.push(trv->next);
        }
        trv = dummy->next;
        delete dummy;
        return trv;
    }
};





考虑柔性负荷的综合能源系统低碳经济优化调度【考虑碳交易机制】(Matlab代码实现)内容概要:本文围绕“考虑柔性负荷的综合能源系统低碳经济优化调度”展开,重点研究在碳交易机制下如何实现综合能源系统的低碳化与经济性协同优化。通过构建包含风电、光伏、储能、柔性负荷等多种能源形式的系统模型,结合碳交易成本与能源调度成本,提出优化调度策略,以降低碳排放并提升系统运行经济性。文中采用Matlab进行仿真代码实现,验证了所提模型在平衡能源供需、平抑可再生能源波动、引导柔性负荷参与调度等方面的有效性,为低碳能源系统的设计与运行提供了技术支撑。; 适合人群:具备一定电力系统、能源系统背景,熟悉Matlab编程,从事能源优化、低碳调度、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究碳交易机制对综合能源系统调度决策的影响;②实现柔性负荷在削峰填谷、促进可再生能源消纳中的作用;③掌握基于Matlab的能源系统建模与优化求解方法;④为实际综合能源项目提供低碳经济调度方案参考。; 阅读建议:建议读者结合Matlab代码深入理解模型构建与求解过程,重点关注目标函数设计、约束条件设置及碳交易成本的量化方式,可进一步扩展至多能互补、需求响应等场景进行二次开发与仿真验证。
<think>嗯,用户问的是关于C语言中结构体初始化的问题。具体来说,他们看到一段代码里用到了结构体的初始化方式,比如struct ListNode dummy = { .val = 0, .next = NULL };,然后想知道是否可以写成dummy.val和dummy.next来访问这些成员。 首先,我需要确认用户的理解是否正确。结构体的成员访问在C语言中确实是通过点操作符进行的。所以,dummy.val和dummy.next这样的写法是正确的,没有问题。用户可能对结构体初始化语法和成员访问有些混淆,需要明确这两者的区别。 接下来,用户提供的代码示例中使用了C99的指定初始化器(designated initializers),也就是在初始化结构体时指定成员的名称,比如.val = 0。这种写法在C99及以后的版本中是合法的,可以明确地为特定成员赋值,而不用按照结构体定义的顺序。这有助于代码的可读性,尤其是当结构体有很多成员时。 然后,用户提到的代码中创建了一个哨兵节点(dummy node),用来简化链表操作。这里cur指针指向dummy,之后通过cur->next来链接新的节点,这样就不需要处理头节点的特殊情况。这部分逻辑是正确的,但用户可能更关心结构体成员的访问方式是否正确。 需要进一步解释的是,结构体变量在定义时初始化和使用时的成员访问是不同的阶段。初始化时的指定初始化器语法只是在创建结构体实例的时候使用,而后续访问成员则是通过点操作符或者箭头操作符(对于指针)进行的。例如,dummy.val和dummy.next是正确的访问方式,而如果有一个结构体指针ptr,则需要用ptr->val和ptr->next。 可能用户看到代码中使用了.来初始化,担心是否在访问时也必须使用同样的方式。需要明确的是,初始化语法中的.val是初始化时的指定,而后续访问成员是另一回事,两者语法上都是点操作符,但上下文不同。 另外,用户可能对结构体的内存分配和初始化过程不太清楚。在C语言中,局部变量的结构体(如dummy)会在栈上分配内存,初始化时指定的成员会被赋予相应的值,未指定的成员会被初始化为0或NULL(根据编译器实现)。因此,dummy.next在这里被显式初始化为NULL,其他成员如果存在的话会被默认初始化为0。 总结一下,用户的问题核心在于确认结构体成员访问的正确性,以及初始化语法的理解。需要明确回答他们:是的,可以写成dummy.val和dummy.next来访问这些成员,这符合C语言的语法规则。同时,解释初始化语法和成员访问的区别,确保用户明白这两者是不同的操作阶段。</think>在 C 语言中,`.val` 和 `.next` 的写法是完全正确的,这是结构体成员的访问语法。您提到的两种写法本质上是等价的,区别在于初始化语法: ▋ 代码解析 ▋ 1. **C99 标准写法**(指定初始化器) ```c struct ListNode dummy = { .val = 0, // 显式初始化 val 成员 .next = NULL // 显式初始化 next 成员 }; ``` 这是 C99 引入的「指定初始化器」(Designated Initializers),优点: - 初始化顺序可与结构体定义顺序不同 - 未指定的成员会自动初始化为 0/NULL - 可读性更强(尤其在多成员结构体中) 2. **传统写法** ```c struct ListNode dummy = {0, NULL}; ``` 需要严格按照结构体成员的声明顺序初始化 ▋ 访问成员的正确方式 ▋ 无论采用哪种初始化方式,访问成员时都使用 `.` 操作符: ```c printf("%d", dummy.val); // ✅ 正确 struct ListNode* p = dummy.next; // ✅ 正确 ``` ▋ 哨兵节点的作用 ▋ 原代码中 `dummy` 节点的精妙之处: 1. **简化边界处理**:无需单独处理头节点为空的情况 2. **统一操作逻辑**:所有节点(包括第一个节点)都通过 `cur->next` 插入 3. **内存安全**:`dummy` 是栈变量,生命周期随函数自动释放 ▋ 完整示例 ▋ ```c struct ListNode { int val; struct ListNode* next; }; int main() { // 初始化方式一:C99 指定初始化器(推荐) struct ListNode dummy1 = { .val = 0, .next = NULL }; // 初始化方式二:传统顺序初始化 struct ListNode dummy2 = { 0, NULL }; // 成员访问演示 dummy1.val = 10; // ✅ 修改 val dummy1.next = &dummy2; // ✅ 修改 next return 0; } ``` 建议优先使用 C99 的指定初始化器,可显著提升代码可读性和可维护性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值