请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
- 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
完整代码
题目分析:
本道题的重点在于如何保证代码的空间复杂度为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 == NULL || head->next == NULL)
return true;
//1.找到链表最中间的节点
ListNode * slow = head, * fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}//循环结束后,slow指向最中间的节点(链表中有奇数个节点),或者回文的开始节点(链表中有偶数个节点)
//2.将slow后面的链表逆转
ListNode * cur = slow, *pre = NULL;
while(cur){
ListNode * next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}//循环结束后,pre指向逆转后的链表的头节点
//3.判断是否是回文
while(head != slow){
if(head->val != pre->val)
return false;
head = head->next;
pre = pre->next;
}
return true;
}
};
参考:官方题解
解法一:
定义一个和链表长度相同的数组,定义两个指针,头指针,尾指针,比较头尾指针指向的元素是否相等。
时间复杂度O(N),空间复杂度O(N)
解法二:
递归
依然是将第一个元素和最后一个元素比较,借助递归可以实现链表的从后往前的比较,递归过程中将节点先放入栈中,当走到最后一个节点时,再进行比较。
时间复杂度O(N),空间复杂度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) {
ListNode* firstNode = head;
return recursive(head, firstNode);
}
private:
bool recursive(ListNode* head, ListNode* &firstNode){
if(head != NULL){
if(!recursive(head->next, firstNode))//目的是递归到最后一个节点,递归过程中,若是有不满足条件的直接返回false
return false;
if(head->val != firstNode->val)//比较两个节点的值是否相等
return false;
firstNode = firstNode->next;//在之前的节点都是回文 且当前节点相等的情况下,继续比较下一个
}
return true;
}
};
上述第一种方法的优化
基本思想:
逆转前半部分链表,边寻找中间节点,边进行逆转。
还有一点需要说明,在判断链表是否是回文的时候将链表进行逆转了,这是使用该函数时不希望有的情况,可以在判断结束后,将改变后的链表再逆转回来。
/**
* 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 == NULL || head->next == NULL)
return true;
//1.寻找中间节点,同时逆转前半部分
ListNode * slow = head, * fast = head, * pre = NULL, * cur;
while(fast && fast->next){
cur = slow;
slow = slow->next;
fast = fast->next->next;
cur->next = pre;
pre = cur;
}
//循环结束 ,pre指向逆转后的头节点(原链表前半部分的尾),slow指向后半部分节点
//2.判断是否回文
ListNode * l_head = pre;
ListNode * r_head = slow;
if(fast)//链表中节点是奇数的情况
r_head = r_head->next;
while(l_head && r_head && l_head->val == r_head->val){
l_head = l_head->next;
r_head = r_head->next;
}
bool res = (l_head == NULL ? true : false);
//3.将逆转后的链表还原
ListNode * temp = pre, * next;
pre = NULL;
while(temp){
next = temp->next;
temp->next = pre;
pre = temp;
temp = next;
}
pre->next = slow;
return res;
}
};