我爱学算法之—— 链表
一、两数相加
题目解析

这道题,给定两个非空的链表,表示两个非负的整数;其中每位数字都是按照逆序方式存储的。
现在,我们要计算这两个非负整数的和,并以同样的形式返回一个链表。
算法思路
整体来说,这道题还是非常简单的;
因为给定的数字是按照逆序方式存储的,无非就是按照加法的流程进行计算,然后处理进位和新增节点即可。
思路:
模拟加法的过程,处理进位、构建结果链表。
代码实现
class Solution {
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* head = new ListNode(0);ListNode* cur = head;int tmp = 0; // 进位while (l1 && l2) {int x = l1->val + l2->val + tmp;cur->next = new ListNode(x % 10);cur = cur->next;tmp = x / 10;l1 = l1->next;l2 = l2->next;}while (l1) {int x = l1->val + tmp;cur->next = new ListNode(x % 10);cur = cur->next;tmp = x / 10;l1 = l1->next;}while (l2) {int x = l2->val + tmp;cur->next = new ListNode(x % 10);cur = cur->next;tmp = x / 10;l2 = l2->next;}if (tmp)cur->next = new ListNode(tmp);ListNode* ret = head->next;delete head;return ret;}
};
二、两两交换链表中的节点
题目解析

给定一个链表,现在要将链表中的节点,两两交换,然后返回交换后的链表。(进行节点交换,而不是值交换)
算法思路
对于交换链表中两个节点,还是非常简单的,只要标注一下所有要用到的节点,就无需考虑先后顺序,直接修改指针指向即可。
但是,这道题我们要注意:
第一个节点也要进行交换,交换之后,它就变成了第二个节点,并指向后面节点;此时就会丢掉一个节点
所以,我们需要创建一个虚拟头节点,来当作交换链表的头节点,这样最终等到的链表才是完整的。
代码实现
class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* ret_head = new ListNode(0);ret_head->next = head;ListNode* prev = ret_head;while (prev->next && prev->next->next) {ListNode* node1 = prev->next;ListNode* node2 = prev->next->next;ListNode* next = prev->next->next->next;prev->next = node2;node2->next = node1;node1->next = next;prev = node1;}ListNode* ret = ret_head->next;delete ret_head;return ret;}
};
三、重排链表
题目解析

这道题,给定一个链表L0、L1、L2... Ln,要求我们重排链表,将链表变成L1、Ln、L2、Ln-1...。
算法思路
这道题,整体思路就是模拟,模拟一下重排链表的过程即可。
- 找到链表中间节点:快慢指针
- 反转后半部分的链表:头插法
- 合并两个链表
代码实现
class Solution {
public:void reorderList(ListNode* head) {if (head == nullptr || head->next == nullptr)return;ListNode* fast = head;ListNode *slow = head, *prev = head;while (fast && fast->next) {fast = fast->next->next;prev = slow;slow = slow->next;}prev->next = nullptr;// 逆置后半部分链表ListNode* phead = new ListNode(0);ListNode* cur = phead;// 头插法while (slow) {ListNode* next = slow->next;slow->next = cur->next;cur->next = slow;slow = next;}// 合并两个链表ListNode* cur1 = phead->next;ListNode* cur2 = head->next;cur = head;while (cur1 && cur2) {cur->next = cur1;cur1 = cur1->next;cur->next->next = cur2;cur2 = cur2->next;cur = cur->next->next;}if (cur1)cur->next = cur1;delete phead;}
};
四、合并 K 个升序链表
题目解析

给定k个升序链表,题目要求我们合并这k个升序链表,然后返回合并好的链表。
算法思路
对于这道题,我们知道如何合并两个有序链表,那和并k个有序链表,无非就是多进行几次操作而已;
暴力合并两个有序链表:
依次合并两个有序链表。(会超时)
堆:
对于这道题,我们可以使用堆这一数据结构,将所有链表的头节点都放到一个小根堆里去(比较大小的方式就按照其中存储的值比较);
这样每次从堆顶取数值最小的节点,然后将其下一个节点放到堆中。(nullptr不放入堆中)
建小根堆:
建立小根堆,存储链表节点;使用
vector容器存储即可;实现仿函数,完成链表节点的比较大小。合并链表:
从堆顶取节点,尾插到新链表中,如果其下一个节点不为
nullptr,就将其下一个节点放到堆中。重复上述操作,直到堆中没有节点。
代码实现
class Solution {
public:struct tmp {bool operator()(ListNode* l1, ListNode* l2) {if (l1 && l2)return l1->val > l2->val;elsereturn false;}};ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<ListNode*, vector<ListNode*>, tmp> pq;for (auto& e : lists) {if (e != nullptr)pq.push(e);}ListNode* head = new ListNode(0);ListNode* cur = head;while (!pq.empty()) {// 取堆顶节点ListNode* top = pq.top();pq.pop();// 尾插cur->next = top;cur = top;if (top->next)pq.push(top->next);}ListNode* ret = head->next;delete head;return ret;}
};
五、K 个一组翻转链表
题目解析

这道题给定一个链表和一个整数k,要求我们以k个节点为一组,翻转链表,如果最后节点不足k个,就保留。
算法思路
- 计算需要翻转链表的组数
对于这道题,首先要做的就是计算需要翻转链表的组数;(题目要求,最后不足k个时,要保持原有顺序)
- 使用头插法,反转
k个节点的链表
知道了需要翻转链表的组数,然后就是翻转一组(也就是k个节点);
这里利用头插法返回k个节点。
- 最后,将不足
k个节点的组直接放到结果链表的最后。
代码实现
class Solution {
public:ListNode* reverseKGroup(ListNode* head, int k) {int n = 0;ListNode* cur = head;while (cur) {n++;cur = cur->next;}n /= k;cur = head;ListNode* ret_head = new ListNode(0);ListNode* tail = ret_head;for (int i = 0; i < n; i++) {ListNode* tmp = cur;int j = 0;while (j < k) {// 头插ListNode* next = cur->next;cur->next = tail->next;tail->next = cur;cur = next;j++;}tail = tmp;}tail->next = cur;ListNode* ret = ret_head->next;delete ret_head;return ret;}
};
本篇文章到这里就结束了,感谢支持
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
