> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:熟练掌握链表算法。
> 毒鸡汤:学习,学习,再学习 ! 学,然后知不足。
> 专栏选自:刷题训练营
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
🌟前言分析
最早博主续写了牛客网130道题,这块的刷题是让同学们快速进入C语言,而我们学习c++已经有一段时间了,知识储备已经足够了但缺少了实战,面对这块短板博主续写刷题训练,针对性学习,把相似的题目归类,系统的刷题,而我们刷题的官网可以参考:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网
⭐知识讲解
基本思想:
- 画图,一定要画图,只有画好图才能直观形象便于我们理解。
- 引入虚拟"头"结点。
- 不要吝啬空间,大胆去定义变量。
🌙topic-->1
题目链接:1. 两数相加 - 力扣(LeetCode)
题目分析:
给一个非空的链表,表示的是两个非负的整数,它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字,然后将两个数字相加,并以相同形式返回一个表示和的链表。
算法原理:
- 解法:模拟两数相加的过程即可
图解:
细节处理:
- 判端循环结束标志。
- 可能存在两个链表长短不一。
- 不要忘记释放空间。
代码演示:
class Solution
{
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
// 定义两个指针
ListNode* cur1 = l1,*cur2 = l2;
// 开辟空间(创建一个虚拟头结点,记录最终结果)
ListNode* newhead = new ListNode(0);
ListNode* prev = newhead; // 标记尾指针
int t = 0; // 记录进位
// 循环
while(cur1 || cur2 || t) // 细节一:循环结束标志
{
// 先加上第一个链表
if(cur1)
{
t = t + cur1->val;
cur1 = cur1->next;// 指针需要移动
}
// 再加上第二个链表
if(cur2)
{
t = t + cur2->val;
cur2 = cur2->next;// 指针需要移动
}
// 开始进位
prev->next = new ListNode(t % 10);
prev = prev->next;
t = t/10;
}
prev = newhead->next; // 回到最初位置
delete newhead;// 释放空间
return prev;
}
};
🌙topic-->2
题目链接:2. 两两交换链表中的节点 - 力扣(LeetCode)
题目分析:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题。
算法原理:
- 解法一:采用递归(有兴趣大家自己可以上手写写)
- 解法二:循环+迭代(模拟)
图解:
细节处理:
- 判断链表边界情况。
- 循环结束标志。
- 不要忘记释放空间。
代码演示:
class Solution
{
public:
ListNode* swapPairs(ListNode* head)
{
// 判断边界情况
if(head == nullptr || head->next == nullptr) return head;
// 开辟空间
ListNode* newHead = new ListNode(0);
newHead->next = head;
// 定义四个指针
ListNode* prev = newHead, *cur = prev->next, *next = cur->next, *nnext = next->next;
while(cur && next) // 循环
{
// 交换结点
prev->next = next;
next->next = cur;
cur->next = nnext;
// 修改指针
prev = cur; // 注意顺序
cur = nnext;
if(cur) next = cur->next;
if(next) nnext = next->next;
}
cur = newHead->next;
delete newHead;
return cur;
}
};
🌙topic-->3
题目链接:3. 重排链表 - 力扣(LeetCode)
题目分析:
给定一个单链表 L
的头节点 head
,单链表 L
表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
算法原理:
- 解法:模拟
图解:
无
细节处理:
- 判断链表边界情况。
代码演示:
class Solution
{
public:
void reorderList(ListNode* head)
{
// 处理边界情况
if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
return;
// 1. 找到链表的中间节点 - 快慢双指针(⼀定要画图考虑 slow
// 的落点在哪⾥)
ListNode *slow = head, *fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
// 2. 把 slow 后⾯的部分给逆序 - 头插法
ListNode* head2 = new ListNode(0);
ListNode* cur = slow->next;
slow->next = nullptr; // 注意把两个链表给断开
while (cur)
{
ListNode* next = cur->next;
cur->next = head2->next;
head2->next = cur;
cur = next;
}
// 3. 合并两个链表 - 双指针
ListNode* ret = new ListNode(0);
ListNode* prev = ret;
ListNode *cur1 = head, *cur2 = head2->next;
while (cur1)
{
// 先放第⼀个链表
prev->next = cur1;
cur1 = cur1->next;
prev = prev->next;
// 再放第⼆个链表
if (cur2)
{
prev->next = cur2;
prev = prev->next;
cur2 = cur2->next;
}
}
delete head2;
delete ret;
}
};
🌙topic-->4
题目链接:4. 合并 K 个升序链表 - 力扣(LeetCode)
题目分析:
给你一个链表数组,每个链表都已经按升序排列,请你将所有链表合并到一个升序链表中,返回合并后的链表。
算法原理:
- 解法:递归+分治
图解:
代码演示:
class Solution
{
public:
ListNode* mergeKLists(vector<ListNode*>& lists)
{
return merge(lists, 0, lists.size() - 1);
}
ListNode* merge(vector<ListNode*>& lists, int left, int right)
{
if(left > right) return nullptr;
if(left == right) return lists[left];
// 1. 平分数组
int mid = left + right >> 1;
// [left, mid] [mid + 1, right]
// 2. 递归处理左右区间
ListNode* l1 = merge(lists, left, mid);
ListNode* l2 = merge(lists, mid + 1, right);
// 3. 合并两个有序链表
return mergeTowList(l1, l2);
}
ListNode* mergeTowList(ListNode* l1, ListNode* l2)
{
if(l1 == nullptr) return l2;
if(l2 == nullptr) return l1;
// 合并两个有序链表
ListNode head;
ListNode* cur1 = l1, *cur2 = l2, *prev = &head;
head.next = nullptr;
while(cur1 && cur2)
{
if(cur1->val <= cur2->val)
{
prev = prev->next = cur1;
cur1 = cur1->next;
}
else
{
prev = prev->next = cur2;
cur2 = cur2->next;
}
}
if(cur1) prev->next = cur1;
if(cur2) prev->next = cur2;
return head.next;
}
};
🌙topic-->5
题目链接:5. K 个一组翻转链表 - 力扣(LeetCode)
题目分析:
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
算法原理:
- 解法:模拟
图解:
代码演示:
class Solution
{
public:
ListNode* reverseKGroup(ListNode* head, int k)
{
// 1. 先求出需要逆序多少组
int n = 0;
ListNode* cur = head;
while(cur)
{
cur = cur->next;
n++;
}
n /= k;
// 2. 重复 n 次:⻓度为 k 的链表的逆序即可
ListNode* newHead = new ListNode(0);
ListNode* prev = newHead;
cur = head;
for(int i = 0; i < n; i++)
{
ListNode* tmp = cur;
for(int j = 0; j < k; j++)
{
ListNode* next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = next;
}
prev = tmp;
}
// 把不需要翻转的接上
prev->next = cur;
cur = newHead->next;
delete newHead;
return cur;
}
};
🌟结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。