一道把递归、链表、引用、双指针都结合的题——回文链表

在这里插入图片描述题目描述:给出一个链表,然你判断它是不是回文链表,是则返回true,不是则返回false;
比如1 2 3 5 3 2 11 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;
}

猜你喜欢

转载自blog.csdn.net/wyll19980812/article/details/109095237
今日推荐