[LeetCode]100 链表-专题

目录

[LeetCode 23. 合并 K 个有序链表](#LeetCode 23. 合并 K 个有序链表)

算法原理

解题步骤

代码实现

[LeetCode25 K个一组翻转链表](#LeetCode25 K个一组翻转链表)

算法原理

解题步骤

代码实现


LeetCode 23. 合并 K 个有序链表

算法原理

合并 K 个升序链表的核心是:每一轮都需要从「K 个未完成合并的链表的当前头节点」中,快速找到值最小的节点,将其接入结果链表,再将该节点的后继节点作为对应链表的新头节点,重复此过程直到所有节点合并完成。23. 合并 K 个升序链表 - 力扣(LeetCode)

解题步骤

1、构建小顶堆 :将每个链表的头结点(非空)入堆。

2、虚拟头结点 :用 newHead 简化链表拼接逻辑。

3、循环取堆顶 :每次取出堆顶最小结点,拼接到结果链表;若该结点有 next,将 next 入堆。

4、释放虚拟头:返回合并后的链表。

小顶堆是一种完全二叉树结构,具备两个核心特性:

1、堆顶恒为最小值:可以 O (1) 时间获取当前所有候选节点中的最小值

2、高效的动态更新:插入、删除节点的时间复杂度均为 O (logK)(K 为堆中元素个数,即链表的数量)

时间复杂度:O(NlogK)

N:所有链表的总结点数 每个结点入堆 / 出堆各 1 次,每次堆操作 O(logK)

空间复杂度:O(K)

小顶堆最多同时存储 K 个结点(每个链表的头结点)

代码实现

复制代码
class Solution {
public:
    struct cmp
    {
        //自定义比较器:实现小顶堆
        bool operator()(ListNode* l1,ListNode* l2)
        {
            return l1->val>l2->val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //优先级队列->小堆
        priority_queue<ListNode*,vector<ListNode*>,cmp> heap;
        //将lists里的链表头结点入堆
        for(auto l:lists)
        {
            if(l!=nullptr)
            {
                heap.push(l);
            }
        }

        ListNode* newHead=new ListNode(0);
        ListNode* prev=newHead;
        // 循环取堆顶
        while(!heap.empty())
        {
            //取堆顶的元素
            ListNode* t=heap.top();
            heap.pop();

            //链接
            ListNode* next=t->next;
            t->next=prev->next;
            prev->next=t;
            prev=t;
            // 若有 next,入堆
            if(next)
            {
                heap.push(next);
            }
        }
        //释放虚拟头,返回结果
        ListNode* cur=newHead->next;
        delete newHead;
        return cur;
    }
};

LeetCode25 K个一组翻转链表

算法原理

单链表的翻转本身不难,但本题的核心难点集中在 3 个地方:

分组翻转的衔接问题:每组翻转后,需要正确连接上一组的尾节点和下一组的头节点,不能出现链表断裂

头节点变化问题:第一组翻转后,整个链表的头节点会发生变化,需要统一处理边界逻辑

剩余节点的处理 :必须保证最后不足 k 个的节点不翻转,且正确拼接在链表末尾

题目链接:25. K 个一组翻转链表 - 力扣(LeetCode)

解题步骤

1、先求出需要逆序的组数m

2、对长度为k的链表进行头插逆序,重复m次。

  • 时间复杂度:O (n)

    • 第一次遍历链表计算长度,时间复杂度 O (n)
    • 第二次遍历链表完成分组翻转,每个节点仅被访问一次,时间复杂度 O (n)
    • 整体时间复杂度为 O (n),是本题的最优时间复杂度
  • 空间复杂度:O (1)

    • 仅使用了常数个辅助指针变量,没有申请额外的数组、栈等空间
    • 符合题目进阶要求的 "原地翻转",空间复杂度为常数级

代码实现

复制代码
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(head==nullptr)
        {
            return head;
        }
        //1、计算链表的长度
        ListNode* cur=head;
        int n=0;
        while(cur)
        {
            n++;
            cur=cur->next;
        }

        //2、计算组数
        int m=n/k;

        //翻转链表-头插
        ListNode* newHead=new ListNode(0);
        ListNode* prev=newHead;
        ListNode* next=nullptr;
        ListNode* temp=nullptr;
        cur=head;
        for(int i=0;i<m;i++)
        {
            //记录后续哨兵位结点
            temp=cur;
            // while(k--) error k不能改变 否则会在第二次进入循环时造成死循环 -1!=0成立会继续进循环 从而死循环 最后next=cur->next cur此时为nullptr 对空指针解引用报错
            int count=k;
            while(count--)
            {
                next=cur->next;
                cur->next=prev->next;
                prev->next=cur;
                cur=next;
            }
            prev=temp;
        }
        prev->next=cur;
        cur=newHead->next;
        delete newHead;
        return cur;
    }
};

LeetCode206 反转链表

题目描述

题目链接:206. 反转链表 - 力扣(LeetCode)

解题步骤

法一:

1、设置虚拟头结点进行头插

法二:

1、设置n1、n2、n3三指针,迭代改变指针指向,注意边界n2的条件

时间复杂度:O (n)

  • 每个节点只遍历一次
  • 只做指针修改,常数操作
  • 没有嵌套循环

空间复杂度:O (1)

代码实现

复制代码
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //法一:虚拟头结点
        // ListNode* newHead=nullptr;
        // ListNode* cur=head;
        // ListNode* next=nullptr;
        // while(cur)
        // {
        //     next=cur->next;

        //     cur->next=newHead;
        //     newHead=cur;

        //     cur=next;
        // }
        // return newHead;

        //法二:三指针
    //     ListNode* n1=nullptr;
    //     ListNode* n2=head;
    //     if(n2==nullptr)
    //     {
    //         return nullptr;
    //     }
    //     ListNode* n3=head->next;

    //     while(n2)
    //     {
    //         n2->next=n1;
    //         n1=n2;
    //         n2=n3;
    //         if(n3)
    //         {
    //             n3=n3->next;
    //         }
    //     }
    //     return n1;

    //法三:创建虚拟头结点+头插
        ListNode* newHead=new ListNode(0);
        ListNode* prev=newHead;

        ListNode* cur=head;
        ListNode* next=nullptr;

        while(cur)
        {
            next=cur->next;

            cur->next=prev->next;
            prev->next=cur;

            cur=next;  
        }
        cur=prev->next;
        delete newHead;
        return cur;
    } 
};
相关推荐
Omics Pro3 小时前
首款多模态生物推理大语言模型
人工智能·算法·语言模型·自然语言处理·数据挖掘·数据分析·aigc
国产化创客3 小时前
基于ESP32+Wi‑Fi CSI的开源项目ESPectre
物联网·算法·信息与通信
_深海凉_3 小时前
LeetCode热题100-LRU 缓存
算法·leetcode·缓存
Via_Neo3 小时前
今天是周六,两天后是周几?
java·数据结构·算法
伟大的车尔尼3 小时前
广度优先搜索和深度优先搜索的概念
数据结构·算法·并查集·深度优先搜索·广度优先搜索
keep intensify4 小时前
最小覆盖子串
算法
仟濹4 小时前
【算法打卡day35(2026-03-31 周二)】DFS专项训练2(今日算法:DFS & 记忆化搜索 & 回溯)
c++·算法·蓝桥杯·深度优先
炽烈小老头4 小时前
【每天学习一点算法 2026/04/02】最长递增子序列
学习·算法
IronMurphy4 小时前
【算法三十五】22. 括号生成
算法