一、经验总结
二、相关编程题
2.1 删除链表的倒数第 N 个结点
题目链接
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
题目描述
算法原理
编写代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *guard = new ListNode(0, head); //创建哨兵节点
ListNode *first = head, *second = guard;
while(n--)
{
first = first->next;
}
while(first != nullptr)
{
first = first->next;
second = second->next;
}
ListNode *del = second->next;
second->next = del->next;
delete del;
head = guard->next;
delete guard;
return head;
}
};
2.2 两数相加Ⅰ
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *ret = new ListNode(), *tail = ret;
ListNode *cur1 = l1, *cur2 = l2;
int t = 0;
while(cur1 != nullptr || cur2 != nullptr || t!=0)
{
if(cur1 != nullptr)
{
t += cur1->val;
cur1 = cur1->next;
}
if(cur2 != nullptr)
{
t += cur2->val;
cur2 = cur2->next;
}
tail->next = new ListNode(t%10);
tail = tail->next;
t/=10;
}
ListNode *del = ret;
ret = ret->next;
delete del;
return ret;
}
};
2.3 两数相加Ⅱ
题目链接
LCR 025. 两数相加 II - 力扣(LeetCode)
题目描述
算法原理
由于链表是从高位到低位存储两个非负整数,而加法是从低位到高位相加,因此需要先对两个链表进行reverse操作,再进行相加。返回的结果也是从高位到低位,所以翻转后每一位相加的结果应该进行头插,相当于进行两次翻转,最终就能得到结果。
两数相加的其他细节同上一题两数相加Ⅰ。
编写代码
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
reverse(l1);
reverse(l2);
ListNode *cur1 = l1, *cur2 = l2;
int t = 0;
ListNode *ret = new ListNode;
while(cur1 != nullptr || cur2 != nullptr || t!=0)
{
if(cur1 != nullptr)
{
t += cur1->val;
cur1 = cur1->next;
}
if(cur2 != nullptr)
{
t += cur2->val;
cur2 = cur2->next;
}
ListNode *newnode = new ListNode(t%10, ret->next); //头插,二次翻转
ret->next = newnode;
t/=10;
}
ListNode *del = ret;
ret = ret->next;
delete del;
return ret;
}
void reverse(ListNode*& head) //注意,这里要传引用
{
ListNode *ret = new ListNode;
ListNode *cur = head, *next = nullptr;
while(cur!=nullptr)
{
next = cur->next;
cur->next = ret->next; //头插,翻转
ret->next = cur;
cur = next;
}
ListNode *del = ret;
ret = ret->next;
delete del;
head = ret;
}
};
2.4 两两交换链表中的节点
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *guard = new ListNode(0, head);
ListNode *prev = guard, *cur = guard->next,
*next = nullptr, *nnext = nullptr;
while(cur != nullptr)
{
//确定下一个节点
next = cur->next;
if(next == nullptr) break;
nnext = next->next;
//两两交换
prev->next = next;
next->next = cur;
cur->next = nnext;
//修改指针
prev = cur;
cur = nnext;
}
head = guard->next;
delete guard;
return head;
}
};
2.5 重排链表
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
void reorderList(ListNode* head) {
//使用快慢指针确定链表的中间节点
ListNode *fast = head, *slow = head;
while(fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
}
//将链表一分为二,将第二个链表逆序
ListNode *cur1 = head, *cur2 = reverse(slow->next);
slow->next = nullptr;
//合并两链表
ListNode *guard = new ListNode, *tail = guard;
while(cur1 != nullptr)
{
//链接第一个链表节点
tail->next = cur1;
tail = tail->next;
cur1 = cur1->next;
//链接第二个链表节点
if(cur2 != nullptr)
{
tail->next = cur2;
tail = tail->next;
cur2 = cur2->next;
}
}
tail->next = nullptr;
head = guard->next;
delete guard;
}
ListNode* reverse(ListNode* head)
{
ListNode *guard = new ListNode;
ListNode *cur = head, *next = nullptr;
while(cur != nullptr)
{
next = cur->next;
cur->next = guard->next;
guard->next = cur;
cur = next;
}
head = guard->next;
delete guard;
return head;
}
};
2.6 合并k个升序链表
题目链接
题目描述
算法原理
编写代码
//利用优先级队列优化
class Solution {
struct Greater{
bool operator()(const ListNode *left, const ListNode *right)
{
return left->val > right->val;
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
//创建一个小根堆
priority_queue<ListNode*, vector<ListNode*>, Greater> heap;
ListNode *ret = new ListNode, *tail = ret;
//让所有头节点进入小根堆
for(auto e : lists)
{
if(e!=nullptr) heap.push(e);
}
//合并k个有序链表
while(!heap.empty())
{
ListNode *top = heap.top();
heap.pop();
tail->next = top; //尾插
tail = tail->next;
if(top->next != nullptr)
{
heap.push(top->next);
}
}
tail->next = nullptr;
ListNode *del = ret;
ret = ret->next;
return ret;
}
};
//分治-递归
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return _merge(lists, 0, lists.size()-1);
}
ListNode* _merge(vector<ListNode*>& lists, int begin, int end)
{
if(begin > end) return nullptr;
if(begin == end) return lists[begin];
int mid = (begin+end)/2;
ListNode *l1 = _merge(lists, begin, mid);
ListNode *l2 = _merge(lists, mid+1, end);
ListNode guard, *tail = &guard;
while(l1 != nullptr && l2 !=nullptr)
{
if(l1->val <= l2->val)
{
tail->next = l1;
tail = l1;
l1 = l1->next;
}
else
{
tail->next = l2;
tail = l2;
l2 = l2->next;
}
}
if(l1 != nullptr) tail->next = l1;
else tail->next = l2;
return guard.next;
}
};
2.7 K个一组翻转链表
题目链接
题目描述
算法原理
编写代码
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
//统计一下需要翻转多少组
int n = 0;
ListNode *cur = head;
while(cur!=nullptr)
{
++n;
cur = cur->next;
}
n/=k;
//进行n组翻转
ListNode guard(0, head), *prev = &guard, *next = nullptr;
cur = head;
while(n--)
{
ListNode *tmp = cur;
for(int i = 0; i < k; ++i)
{
next = cur->next;
cur->next = prev->next;
prev->next = cur;
cur = next;
}
prev = tmp;
}
//把不需要翻转的接上
prev->next = cur;
return guard.next;
}
};