反转链表的效果示意图
要改变链表结构时,通常加入一个创建的临时头结点会更容易操作
时间复杂度:遍历2遍,2n
空间复杂度:额外创建一个栈,n
(空间创建一个数组长度最大为5000,你说这个数组是栈也可以,只要光通过一端对它进行操作就可以了)
/**
* 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) {
ListNode h;//临时头结点
h.next = head;
//利用栈
stack<int>s;//创建栈
for(ListNode*p=head;p!=NULL;p=p->next)//从头遍历
{
//入栈
s.push(p->val);//将val值挨个放入栈
}
for(ListNode*p=head;p!=NULL;p=p->next)
{
//出栈
p->val=s.top();
s.pop();//让s.top出栈
}
return h.next;
}
};
利用单链表的头插法进行逆置,将所有结点从后往前重新头插一遍就好了
但比上面的算法要慢一点,因为修改一两个指针的指向也要花时间的
指针p指向图中位置,将head->next断开,赋值为空,
然后利用头插,将每一个结点再重新头插到这个头结点的单链表里面
先插一个1,再插一个2,因为是头插,所以这个2就在1的前面;同理,4,3,2,1
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head) {
if(head==NULL||head->next==NULL)//只有0(无法反转)或1(转完还是自己)个点,
//直接返回(反转要至少2个数据结点以上)
return head;
struct ListNode h;//临时头结点
h.next=NULL;//头结点为空的单链表,等会用来头插
//头插需要单独一个指针把后面标记起来,以免找不到数据了
//struct ListNode*p=head->next;不用初始化,while里面有
struct ListNode*p;//用p记录
while(head!=NULL)//后面还有结点
{
//头插,先绑后面,再绑前面
p=head->next;//初始化
//把head头插到h中
head->next=h.next;// head->next=NULL,右
h.next=head;//左
head=p;//结点往后走
}
return h.next;
}
即双指针解法
一个指针current指向头结点head,正常正向链表head的next指向后(右)边下一个结点
现在要反转,就是说head的next要反过来指向前(左)边,即head成了最后一个结点指向空
则需要另一个指针pre,定义pre为cur指针结点的前一个位置的指针,方便让cur从指向后一位的方向改成指向前一位,所以pre定义在cur头结点的前面
/**
* 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) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;//初始化,cur指向头结点
ListNode* pre = NULL;//初始化,
//pre在cur的前一个,方便反转操作cur->next = pre;为空是cur第一次反转后就从头结点变成尾结点了
while(cur)//cur!=NULL为循环终止条件
{
temp = cur->next; // 保存一下 cur的下一个节点,绑住后面,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;//pre先向后走移动,将2边连接住
//同时,若先移动cur,即cur = temp;那么此时cur的值已经改变了,
//就不能 pre = cur向后移动了,那么pre就不能移动到cur当初的位置了
cur = temp;//cur再向后走
}//出循环遍历则cur==NULL,此时pre指向反转完的新头结点,原尾结点
return pre;//返回头结点
}
};
/**
* 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* reverse(ListNode* pre,ListNode* cur)//反转链表函数
{
if(cur == NULL)//循环终止条件,这里是递归终止条件
return pre;
ListNode* temp = cur->next;//暂存后面
cur->next = pre;//反转方向
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
//向后移动在双指针里面是一层循环,在递归里面是进入下一层递归
//下一层递归里面参数pre = cur;cur = temp;所以把(cur,temp)参数传进去
return reverse(cur,temp);//reverse(pre,cur)的实参
}
ListNode* reverseList(ListNode* head)//力扣给的主函数,里面光调用反转函数就好了
{
// 和双指针法初始化是一样的逻辑
// ListNode* cur = head;
// ListNode* pre = NULL;
return reverse(NULL, head);//reverse(pre,cur)的实参
}
};
上面两第二个图片的运行结果是错的