对于不带头结点的单链表,给定头结点head,返回反转后的头结点
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) {}
};
几种参考思路如下:
1.将每个结点的next指向前面结点
2.从第二个结点依次开始插入到“头结点”的前面
3.最后那个“尾结点”是反转后的“头结点”,所以从第一个结点依次开始插入
“尾结点”的后面
4.联想到栈先入先出的特性,可以考虑使用辅助栈
思路一:(递归)
ListNode* reverseList(ListNode* head)
{
if(!head || !head->next) return head;
//newHead将是最后返回的新链表头结点
//每次递归后head的next指向一个已经反转的子链表
//所以只需要将head的指针指向它的前面结点即可
//可以有多种实现方法
//比如使用辅助头指针pre,令pre->next = head
//这里采用的是:将head和他后面的节点围成环,再从head后面割裂
//总之目的是让链表中每个节点的next指针指向原链表中他前面的结点
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
分析:
时间复杂度主要来源是递归的层层调用,即O(n)
空间复杂度主要来源是每次递归都会创建一个newHead变量,即O(n)
思路一:(非递归)
ListNode* reverseList(ListNode* head)
{
if(!head || !head->next) return head;
//使用双指针来同时移动
//其中pre指向当前节点的前面结点,pi指向当前结点
//随着pi顺着单链表移动,不断反转pi的next指针
//最终跑完整个链表,pre刚好指向原链表最后一个结点
//注意此时原链表head指针此时存在环路结构,要把它的next领域赋零
ListNode *pre = head, *pi = head->next;
ListNode* temp;
while(pi)
{
temp = pi->next;
pi->next = pre;
pre = pi;
pi = temp;
}
head->next = nullptr;
return pre;
}
分析:
时间复杂度同样来源于对链表的遍历操作,O(n)开销
空间复杂度来源于pre、pi、temp指针,为常数开销O(1)
思路二:
ListNode* reverseList(ListNode* head)
{
if(!head || !head->next) return head;
ListNode* cursor = head;
ListNode* temp = nullptr;
while(head->next)
{
temp = head->next;
head->next = temp->next;
temp->next = cursor;
cursor = temp;
}
return cursor;
}
分析:
时间复杂度O(n)
空间复杂度只使用了两个临时变量,O(1)
思路三:
(这种方法其实很适合于带有头结点的单链表,在这种不带头结点的情况其实很冗余)
ListNode* reverseList(ListNode* head)
{
if(!head || !head->next) return head;
ListNode* pi = head;
while(pi->next) pi = pi->next;
ListNode* Hp = new ListNode;
Hp->next = head;
while(Hp->next != pi)
{
head = Hp->next;
Hp->next = head->next;
head->next = pi->next;
pi->next = head;
}
head = pi;
delete Hp;
return head;
}
分析:
时间复杂度O(n)
空间复杂度来源于几个临时变量,为O(1)
思路四:
ListNode* reverseList(ListNode* head)
{
stack<ListNode*> myStack;
ListNode* temp;
while(head)
{
temp = head->next;
head->next = nullptr;
myStack.push(head);
head = temp;
}
ListNode* Hp = new ListNode;
head = Hp;
while(!myStack.empty())
{
head->next = myStack.top();
myStack.pop();
head = head->next;
}
head = Hp->next;
delete Hp;
return head;
}
分析:
时间复杂度来源入栈和出栈,仍是链表遍历的时间,还是O(n)级别
空间复杂度来源于栈,O(n)
当然,本人对于栈仅有粗略研究,代码没有更好优化,速度可能会稍微快一点但也就这样了。