题目如下:
请判断一个链表是否为回文链表。
输入: 1->2
输出: false
输入: 1->2->2->1
输出: true
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
首先读题,不考虑进阶情况的话,由于回文链表的数值是对称的。
因此,我们可以遍历一遍输入的链表,声明两个空的vector 分别为left和right,每读取一个值,将节点的node->val在left的左边插入,在right的右边插入。
可以想得到,我们得到的right的顺序和链表的数值的顺序是一样的;而left的数值的顺序和链表的数值顺序相反。
由于回文链表的数值对称,因此,如果我们最终得到的两个vector是相等的话,则这个链表是回文链表。
这样做的只用了一次遍历,时间复杂度是O(n),但是用了两个长度为n的vector,因此空间复杂度为O(n)。
解答代码如下:
class Solution
{
public:
bool isPalindrome(ListNode* head)
{
if (!head || !(head->next))
{
return true;
}
vector<int> left;
vector<int> right;
while (head)
{
left.insert(left.begin(), head->val);
right.push_back(head->val);
head = head->next;
}
return left == right;
}
};
我们之前用的方法时间复杂度达到了要求,但是空间复杂度不符合进阶的要求。
我之前做过反转链表的题目,于是想到,首先设置一个first节点,指向链表表头;
然后遍历一次得到链表长度count,然后从链表的(count + 1)/2的地方开始,反转链表(也就是说将链表的后半部分反转,奇数链表的话反转过后后半部分少一个节点,比如,五个节点的链表,前面三个节点,后面两个节点);反转之后得到原本链表尾巴上的节点,将头结点和尾节点同时开始遍历,即可判断出结果。
这一思路,一共用了三个ListNode指针,空间复杂度为O(1);一共遍历四次,时间复杂度为O(n),符合进阶要求。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
{
public:
bool isPalindrome(ListNode* head)
{
if (!head || !(head->next))
{
return true;
}
ListNode* first = new ListNode(0);
first = head;
int count = 1;
while (head->next)
{
count += 1;
head = head->next;
}
head = first;
count = (count + 1) / 2;
while (count > 0)
{
head = head->next;
count--;
}
// head = first;
ListNode* pre;
ListNode* pnext;
pre = NULL;
// cout << head->val << endl;
while (head->next)
{
pnext = head->next;
head->next = pre;
pre = head;
head = pnext;
}
head->next = pre;
while(head)
{
if(first->val != head->val)
{
// delete first;
return false;
}
head = head->next;
first = first->next;
}
// delete first;
return true;
}
};
这里遇到了一个问题,就是不注释delete的话会报错,报错信息为:double free or corruption (out)。
感觉要代码还是可以继续改得简洁一点,不过感觉这样代码挺好读懂的,希望各位大佬批评指正,提出一下更好的思路。