描述
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0≤n≤1000
要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
题解
注意:本题代码全为不带头结点的链表逆置,若带头结点,则需要先将头节点的next指向空,逆置后再链接即可。(迭代法和头插法)
迭代法求解(原地逆置)
struct ListNode
{
int val;
struct ListNode *next;
};
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
ListNode* curr = head;
ListNode* next;
ListNode* prior = NULL;
while(curr!=NULL)
{
next = curr->next;//保存当前结点的下一个结点
curr->next = prior;//将当前结点的next指向它前一个结点(初始为空)
prior = curr;//更新前一个结点为当前节点
curr = next;//curr = curr->next;//更新当前节点为下一个结点
}
return prior;
}
};
递归法求解
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
//若链表本身为空或找到了链表最后一个结点,则直接返回head结点
if(head==NULL||head->next==NULL)
return head;
//递归翻转
ListNode* newhead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newhead;
}
};
头插法求解
与迭代法本质相同,只不过头插法是新建一个链表,对其在头部插入原链表的结点。
(注:如果该链表本身自带头节点,则可以将头节点的next置为NULL,然后就在该头节点之后进行头插法插入结点,相当于用头插法在带头结点的链表中原地逆置!)
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
ListNode* pHead = new ListNode(0);//新链表,设置一个哑节点作为头节点,避免特殊处理
ListNode* curr = head;
//遍历旧链表
while(curr)
{
ListNode* next = curr->next;//保存下一节点
curr->next = pHead->next;
pHead->next = curr;
curr = next;
}
return pHead->next;
}
};
进阶:每k个一组反转链表
迭代法(头插法)
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k)
{
ListNode* dummy = new ListNode(0,head);
ListNode* curr = head;
int len = 0;//先计算出链表的长度
while(curr != NULL) {
len++;
curr = curr->next;
}
int n = len/k;//计算出有几组
ListNode* pre = dummy;
curr = head;
for(int i=0;i<n;i++)
{
for(int j=0;j<k-1;j++)
{
//原地头插法逆置:pre为头,插到pre之后,其他节点之前
ListNode* t = curr->next;//保存下一结点
curr->next = t->next;
t->next = pre->next;//注意该地方的pre->next不可以为curr!因为curr并不是总等于pre->next
pre->next = t;
}
pre = curr;//更新新的头
curr = curr->next;
}
return dummy->next;
}
};
总结
任何关于链表的逆置问题,就考虑四个步骤:
1.首先把下一节点保存下来 next = curr->next;
2.curr->next该指向哪里 curr->next = A;
3.A该指向哪里 A = B
4.B该指向哪里(这一步其实就是更新当前结点)