链表
1.两数相加

题目较为简单,模拟加法计算即可。要注意的是,当cur1,cur2都走到空时且t为0时,加法计算才结束。
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
ListNode* cur1=l1,*cur2=l2;
ListNode* head=nullptr;
ListNode* tail=nullptr;
int t=0;
while(cur1||cur2||t)
{
if(cur1)
{
t+=cur1->val;
cur1=cur1->next;
}
if(cur2)
{
t+=cur2->val;
cur2=cur2->next;
}
if(head==nullptr)
head=tail=new ListNode(t%10);
else
{
tail->next=new ListNode(t%10);
tail=tail->next;
}
t/=10;
}
return head;
}
};
2.两两交换链表中的节点

对于无头节点的单链表,添加一个哨兵卫头节点能更方便地解题。同时,可以多定义变量来记录节点,这样在链接节点时不易被绕晕。newHead为哨兵卫,cur为当前节点,prev为cur的前驱节点,Next为cur的下一个节点,nNext为Next的下一个节点,

第一次交换后如下所示。

如果是奇数个节点,那么交换结束后Next为空。

如果是偶数个节点,则交换后cur为空。

cpp
class Solution2 {
public:
ListNode* swapPairs(ListNode* head)
{
if (head == nullptr || head->next == nullptr)
return head;
ListNode* newHead = new ListNode(-1);
newHead->next = head;
ListNode* prev = newHead, * cur = prev->next, * Next = cur->next, * nNext = Next->next;
while (cur && Next)
{
prev->next = Next;
cur->next = nNext;
Next->next = cur;
prev = cur;
cur = nNext;
if (cur)
Next = cur->next;
if (Next)
nNext = Next->next;
}
cur = newHead->next;
delete newHead;
return cur;
}
};
3.重排链表

这道题考察了三个知识点,分别是找到链表中间节点、链表逆置、合并链表。解决这道题的思路是把链表从中间断开,再把后半部分链表逆置,最后按一定规则合并这两个链表。每一步的实现方法多样,如链表逆置可以用头插法或三指针法,合并链表时可以创建哨兵卫,把两个链表的节点链接到新链表中,也可以不创建哨兵卫交替链接到原链表。

cpp
class Solution {
public:
ListNode* GetMid(ListNode* head)//获取中间节点
{
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
void reverseList(ListNode*& head)//链表逆置(三指针)
{
if (head == nullptr)
return;
ListNode* prev = nullptr, * cur = head, * Next = cur->next;
while (cur)
{
cur->next = prev;
prev = cur;
cur = Next;
if (Next)
Next = Next->next;
}
head = prev;
}
void reorderList(ListNode* head)//重排链表
{
if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
return;
ListNode* mid = GetMid(head);
ListNode* Head = mid->next;
mid->next = nullptr;
reverseList(Head);
ListNode* cur1 = head, * cur2 = Head;
ListNode* n1 = cur1->next, * n2 = cur2->next;
while (cur1 && cur2)
{
cur1->next = cur2;
cur2->next = n1;
cur1 = n1;
cur2 = n2;
if (n1)
n1 = n1->next;
if (n2)
n2 = n2->next;
}
}
};
下面代码则采用头插法逆置链表,创建哨兵卫合并链表。
cpp
class Solution4 {
public:
void reorderList(ListNode* head)
{
if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
return;
//找中间节点
ListNode* slow = head, * fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
//头插法逆置
ListNode* Head = new ListNode(-1);
ListNode* cur = slow->next;
slow->next = nullptr;
while (cur)
{
ListNode* Next = cur->next;
cur->next = Head->next;
Head->next = cur;
cur = Next;
}
//合并链表
ListNode* ret = new ListNode(-1);
ListNode* cur1 = head, * cur2 = Head->next, * prev = ret;
while (cur1)
{
prev->next = cur1;
cur1 = cur1->next;
prev = prev->next;
if (cur2)
{
prev->next = cur2;
cur2 = cur2->next;
prev = prev->next;
}
}
delete Head;
delete ret;
}
};
4.合并k个升序链表

暴力解法,第一个链表与第二个链表合并,然后再和第三个链表合并,以此类推,这样的时间复杂度为O(n*k2)。
可以用优先级队列优化,建立小堆,先把链表第一个节点放入堆中,每次取堆顶元素,将堆顶元素重新链接成一个新链表。时间复杂度O(n * k * log k)。
cpp
struct Greater
{
bool operator()(ListNode* l1, ListNode* l2)
{
return l1->val > l2->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists)
{
priority_queue<ListNode*, vector<ListNode*>, Greater> pq;//建小堆
for (auto& e : lists)//入堆
if (e)
pq.push(e);
ListNode* newHead = new ListNode(-1);
ListNode* tail = newHead;
while (!pq.empty())
{
ListNode* cur = pq.top();
pq.pop();
tail->next = cur;
tail = cur;
if (cur->next)
pq.push(cur->next);
}
tail = newHead->next;
delete newHead;
return tail;
}
};
也可以用分治的思想,把数组lists不断分成两部分,最后合并两个有序链表并回溯

cpp
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];
int mid = (left + right) >> 1;
ListNode* l1 = Merge(lists, left, mid);
ListNode* l2 = Merge(lists, mid + 1, right);
return MergeTwoList(l1, l2);
}
ListNode* MergeTwoList(ListNode* l1, ListNode* l2)//合并两个有序链表
{
if (l1 == nullptr)
return l2;
if (l2 == nullptr)
return l1;
ListNode head;
ListNode* cur1 = l1, * cur2 = l2, * prev = &head;
while (cur1 && cur2)
{
if (cur1->val <= cur2->val)
{
prev->next = cur1;
prev = cur1;
cur1 = cur1->next;
}
else
{
prev->next = cur2;
prev = cur2;
cur2 = cur2->next;
}
}
if (cur1)
prev->next = cur1;
if (cur2)
prev->next = cur2;
return head.next;
}
};
5.K个一组翻转链表

先根据链表长度求出需要逆置n组,执行n次循环。使用头插法逆置,用指针 t 记录每组逆置前的第一个节点,下一组逆置时应在 t 后头插。

cpp
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k)
{
if (head == nullptr || head->next == nullptr || k == 1)
return head;
int len = 0;
ListNode* cur = head;
while (cur)
{
cur = cur->next;
len++;
}
int n = len / k;
cur = head;
ListNode* newHead = new ListNode(-1), * prev = newHead, * Next = cur->next;
while (n--)
{
ListNode* t = cur;
int i = k;
while (i--)
{
cur->next = prev->next;
prev->next = cur;
cur = Next;
if (Next)
Next = Next->next;
}
prev = t;
}
prev->next = cur;
prev = newHead->next;
delete newHead;
return prev;
}
};