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


    在这里插入图片描述题目描述:给出一个链表,然你判断它是不是回文链表,是则返回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;
    }
    
  • 相关阅读:
    C#编码标准--编码习惯
    课程九,课堂测试
    JAVAweb 分级测试
    第八周 课堂报告
    javaweb界面
    12月9日,第一次自查报告
    课程管理系统后台JAVA代码
    《程序员修炼之道+从小工到专家》读后有感
    12月9日 自查后续
    课程管理系统JAVAweb前端代码
  • 原文地址:https://www.cnblogs.com/Luweir/p/14147281.html
Copyright © 2020-2023  润新知