LeetCode专题——链表
1.两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头
示例1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
1)我的思路
/**
* 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* result=new ListNode;
int flag=0; //进位
result->val=l1->val+l2->val;
flag=result->val/10;
result->val=result->val%10;
ListNode* taile=result; //始终指向链表尾部
l1=l1->next;
l2=l2->next;
//某一个到达结尾的时候退出while
while(l1!=nullptr&&l2!=nullptr)
{
ListNode *node=new ListNode;
node->val=l1->val+l2->val+flag;
flag=node->val/10;
node->val=node->val%10;
taile->next=node;
taile=node;
l1=l1->next;
l2=l2->next;
}
//若两个同时为空,直接返回
if(l1==nullptr&&l2==nullptr)
{
//最后一位有进位
if(flag)
{
ListNode* node=new ListNode;
node->val=1;
taile->next=node;
taile=node;
}
return result;
}
else
{
//若l1为空,说明l2不为空
if(l1==nullptr)
{
//不为空
while(l2!=nullptr)
{
l2->val+=flag;
flag=l2->val/10;
l2->val=l2->val%10;
taile->next=l2;
taile=l2;
l2=l2->next;
}
//为空且有进位
if(flag)
{
ListNode* node=new ListNode;
node->val=1;
taile->next=node;
taile=node;
return result;
}
return result;
}
else
{
while(l1!=nullptr)
{
l1->val+=flag;
flag=l1->val/10;
l1->val=l1->val%10;
taile->next=l1;
taile=l1;
l1=l1->next;
}
if(flag)
{
ListNode* node=new ListNode;
node->val=1;
taile->next=node;
taile=node;
return result;
}
return result;
}
}
}
};
运行结果:
2)官方解答
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head = nullptr, *tail = nullptr;
int carry = 0; //保存进位
//当两个链表同时到达末尾时才结束循环
while (l1 || l2) {
//获取l1和l2中的值;若为空,值为0;
int n1 = l1 ? l1->val: 0;
int n2 = l2 ? l2->val: 0;
int sum = n1 + n2 + carry;
//若链表为空,head和tail同时赋值,若不为空,添加到链表尾,并更新tail
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
//是否进位
carry = sum / 10;
//继续遍历链表
if (l1) {
l1 = l1->next;
}
if (l2) {
l2 = l2->next;
}
}
//若最后产生进位,链表需要新增一个节点
if (carry > 0) {
tail->next = new ListNode(carry);
}
return head;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2.删除链表的倒数第N个节点
题目描述:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例2:
输入:head = [1], n = 1
输出:[]
示例3:
输入:head = [1,2], n = 1
输出:[1]
1)我的思路
/**
* 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* removeNthFromEnd(ListNode* head, int n) {
int sz=0;
ListNode* head2=head;
//遍历求长度
while(head2!=nullptr)
{
sz++;
head2=head2->next;
}
//如果删除的是头结点
if(sz==n)
{
head=head->next;
return head;
}
head2=head;
//求倒数第n个节点的前节点
while((sz-n)>1)
{
head2=head2->next;
sz--;
}
//删除第n个节点
ListNode* temp=head2->next;
head2->next=temp->next;
delete temp;
return head;
}
};
2)官方解答
使用双指针:使用first和second指针,先让first指针走n步,然后first指针和second指针同步走,这样就会导致first指针和second指针相差n。最后当first指针为空时,second指针恰好是倒数第n个节点。
但是我们需要指导倒数第n个节点的前驱节点,才可以删除倒数第n个节点,因此second指针可以指向哑结点。
哑结点:哑结点的next指针指向头结点,值为0;
/**
* 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* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
ListNode* first = head;
ListNode* second = dummy;
for (int i = 0; i < n; ++i) {
first = first->next;
}
while (first) {
first = first->next;
second = second->next;
}
second->next = second->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/shan-chu-lian-biao-de-dao-shu-di-nge-jie-dian-b-61/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.合并两个有序链表
题目描述:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例2:
输入:l1 = [], l2 = []
输出:[]
示例3:
输入:l1 = [], l2 = [0]
输出:[0]
1)我的思路
/**
* 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* mergeTwoLists(ListNode* l1, ListNode* l2) {
//边界条件判断
if(l1==nullptr) return l2;
else if(l2==nullptr) return l1;
ListNode* ans=new ListNode();
ListNode* tail=new ListNode(0,ans);
//初始化ans
if(l1->val<l2->val)
{
ans=l1;
tail=ans;
l1=l1->next;
}
else
{
ans=l2;
tail=ans;
l2=l2->next;
}
//一直到l1或者l2为空
while(l1 && l2)
{
if(l1->val>l2->val)
{
ListNode* node=new ListNode();
node->val=l2->val;
tail->next=node;
tail=node;
l2=l2->next;
}
else
{
ListNode* node=new ListNode();
node->val=l1->val;
tail->next=node;
tail=node;
l1=l1->next;
}
}
//若l1为空,连接l2
if(l1==nullptr)
{
while(l2)
{
ListNode* node=new ListNode();
node->val=l2->val;
tail->next=node;
tail=node;
l2=l2->next;
}
}
else if(l2==nullptr)
{
while(l1)
{
ListNode* node=new ListNode();
node->val=l1->val;
tail->next=node;
tail=node;
l1=l1->next;
}
}
return ans;
}
};
2)官方解答:
(1)递归调用
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr) {
return l2;
} else if (l2 == nullptr) {
return l1;
} else if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
(2)遍历
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
prev->next = l1;
l1 = l1->next;
} else {
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4.合并k个升序链表
题目描述:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例2:
输入:lists = []
输出:[]
示例3:
输入:lists = [[]]
输出:[]
1)我的思路
/**
* 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) {}
* };
*/
ListNode* mergeTwoLists(ListNode* l1,ListNode*l2)
{
if (l1 == nullptr) {
return l2;
} else if (l2 == nullptr) {
return l1;
} else if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* head=nullptr;
for(vector<ListNode*>::iterator it=lists.begin();it!=lists.end();it++)
{
if(it==lists.begin())
{
head=*it;
continue;
}
head=mergeTwoLists(head,*it);
}
return head;
}
};
2)官方解答
class Solution {
public:
ListNode* mergeTwoLists(ListNode *a, ListNode *b) {
if ((!a) || (!b)) return a ? a : b;
ListNode head, *tail = &head, *aPtr = a, *bPtr = b;
while (aPtr && bPtr) {
if (aPtr->val < bPtr->val) {
tail->next = aPtr; aPtr = aPtr->next;
} else {
tail->next = bPtr; bPtr = bPtr->next;
}
tail = tail->next;
}
tail->next = (aPtr ? aPtr : bPtr);
return head.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode *ans = nullptr;
for (size_t i = 0; i < lists.size(); ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/he-bing-kge-pai-xu-lian-biao-by-leetcode-solutio-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
5.两两交换链表中的节点
题目描述:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
第一个节点与第二个节点交换,第三个节点与第四个节点交换,以此类推。
示例1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例2:
输入:head = []
输出:[]
示例3:
输入:head = [1]
输出:[1]
1)我的思路
/**
* 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* swapPairs(ListNode* head) {
//head为空或者只有一个节点
if(head==nullptr||head->next==nullptr) return head;
ListNode* next=head->next;
head->next=next->next;
next->next=head;
//递归调用
if(head->next!=nullptr)
{
head->next=swapPairs(head->next);
}
//next会变成新的头结点
return next;
}
};
2)官方解答:
思路与算法
创建哑结点 dummyHead,令 dummyHead.next = head。令 temp 表示当前到达的节点,初始时 temp = dummyHead。每次需要交换 temp 后面的两个节点。
如果 temp 的后面没有节点或者只有一个节点,则没有更多的节点需要交换,因此结束交换。否则,获得 temp 后面的两个节点 node1 和 node2,通过更新节点的指针关系实现两两交换节点。
具体而言,交换之前的节点关系是 temp -> node1 -> node2,交换之后的节点关系要变成 temp -> node2 -> node1,因此需要进行如下操作。
temp.next = node2
node1.next = node2.next
node2.next = node1
完成上述操作之后,节点关系即变成 temp -> node2 -> node1。再令 temp = node1,对链表中的其余节点进行两两交换,直到全部节点都被两两交换。
两两交换链表中的节点之后,新的链表的头节点是 dummyHead.next,返回新的链表的头节点即可。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* temp = dummyHead;
while (temp->next != nullptr && temp->next->next != nullptr) {
ListNode* node1 = temp->next;
ListNode* node2 = temp->next->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return dummyHead->next;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs/solution/liang-liang-jiao-huan-lian-biao-zhong-de-jie-di-91/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6.翻转链表
题目描述:
翻转一个给定链表。
示例1:
输入:list = [1,2,3,4,5]
输出:[5,4,3,2,1]
/**
* 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* reverseList(ListNode* head) {
if(head==nullptr||head->next==nullptr) return head;
ListNode* current=head;
ListNode* pre=nullptr;
while(current)
{
ListNode* temp=current->next;
current->next=pre;
pre=current;
current=temp;
}
return pre;
}
}
7.K个一组翻转链表
题目描述:
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例1:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例2:
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
示例3:
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
示例4:
输入:head = [1], k = 1
输出:[1]
class Solution {
public:
// 翻转一个子链表,并且返回新的头与尾
pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail) {
ListNode* prev = tail->next;
ListNode* p = head;
while (prev != tail) {
ListNode* nex = p->next;
p->next = prev;
prev = p;
p = nex;
}
return {tail, head};
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* hair = new ListNode(0);
hair->next = head;
ListNode* pre = hair;
while (head) {
ListNode* tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail->next;
if (!tail) {
return hair->next;
}
}
ListNode* next = tail->next;
// 这里是 C++17 的写法,也可以写成
// pair<ListNode*, ListNode*> result = myReverse(head, tail);
// head = result.first;
// tail = result.second;
tie(head, tail) = myReverse(head, tail);
// 把子链表重新接回原链表
pre->next = head;
tail->next = nex;
pre = tail;
head = tail->next;
}
return hair->next;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/k-ge-yi-zu-fan-zhuan-lian-biao-by-leetcode-solutio/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。