文章目录
- 简介
- 链表的常用技巧
- 两数相加
- 原理
- 代码
- 代码||
- 两两交换链表中的节点
- 代码
- 原理
- 重排链表(重要)
- 原理
- 代码
- 合并 K 个升序链表
- 代码
- 递归代码
- K 个一组翻转链表
- 原理
- 代码
简介
大家好,这里是jiantaoyab,这篇文章给大家带来的是链表相关的题目练习和解析,希望大家能相互讨论进步
链表的常用技巧
- 画图
画图能更加清晰,方便我们去理解
- 引入虚拟的头结点
创建新的头结点指向原来的链表,方便处理边界情况
- 多定义一个变量
多定义一个next就不用考虑先链接谁的问题
- 快慢双指针
- 判断链表中是否有环
- 找环的入口
- 找链表倒数第n个节点
- 链表逆序用头插
两数相加
https://leetcode.cn/problems/add-two-numbers/
https://leetcode.cn/problems/lMSNwu/ 两数相加||
原理
代码
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(0); //创建哨兵位头结点
ListNode* tail = new_head;
int x = 0;//记录进位
ListNode* cur1 = l1, *cur2 = l2;
while(cur1 || cur2 || x)
{
if(cur1)
{
x += cur1->val;
cur1 = cur1->next;
}
if(cur2)
{
x += cur2->val;
cur2= cur2->next;
}
tail->next = new ListNode(x % 10);
tail = tail->next;
x /= 10;
}
tail = new_head->next;
delete new_head;
return tail;
}
};
代码||
class Solution {
public:
ListNode* ReserveList(ListNode* head) {
ListNode * head= nullptr;
ListNode * curr = head;
while(curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(0); //创建哨兵位头结点
ListNode* tail = new_head;
int x = 0;//记录进位
l1 = ReserveList(l1);
l2 = ReserveList(l2);
ListNode* cur1 = l1, *cur2 = l2;
while(cur1 || cur2 || x)
{
if(cur1)
{
x += cur1->val;
cur1 = cur1->next;
}
if(cur2)
{
x += cur2->val;
cur2= cur2->next;
}
tail->next = new ListNode(x % 10);
tail = tail->next;
x /= 10;
}
tail = new_head->next;
tail = ReserveList(tail);
delete new_head;
return tail;
}
};
两两交换链表中的节点
https://leetcode.cn/problems/swap-nodes-in-pairs/
代码
- 递归的方式
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
//终止条件是链表只有一个节点 / 链表中没有节点
if(head == nullptr || head->next == nullptr) return head;
ListNode* nnext = swapPairs(head->next->next);
ListNode* newhead = head->next;
newhead->next = head;
head->next = nnext;
return newhead;
}
};
- 迭代的方式
原理
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
// 0个和1个节点直接返回就好
if(head == nullptr || head->next == nullptr) return head;
ListNode* newhead = new ListNode(0); //哨兵位头结点
newhead->next = head;
ListNode* prev =newhead;
ListNode* cur = prev->next;
ListNode* next = cur->next;
ListNode* nnext = next->next;
while(cur != nullptr && next != nullptr)
{
//交换节点
prev->next = next;
next ->next = cur;
cur ->next = nnext;
//更新位置
prev = cur;
cur = nnext;
if(cur != nullptr)
next = cur->next;
if(next != nullptr)
nnext = next->next;
}
cur = newhead->next;
delete newhead;
return cur;
}
};
重排链表(重要)
https://leetcode.cn/problems/LGjMqU/
原理
代码
class Solution {
public:
void reorderList(ListNode* head) {
// <=2节点的直接返回
if(head == nullptr || head->next == nullptr || head->next->next ==nullptr) return ;
//1.找到链表的中间节点
ListNode * slow = head, *fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
//2.将后半部分链表逆置
ListNode* new_head = new ListNode(0);
ListNode* cur = slow->next;
slow->next = nullptr; //断开链表
while(cur)
{
ListNode* next = cur->next;
cur->next = new_head->next;
new_head->next = cur;
cur = next;
}
//3.合并2个链表
ListNode* ret_head = new ListNode(0);
ListNode* prev = ret_head;
ListNode* cur1 =head, *cur2 = new_head->next;
while(cur1)
{
//先放第一个链表
prev->next = cur1;
cur1 = cur1->next;
prev = prev->next;
//再放第二个链表
if(cur2)
{
prev->next = cur2;
cur2 = cur2->next;
prev = prev->next;
}
}
delete new_head;
delete ret_head;
}
};
合并 K 个升序链表
https://leetcode.cn/problems/vvXgSW/
代码
class Solution {
struct cmp
{
bool operator() (const ListNode* l1, const ListNode* l2)
{
return l1->val > l2->val;
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
//创建小根堆
priority_queue<ListNode* ,vector<ListNode*>,cmp> heap;
//让所有链表的头结点加入到小根堆中
for(auto l :lists)
{
if(l) heap.push(l);
}
//合并k个有序链表
ListNode* new_head = new ListNode(0);
ListNode* prev = new_head;
//小根堆中还有元素说明还有链表没到nullptr
while(!heap.empty())
{
ListNode* min = heap.top();
heap.pop();
prev->next = min;
prev = min;
if(min->next) heap.push(min->next);
}
prev->next = nullptr;
prev = new_head->next;
delete new_head;
return prev;
}
};
//自己用vs调试的时候,可以加上下面代码调试一步一步看
int main()
{
vector<ListNode*> lists = { new ListNode(1, new ListNode(4, new ListNode(5))),
new ListNode(1, new ListNode(3, new ListNode(4))),
new ListNode(2, new ListNode(6)) };
mergeKLists(lists);
}
递归代码
class Solution {
public:
ListNode* MergeTowList(ListNode* l ,ListNode* r)
{
if(l == nullptr) return r;
if(r == nullptr) return l;
ListNode new_head ;
new_head.next = nullptr;
ListNode* cur1 = l, *cur2 = r, *prev = &new_head ;
while(cur1 && cur2)
{
if(cur1->val >= cur2->val)
{
prev = prev->next = cur2;
cur2 = cur2->next;
}
else
{
prev = prev->next = cur1;
cur1 = cur1->next;
}
}
if(cur1) prev->next =cur1;
if(cur2) prev->next =cur2;
return new_head.next;
}
ListNode* Merge(vector<ListNode*>& lists, int left, int right)
{
if(left > right) return nullptr;
if(left == right) return lists[left];
int mid = (left + right) >> 1;
ListNode* l = Merge(lists, left, mid);
ListNode* r = Merge(lists, mid + 1, right);
//合并2个有序链表
return MergeTowList(l,r);
}
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return Merge(lists, 0, lists.size() - 1);
}
};
K 个一组翻转链表
原理
代码
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
int N = 0;
ListNode * p = head;
while(p)
{
p = p->next;
N++;
}
N /= k; //划分N组
ListNode* new_head = new ListNode(0);
ListNode* prev = new_head;
ListNode* cur = head;
for(int i = 0; i < N; i++)
{
ListNode *first = cur;
for(int j = 0; j < k; j++)
{
ListNode* next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = next;
}
prev = first;
}
//把不需要翻转的接上
prev->next = cur;
cur = new_head->next;
delete new_head;
return cur;
}
};