一开始看到下面的提示,我就先考虑了下思路。
最简单思路,把链表复制到数组里面。主要花在了空间复杂度上。但空间复杂度高。
如果要求空间复杂度常数,想到的就是反转后半部分链表。但是又觉得是不是太麻烦。对了下答案,还真是这中思路。
唯一思路没通的是递归解法。
1、最简单思路
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int> p;
int i=-1,j=0;
while(head!=nullptr)
{
p.push_back(head->val);
head=head->next;
i++;
}
while(j<i)
{
if(p[i--]!=p[j++])
return false;
}
return true;
}
};
2、反转后半部分链表的做法,按我的思路最少需要三遍遍历,第一遍求长度,第二遍进行到n/2处开始反转,第三遍才是对比是否回文。
但看了答案解法,发现只要两遍就够了,第一遍长度可以不用求的,直接用快慢指针求到中间位置。直接开始反转,还是太嫩了,那就直接上快慢指针解法。
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head==nullptr||head->next==nullptr)
return true;
ListNode *fast=head,*slow=head,*halfhead=nullptr;
while(!(fast==nullptr||fast->next==nullptr))
{
fast=fast->next->next;
slow=slow->next;
}
//我这里是想最后循环中止条件简单点,所以让奇数的情况从n/2+2开始。
if(fast!=nullptr)
slow=slow->next;
fast=slow->next;
slow->next=nullptr;
while(fast!=nullptr)
{
halfhead=fast;
fast=fast->next;
halfhead->next=slow;
slow=halfhead;
}
while(slow!=nullptr)
{
if(head->val!=slow->val)
return false;
head=head->next;
slow=slow->next;
}
return true;
}
};
递归解法。递归解法关键在于边界条件和递归时机。
之前我想不明白,是因为我把left也放在递归函数里,这样自然无法解答。实际上他俩一个是退一个是进,是没法放在一个递归里,所以只能把left放在函数体外。惭愧的是这个关键点是看了答案才想出来的。
class Solution {
public:
ListNode *left;
bool issame(ListNode *right)
{
if(right==nullptr)
return true;
bool res=issame(right->next);
bool now=(left->val==right->val);
left=left->next;
return res&&now;
}
bool isPalindrome(ListNode* head) {
left=head;
return issame(head);
}
};
第一遍递归有些变量没有必要,逐渐优化为下面
class Solution {
public:
ListNode *left;
bool issame(ListNode *right)
{
if(right==nullptr)
return true;
if(!issame(right->next))
return false;
if(left->val!=right->val)
return false;
left=left->next;
return true;
}
bool isPalindrome(ListNode* head) {
left=head;
return issame(head);
}
};