当前位置:   article > 正文

LeetCode 之 回文链表_leetcode回文链表

leetcode回文链表

回文链表是指链表中的结点值正着读、反着读结果都是一样的链表。题目要求判断一个单链表是否为回文链表。
判断回文字符串使用双指针法相对容易,但是由于链表不能直接倒序遍历,所以需要另寻他法。

在这里插入图片描述

1. 递归法

算法的时间和空间复杂度都是 O(N)。
在这里插入图片描述

1.1 思路

由于链表不能直接倒序遍历,所以考虑生成一个原链表的反转链表。
之后遍历原链表与反转链表,对比两个链表是否相同。
相同即为回文链表,不同则不是回文链表。

1.2 代码实现

生成单链表的反转链表,可以参考反转链表
此处采取一直新的倒序遍历方法,不采用显式反转链表的方法。

下面来介绍一种神奇的链表遍历框架
在这里插入图片描述
举个简单例子来说明框架的使用。
如果想要正序打印链表的值,可以将 print 写在前序遍历代码的位置。
如果想要倒序打印链表的值,可以将 print 写在后序遍历代码的位置,如下图所示。

在这里插入图片描述
这种框架的本质是: “实际上就是把链表节点放入一个栈,然后再拿出来,这时候元素顺序就是反的,只不过我们利用的是递归函数的堆栈而已。”

下面开始动手实现上述框架。
遍历整个链表,left 为链表的左指针,初始值为head,right为链表的右指针,reverse 最先弹出的 right 是链表的最后一个结点。之后,left 不断向右移动,reverse 函数 return 是否为回文链表的布尔值。

class Solution {
public:
    ListNode* left = NULL;
    bool isPalindrome(ListNode* head) {
        left = head;
        return reverse(head);
    }
    bool reverse(ListNode* right)
    {
    	//基础条件
    	//单结点链表为回文链表
        if(right == NULL)
            return true;
        bool res = reverse(right->next);
        //后序遍历代码
        res = res && (left->val == right->val);
        left = left->next;
        return res;

    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2. 快慢指针法

算法的时间复杂度是 O(N),空间复杂度是 O(1) 。递归法重新生成了一个新的链表,占用了很大的空间。使用快慢指针法可以在原有链表的基础上做判断,降低空间复杂度

在这里插入图片描述

2.1 思路

回文链表中的结点值应该是关于链表中点对称的。因此可以考虑反转链表中点后的部分,并与链表中点前的部分做比较,如果相同则说明是回文链表,如果不同则说明不是回文链表。
那么如何确定链表的中点位置呢?

2.2 代码实现

使用快慢指针法找链表的中点位置。
快慢指针顾名思义有两个指针,一个为 fast 指针,另一个为 slow 指针。
快慢指针法是双指针法的一种,两个指针的速度不同。要想找到中点,可以令快指针速度是慢指针速度的二倍

slow = slow->next;
fast = fast->next->next;
  • 1
  • 2

这样当 fast 指针到达链表末尾时,slow 指针就到达了链表中点。

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast != nullptr && fast->next != nullptr)
        {
            slow = slow->next;
            fast = fast->next->next;

        }
        if(fast != nullptr)
            slow = slow->next;
        
        ListNode* left = head;
        ListNode* right = reverse(slow);
        while(right != nullptr)
        {
            if(left->val != right->val)
                return false;
            left = left->next;
            right = right->next;

        }
        return true;
    }

private:
    ListNode* reverse(ListNode* head)
    {
        ListNode* pre = nullptr;
        ListNode* cur = head;

        while(cur != nullptr)
        {
            ListNode* nxt = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nxt;
        }
        return pre;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

2.3 实现细节

链表中结点个数可以为奇数,也可以为偶数。
所以判断条件应写成 while(fast != nullptr && fast->next != nullptr)
在这里插入图片描述
确切的说,找到链表的中点不是目的。我们需要找到的是,链表后半部分的头结点。
因此当链表长度为奇数时,slow 需要后移一步,作为待反转链表部分的头结点。

if(fast != nullptr)
    slow = slow->next;
  • 1
  • 2

在这里插入图片描述

反转后链表如下图所示,left 和 right 分别为链表的最左结点和最右结点。
如果 while(right != nullptr) 就一直比较 left->val 和 right->val ,直至返回 true 或 false。
在这里插入图片描述
这里还有一个问题需要注意:
就是上述方法破坏了链表的原有结构,那么有什么办法可以解决这个问题呢?
在这里插入图片描述
记录 p 、q 两个位置,在函数 return 前,将 p 和 q 连接起来,就可以恢复链表的原有结构。

p.next = reverse(q);
  • 1

参考文献

  1. labuladong的算法小抄
  2. 一种更优的快慢指针法
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号