这个题目值得专门记一笔,其中有一个点(对我而言)实在是很精妙,颇有茅塞顿开之感。我觉得可以算是双指针这一块比较灵性的写法了。
其实判断回文数没什么难度,有各种各样的方法,但是把回文数放进链表里就不一样了。我感觉链表的一个特点就是局部性,每次只能知道下一个元素,不像数组一样能掌控全局,相对来说信息量比较少(所以为了更多的信息量,就有了双向链表);数组和链表一个全局一个局部,正好构成了数据结构的最基本的元素。
因为链表本身的特点,倒序遍历是不可行的,一开始不知道结尾在哪,就不能用头尾双指针来做。所以我一开始想到的办法是最简单的那种,顺序遍历的时候拿个数据结构把一路上遇到的东西存下来,然后再逆序,看看一不一样。在这个场景下,字符串就是一个挺好的容器:
bool isPalindrome(ListNode *head)
{
string store;
while (head)
{
store += head->val;
head = head->next;
}
string copy = store;
reverse(store.begin(), store.end());
return store == copy;
}
这个好是好,但是总觉得有点美中不足。因为还是之前说的那个问题,能不能利用这个结构本身的特点,而不是一味去线性地思考问题。一开始想着能不能构造一个环,这也是链表中比较常见的特殊结构,但是在这里好像不太行。如果反转链表吧,那还不如直接拿个容器来存呢。后来看了看题解,看到一个我觉得很精妙的写法。当然了,这是“意译”过来的(看完题解之后自己写的):
bool isPalindrome(ListNode *head)
{
ListNode *slow = head;
ListNode *slow_next = nullptr;
ListNode *fast = head;
while (fast && fast->next)
{
fast = fast->next->next;
ListNode *temp = slow->next;
slow->next = slow_next;
slow_next = slow;
slow = temp;
}
// 这个很重要,通过fast是否结束来判断长度是奇数还是偶数
if (fast)
{
slow = slow->next;
}
while (slow && slow_next)
{
if (slow_next->val != slow->val)
{
return false;
}
slow_next = slow_next->next;
slow = slow->next;
}
return true;
}
思路很简单,通过快慢指针,找到整个链表的中点。但是这里有两个特殊的地方,第一个是一边遍历一边反转,这样找到中点之后直接next就行;第二个是对奇偶数的处理,因为对于回文来说,长度为奇数和长度为偶数的情况是不一样的。所以这里的处理就很巧妙,根据快指针是否指向nullptr
来判断奇偶:
if (fast)
{
slow = slow->next;
}
其他的一些简单题没什么写头,先这样吧。