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;
    }
};





内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储与模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本与Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署与推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预测功能。同时展望了多模态标注、主动学习与边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展与人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker与弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理与DOM操作的稳定性,在实践中理解浏览器扩展的安全机制与性能优化策略。
基于Gin+GORM+Casbin+Vue.js的权限管理系统是一个采用前后端分离架构的企业级权限管理解决方案,专为软件工程和计算机科学专业的毕业设计项目开发。该系统基于Go语言构建后端服务,结合Vue.js前端框架,实现了完整的权限控制和管理功能,适用于各类需要精细化权限管理的应用场景。 系统后端采用Gin作为Web框架,提供高性能的HTTP服务;使用GORM作为ORM框架,简化数据库操作;集成Casbin实现灵活的权限控制模型。前端基于vue-element-admin模板开发,提供现代化的用户界面和交互体验。系统采用分层架构和模块化设计,确保代码的可维护性和可扩展性。 主要功能包括用户管理、角色管理、权限管理、菜单管理、操作日志等核心模块。用户管理模块支持用户信息的增删改查和状态管理;角色管理模块允许定义不同角色并分配相应权限;权限管理模块基于Casbin实现细粒度的访问控制;菜单管理模块动态生成前端导航菜单;操作日志模块记录系统关键操作,便于审计和追踪。 技术栈方面,后端使用Go语言开发,结合Gin、GORM、Casbin等成熟框架;前端使用Vue.js、Element UI等现代前端技术;数据库支持MySQL、PostgreSQL等主流关系型数据库;采用RESTful API设计规范,确保前后端通信的标准化。系统还应用了单例模式、工厂模式、依赖注入等设计模式,提升代码质量和可测试性。 该权限管理系统适用于企业管理系统、内部办公平台、多租户SaaS应用等需要复杂权限控制的场景。作为毕业设计项目,它提供了完整的源码和论文文档,帮助学生深入理解前后端分离架构、权限控制原理、现代Web开发技术等关键知识点。系统设计规范,代码结构清晰,注释完整,非常适合作为计算机相关专业的毕业设计参考或实际项目开发的基础框架。 资源包含完整的系统源码、数据库设计文档、部署说明和毕
<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、付费专栏及课程。

余额充值