目录
[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 反转链表
题目描述

解题步骤
法一:
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;
}
};