日常: 算法、数据结构、分布式系统、日常思考感悟等

[LeetCode] Palindrome Linked List

2021.04.01

问题描述

Given the head of a singly linked list, return true if it is a palindrome.

Example 1:

Input: head = [1,2,2,1]

Output: true

Example 2:

Input: head = [1,2]

Output: false

Constraints:

  • The number of nodes in the list is in the range [1, 105]. 0 <= Node.val <= 9

Follow up: Could you do it in O(n) time and O(1) space?

问题分析

问题需要我们在O(N)的时间和O(1)的空间复杂度内解决。所以基本前提是单链表只遍历一次。

如果不是回文单链表,而是判断回文字符串,那么常见做法是定义2个指针,分别指向一头一尾,向中间移动,判断扫过的字符是否相等即可。那么对于单链表,思想类似,只不过手段上要变换一下。

单链表只能由一个方向进行遍历,因此双指针方式不可行,那么我们就考虑先找到单链表的中间位置,然后从中间位置分别向2边遍历,判断对应元素是否相等。所以找到中间位置后,我们需要对其中一半的单链表进行翻转,然后再遍历。

综上,我们采取如下方式进行:

  1. 利用快慢指针找到单链表的中间位置
  2. 在步骤1的过程中,对慢指针扫过的部分进行单链表翻转
  3. 在找到中间位置后,左半部分已经翻转完毕,此时左右各自向两边顺序遍历,判断对应元素是否一致即可。

示例代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
// O(N)时间 O(1)空间
// 1. 利用快慢指针找到单链表的中间位置
// 2. 在1过程中,对慢指针扫过的部分进行单链表翻转
// 3. 找到中间位置后,从中间各自向两边遍历,判断是否一致
func isPalindrome(head *ListNode) bool {
	ans := true
	if head == nil || head.Next == nil {
		return ans
	}
    // 如果仅有2个元素,直接判断即可
	if head.Next.Next == nil {
		return head.Val == head.Next.Val
	}
    // 定义快慢指针slow, fast; 以及用来进行单链表翻转的辅助指针prev, next
	slow, fast, prev, next := head, head, head, head.Next
	for {
		if fast.Next == nil || fast.Next.Next == nil {
			break
		}
		fast = fast.Next.Next

        // 如果prev指针为head,将prev.Next置为nil,防止后面遍历时出现环导致空指针引用
		if prev == head {
			prev.Next = nil
		}
        // 快慢指针移动,同时进行单链表翻转
		prev = slow
		slow = next
		next = next.Next
		slow.Next = prev
	}
	// 如果fast.Next为nil,说明单链表元素个数为奇数;此时slow位于中间元素,需要往前挪一个位置再与next指向的右半部分进行比较
	if fast.Next == nil {
		slow = slow.Next
	}
	for {
		if slow == nil {
			break
		}
		if slow.Val != next.Val {
			return false
		}
		slow = slow.Next
		next = next.Next
	}
	return true
}

原题链接

Palindrome Linked List

发表评论