原题目
面试题 02.06. 回文链表
编写一个函数,检查输入的链表是否是回文的。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
第一遍解法
回文数正序与倒数一样,所以想到了头插法逆序的性质。
使用头插法创建一个新的链表,因与原链表逆序。
从头至尾,依次比较两个链表,若全相等,则认为是回文链表。
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode *head2 = new ListNode(-1), *p = head, *p2;
while (p) {
p2 = new ListNode(p->val);
p2->next = head2->next;
head2->next = p2;
p = p->next;
}
p = head;
p2 = head2->next;
while (p && p2) {
if (p->val != p2->val) { return false; }
p = p->next;
p2 = p2->next;
}
return true;
}
};
时间复杂度: O(n)
空间复杂度: O(n) -> 新创建了一个原来的链表
进阶,回文链表是前一半元素与后一半元素顺序相反,所以我们可以原地逆序前一半链表元素,然后使用两个指针分别指向前一半和后一半的头,进行依次比较。
此时空间复杂度为O(1)。
class Solution {
public:
bool isPalindrome(ListNode* head) {
int len = 0;
// 获取链表长度,用于原地逆序前一半元素
ListNode *pre = head, *cur;
while (pre) {
len++;
pre = pre->next;
}
// 原地逆序前一半元素
int mid = len / 2;
pre = head;
cur = pre;
while (cur && mid--) {
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
// 奇数时,需要跳过中间的元素
mid = len / 2;
if (cur && len % 2) { cur = cur->next; }
// 依次比较元素,pre为前一半元素的头指针, cur为后一半元素的头指针
while (pre && cur && mid--) {
if (pre->val != cur->val) {
return false;
}
pre = pre->next;
cur = cur->next;
}
return true;
}
};
时间复杂度: O(n)
时间复杂度: O(1)
网上好的解法
快慢指针,也是逆序前一半元素。只是使用了快指针每次移动两个单位,这个逆序前一半元素完后,快指针指向末尾。就不用像前面还要计算长度。
bool isPalindrome(ListNode* head)
{
ListNode* slow = head, *fast = head, *prev = NULL;
while (fast && fast->next)
{
fast = fast->next->next;
ListNode* temp = slow->next;
slow->next = prev;
prev = slow;
slow = temp;
}
if (fast)
slow = slow->next;
while (prev && slow)
{
if (prev->val != slow->val)
return false;
prev = prev->next;
slow = slow->next;
}
return true;
}
作者:anobody-5
链接:https://leetcode-cn.com/problems/palindrome-linked-list-lcci/solution/liang-chong-fang-fa-jie-jue-hui-wen-lian-biao-wen-/
来源:力扣(LeetCode)
时间复杂度: O(n)
空间复杂度: O(1)
最后的代码
与网上的代码差不多。
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode *slow = head, *fast = head, *prev = NULL;
while (fast && fast->next) {
fast = fast->next->next;
ListNode *temp = slow->next;
slow->next = prev;
prev = slow;
slow = temp;
}
if (fast) { slow = slow->next; }
while (prev && slow) {
if (prev->val != slow->val)
return false;
prev = prev->next;
slow = slow->next;
}
return true;
}
};
时间复杂度: O(n)
空间复杂度: O(1)
小结
- 理解快慢指针思想,有时可以避免一些不必要的操作。