算法通关之路
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 回文链表

链表相对于字符串来说难度会增加一点,但其实二者都属于线性数据结构,因此难度也只表现在写法上,在思路上并没有增加太大的难度。

题目描述(第234题)

请判断一个链表是否为回文链表。

示例1

输入:1→2

输出:False

示例2

输入:1→2→2→1

输出:True

进阶

你能否用O(n)时间复杂度和O(1)空间复杂度解决此题?

思路

这是一道Amazon的面试题,难度级别为简单,是一道热身题。链表不同于数组(字符串本质上是字符数组),不支持随机访问。对于单链表来说,只有一个指向下一个节点的指针。如果不考虑复杂度,可以将链表进行一次遍历,遍历的同时将值放到一个数组中,然后可以采用字符串的思路去解决。这种算法需要额外的数组存储链表的值,因此这种算法的空间复杂度是O(n)。空间上可以进一步优化。下面介绍一种空间复杂度为O(1)的解法。

首先使用快/慢指针的技巧找到中点,找到中点的同时将前半部分链表进行反转。然后将慢指针和慢指针的前一个指针同时移动,如果两者指向的数字有一个不同则说明不是回文,否则则是回文。这里有两个要点。

● 如何找到中点?我们只需要建立快/慢指针,快指针fast每次走两步,慢指针slow每次走一步。这样当快指针走到终点时,慢指针刚好走到中间位置。

● 如何进行反转?其实链表反转是一个单独且常见的考点,力扣(LeetCode)上也有原题(第206题反转链表,见参考链接/正文[8])。我们使用一个变量记录前驱pre,一个变量记录后继next,不断更新current.next=pre就好了。

反转链表的代码如下所示。

找到中点时链表情况如下图所示。

之后pre和slow分别向前、向后遍历即可。当然这里的“向前”本质上还是借助于next指针移动,只不过我们已经对前半部分进行了反转,因此可以通过next找到前驱节点。

代码

复杂度分析

● 时间复杂度:算法由两部分组成。第1部分是找到中点,这部分的时间复杂度为O(n);第2部分是从中点分别向左和向右移动,这部分的时间复杂度同样是O(n),因此总的时间复杂度为O(n),其中n为节点数。

● 空间复杂度:O(1)。