LeetCode206.反转链表
1.题目
2.思路
1.我最初的设想是把最后一个节点依次前移,后来发现有两个困难。一是单向链表没办法访问已知结点的前驱结点,犯了一些错误。比如:
(1)LinkNode*q; q->next = p; //q根本没指向p的前驱
(2)LinkNode *q = dummyHead; q->next = p; //这样会变成dummyHead的next变为p 链表错排了!
二就是如果需要访问前驱节点,每次都需要遍历,时间复杂度太高。综上,我放弃了这个想法。
2.双指针应用。设定一前一后两个结点顺次后移。使后面的节点的指针指向它前面的结点。
3.递归。
下面分开实现一下。
3.代码实现
(1)双指针应用
双指针的基本思路:
1.我最开始的思路是统计链表长度,特判长度为0/1的情况,最后使用双指针。这样略复杂
2.记得使用tmp结点保存后指针后面的结点,要不就丢了
主体思路就是上图:(1)保存结点(2)指针反转(3)指针后移
先贴我自己最初始的代码,很复杂,有很多多余的步骤…
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* dummyHead = new ListNode(1);//哨头结点 考虑头结点为空
if(head!= NULL) dummyHead->next = head;
ListNode* p = dummyHead;
int size = 0;//初始化!
while(p->next != NULL)
{
p = p->next;
size++;//链表长度
}
//特判空链表和长度为1的情况
if(size == 0) return nullptr;
else if(size == 1) return head;
else{
ListNode *m = dummyHead->next;
ListNode *n = dummyHead->next->next;
ListNode *tmp;
while(n != NULL)
{//保存节点
tmp = n->next;
//反转
n->next = m;
m = n;
//后移
n = tmp;
}
dummyHead->next->next = nullptr;//这句必须有
delete dummyHead;
return m;
}
}
};
和大佬的代码对比了一下有两个最大的缺点:
1.没有必要使用哨兵结点,太多余,而且容易丢掉
dummyHead->next->next = nullptr;
这一步。
2.最近养成了特判的意识,会对空链表和节点为1的链表进行思考了,很开心。但是不一定需要特判,可以更精简!
精简之后的代码:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* tmp;
ListNode* m = NULL;
ListNode* n = head;
while(n != NULL)
{
//保存节点
tmp = n->next;
//反转
n->next = m;
//后移
m = n;
n = tmp;
}//不用对头结点进行特判了,第一步就置为NULL了
return m;
}
};
(2)递归方法
和双指针是一样的思路
ListNode* reverse(ListNode*m,ListNode* n){
if(n == NULL) return m;//递归出口
//存点
ListNode* tmp = n->next;
//交换
n->next = m;
//后移 这里我卡了一会儿 但其实和双指针思路一样
//m = n;
//n = tmp;
return reverse(n,tmp);
}
ListNode* reverseList(ListNode* head) {
//就是双指针的初始化
//ListNode* m = NULL; ListNode* n = head;
return reverse(NULL,head);
}