力扣-234-回文链表
这只是一道简单题,我看到题目示例的时候理所当然地想用“栈”来解决
扫描一个元素,如果栈空或者栈顶元素不等于当前节点值,就压栈,否则弹栈
后来却接连碰壁
- 没有考虑单节点,例如“1”地情况
- 没有考虑单数节点,例如“101”地情况
其实本质上还是没有考虑单数的情况
栈其实不好解决单数的情况,还是因为不知道长度的原因
我找到一个老哥写的栈解法
其实是这样:
- 遍历一遍链表,并把每个元素入栈
- 再遍历一遍链表,把每个元素和栈顶元素比较(比一个弹一个)
如果有不一样的,就返回false,否则返回true
O(N)的空间复杂度(一个长度为n的栈),其实还可以遍历一遍放入一个数组,然后首尾双指针往中间靠并比较元素是否相同,也差不多的
其实在做的过程中也意识到了,这并不是最优的做法,题目提示时间O(N),空间O(1)
其实应该用双指针……但是这是个链表啊,我不知道它的长度,就没法两头往中间扫描
O(1)空间复杂度用指针的解法肯定是跑不掉的,而且肯定是同向指针,只是我没想出来
官方题解
官方的题解是快慢指针:找到中间节点,然后反转后半部分的节点
这里复习一下反转反转链表,我记得最好的解法是用复制节点来做(记混了)
这里有一个我没想起来的点——通过快慢指针找链表的中间节点
快、慢指针初始位置都在链表头,快指针一次走两步,慢指针一次走一步,当快指针走到链表尾部的时候,慢指针指向的就是中间节点
思路
思路是这样的:
- 先找到链表的中间节点,用快慢指针的方法
如果是复数节点,就是前半部分的最后一个
- 将后半部分指针链表反转,返回尾节点的指针
- 头尾两个指针向中间靠,如果指向的值不一致,就表示不是回文链表
官方题解里有额外的操作,定义了一个布尔值,主要是为了后面把反转的后半部分链表再恢复过来
class Solution {
public:
// 找中间节点
ListNode* endOfHalfStart(ListNode* head) {
ListNode* fast = head;
ListNode* slow=head;
while (fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
}
}
bool isPalindrome(ListNode* head) {
if (!head) return true;
// 找到中间节点(前半部分的尾节点)并反转后半部分节点
ListNode* firstHalfEnd = endOfHalfStart(head);
// 这应该是尾节点才对吧
ListNode* secondHalfStart = reverseList(firstHalfEnd->next);
// 双指针,一个指向头,一个指向尾
ListNode* p1 = head;
ListNode* p2 = secondHalfStart;
bool result = true;
// 为什么这里p2的条件是!=nullptr,而不是!=p1
// 因为反转后中间节点(后半部分的第一个节点)的next指向会被置空 nullptr,这个节点被前后两个节点指向
// 这个情况在单双都会发生
while (result && p2 != nullptr) {
if (p1->val != p2->val) result = false;
p1 = p1->next;
p2 = p2->next;
}
firstHalfEnd->next = reverseList(secondHalfStart);
return result;
}
ListNode* reverseList(ListNode* head) {
ListNode* pre= nullptr;
ListNode* cur = head;
while (cur) {
ListNode* nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
};
这边还有道兄弟题:力扣-9-回文数