Description
Given a singly linked list, determine if it is a palindrome.
Example 1:
Input: 1->2
Output: false
Example 2:
Input: 1->2->2->1
Output: true
Follow up:
- Could you do it in O(n) time and O(1) space?
思路
解法一
用一个栈将全部元素存储起来,记录回文串的中点。从序列的两边往中间遍历元素,同时比较两者是否相等,如果不相等说明就不是回文串,反之则是回文串。
需要额外注意的是,中点需要根据奇偶情况而定,偶数时需要额外减一。
时间复杂度:O(n)
空间复杂度:O(n)
耗时 24 ms, faster than 69.10%, Memory 14.8 MB
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (!head) return true;
stack<int> stk;
for (ListNode *ptr = head; ptr != nullptr; ptr = ptr->next) {
stk.push(ptr->val);
}
// Only need to compare the half of the palindrome
int palindrome_pos = stk.size() & 1 ? stk.size() / 2 : stk.size()/2 - 1;
int i = 0;
ListNode *ptr = head;
while (i <= palindrome_pos) {
if (stk.top() != ptr->val) {
break;
}
stk.pop();
ptr = ptr->next;
++i;
}
if (i > palindrome_pos) {
return true;
} else {
return false;
}
}
};
解法二
解法一没法应对 follow up,所以我们需要再想想办法。
受到之前做的快慢指针套路题(求是否有环、求中点)的启发,我们可以使用快慢指针思路来记录回文串的中点,而且这么找中点会比解法一更好一点,因为我们不用讨论奇偶性,慢指针停下来的地方一定就是中点。OK,第一个 tricky 的点就解决了。
另一个 tricky 的点是,如何用另一个方法代替栈的“后入先出”的特性?
经过参考博客的启发,我发现了“链表逆置”其实可以在 O(n) 的时间复杂度解决的,那么就可以把回文串的后半部分做一个逆置操作,然后从起点与中点后的第一个位置开始比较,从而判断出这个串是不是回文串。比如"1 2 3 3 2 1",中点是第一个 3,逆置后的序列为"1 2 3 1 2 3",从第一个 1 和第二个 1 开始往后遍历并比较两者的值,从而判断出这个串是不是回文串。
时间复杂度:O(n)
空间复杂度:O(1)
耗时 28 ms, faster than 30.87 %, Memory 14.3 MB
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (!head || !head->next) return true;
// use "slow-fast point" to get mid point of linked list
ListNode *slow = head;
ListNode *fast = head;
while (fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
}
// reverse second half of linked list
ListNode *last = slow->next;
while (last->next) {
ListNode *tmp = last->next;
last->next = tmp->next;
tmp->next = slow->next;
slow->next = tmp;
}
// compare the first half and second half
ListNode *first = head;
while (slow->next) {
slow = slow->next;
if (first->val != slow->val) return false;
first = first->next;
}
return true;
}
};