Leetcode234.回文链表
题目地址
题目详情
请判断一个链表是否为回文链表。
示例
输入: 1->2->2->1
输出: true
说明
用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题
思路
可以借鉴判断数组是否是回文数的思想:双指针。扩展到链表中,则为快慢指针(fast-slow),即慢指针每次前进一个,快指针每次前进两个,初始化都在head。
主要思想:当fast指针下次移动到nil处(或遍历完链表)时,根据链表长度的奇偶性,可分为以下两种情况。
- 长度为奇数时:fast指针到达最后一个节点时,slow指针刚好到达中间节点,slow指针再走一步即可到达后半部分(根据回文数的性质,分为前半部分和后半部分)的第一个节点。
- 长度为偶数时:fast移动到nil处时,slow指针到达后半部分的第一个节点。
由上面两种情况分析可知:slow节点可以到达后半部分的第一个节点。这是如果我们可以知道前半部分的最后一个节点,那么就可以通过向后遍历slow指针和向前遍历指向前半部分的pre指针,通过比对二者的值,就可以判断该链表是否是回文数。
此时,问题就简化为找到指向前半部分最后一个节点的指针pre,并且可以向前遍历。解决这个问题的思路也很容易想到,那就是创建pre指针指向一个辅助的空节点,随着slow指针的遍历,翻转链表,直到fast指针指向最后一个节点或者指向nil。
实现代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func isPalindrome(head *ListNode) bool {
if head == nil || head.Next == nil {
return true
}
// 创建快慢指针和辅助指针
slow, fast, pre := head, head, &ListNode{}
pre = nil
// 遍历链表,直到fast指针到最后一个节点或者指向nil
for fast != nil && fast.Next != nil {
fast = fast.Next.Next
// 翻转链表
slow, slow.Next, pre = slow.Next, pre, slow
}
// 如果fast指针指向最后一个节点,说明链表长度是奇数,需要进一步移动slow指针
// 到后半部分的第一个节点,否则不用
if fast != nil {
slow = slow.Next
}
// 分别向前和向后移动pre和slow指针,比较二者的值,判断是否是回文链表
for pre != nil {
if pre.Val != slow.Val {
return false
}
pre, slow = pre.Next, slow.Next
}
return true
}
复杂度分析
时间复杂度:O(n),其中 n 为链表长度。
空间复杂度:O(1)。只需要常数的空间存放若干变量。
参考
Leetcode题解