题目描述:给出一个链表,然你判断它是不是回文链表,是则返回true,不是则返回false;
比如1 2 3 5 3 2 1
和1 2 3 5 5 3 2 1
都是回文链表;
限制条件:控制时间复杂度为O(n)、空间复杂度为O(1);
解题思路:
step1:判断是否为回文链表,必然要判断前半部分(顺序)和后半部分(反序)的元素是否全部一致;所以,我们首先要找到链表的中间结点;
这里可能会想到先遍历一遍结点得到长度size,然后再从头遍历size/2的结点即可得到中间结点,这是普通的方法,需要遍历1.5次链表;而即将使用的双指针——快慢指针的方法只需遍历0.5次链表,原理就是快指针每次能走两步就走两步,慢指针每次走一步,这样当快指针到达终点(NULL)时,慢指针刚好指向后半部分链表的第一个元素;
方法一
step2:此时比较容易想到的就是将后半部分链表反转,然后再比较前半部分和反转后的后半部分链表即可,所以第二步将链表反转;只需要now、pre、aft
三个节点就能将链表反转;
step3:依次遍历两个链表,比较对应位置上的两个元素是否相等即可;
代码:
bool isPalindrome(ListNode* head) {//O(n)、O(1)
ListNode* slow = head, *fast = head;
while (fast) //find mid node
{
slow = slow->next;
fast = fast->next ? fast->next->next: fast->next;
}
ListNode *prev = nullptr;
while (slow) //reverse
{
ListNode* temp = slow->next;
slow->next = prev;
prev = slow;
slow = temp;
}
while (head && prev) //check
{
if (head->val != prev->val){
return false;
}
head = head->next;
prev = prev->next;
}
return true;
}
方法二:
采用递归的方法判断是否为回文链表;
step2:
1° 找重复:找出重复子问题
这里我们大概就能得到递归函数的大体样子了:
void check(ListNode *head1,int l,ListNode *&head2,int r,flag) //判断链表[l~r]是否为回文链表
因为栈的先进后出,所以head1为指向头结点的指针,当递归到5的时候,就会慢慢返回递归,从中间往前面走;
而对于从中间往后面走的指针,我们就需要使用引用&head2
来一步步增加它的值,让它往后走,从而保证head1和head2是比较的对应位置;
2° 找变化:变化的量为l,r,head2,flag
l,r代表check函数比较链表第i和第r个的元素,flag用于标志是否为回文链表,初始为true,如果出现两比较值不相等,则置为false
3° 找边界: 我们一步步将问题缩小,当最后l>=r
时表示子问题已达到最小规模(l==r 表示指向单个元素,无需比较;l>r表示超出界限),此时return
;
那么整个递归函数就是如下形式:
void check(ListNode *head1, int l, ListNode *&head2, int r, bool &flag) //判断链表[l~r]是否为回文 (注意head2是变化的 它要一次次往后走 一定要引用)
{
//边界条件
if (l >= r)
{
return;
}
//将问题规模缩小
if (r - l >= 2)
{
check(head1->next, l + 1, head2, r - 1, flag);
}
//比较对应位置节点值
int a = head1->val, b = head2->val;
if (a != b)
{
flag = false;
}
//head2指向下一个结点,此时该函数完成返回其调用函数时,head1也指向前一个结点,这样又能比较两个数是否相等了
head2 = head2->next;
}
step3:返回判断标志flag即可;
void check(ListNode *head1, int l, ListNode *&head2, int r, bool &flag) //判断链表[l~r]是否为回文 (注意head2是变化的 它要一次次往后走 一定要引用)
{
//边界条件
if (l >= r)
{
return;
}
//将问题规模缩小
if (r - l >= 2)
{
check(head1->next, l + 1, head2, r - 1, flag);
}
//比较对应位置节点值
int a = head1->val, b = head2->val;
if (a != b)
{
flag = false;
}
//head2指向下一个结点,此时该函数完成返回其调用函数时,head1也指向前一个结点,这样又能比较两个数是否相等了
head2 = head2->next;
}
bool isPalindrome(ListNode *head)
{
//如果有1个及以下的元素,返回true
if (head == nullptr || head->next == nullptr)
{
return true;
}
//利用快慢指针求链表个数和中间节点
int size = 0;
ListNode *slow = head, *fast = head, *mid_point = nullptr;
while (fast)
{ //find mid node
size++;
slow = slow->next;
fast = fast->next ? fast->next->next : fast->next;
}
//设置指向中间节点的指针
mid_point = slow;
while (slow)
{
size++;
slow = slow->next;
}
bool flag = true;
check(head, 1, mid_point, size, flag);
return flag;
}