题目描述
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:

1 2
| 输入:head = [1,2,2,1] 输出:true
|
示例 2:

1 2
| 输入:head = [1,2] 输出:false
|
提示:
- 链表中节点数目在范围
[1, 105] 内 0 <= Node.val <= 9
基本思路
法一:复制到数组
小生不才,链表使用不够熟练,先用复制链表到数组的笨方法做出来一遍。
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
|
bool isPalindrome(struct ListNode* head) { struct ListNode* cur = head; int count = 0;
while(cur) { count++; cur = cur->next; }
int* a = (int*)malloc(count * sizeof(int)); if (!a) { return false; }
cur = head; for(int i = 0; i < count; i++) { a[i] = cur->val; cur = cur->next; }
for(int i = 0; i < count / 2; i++) { if(a[i] != a[count - 1 - i]) { free(a); return false; } }
free(a); return true; }
|
法二:快慢指针
这也是我想到的第二个方法。
整个流程可以分为以下五个步骤:
找到前半部分链表的尾节点。
反转后半部分链表。
判断是否回文。
恢复链表。
返回结果。
执行步骤一,我们可以计算链表节点的数量,然后遍历链表找到前半部分的尾节点。
我们也可以使用快慢指针在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。
若链表有奇数个节点,则中间的节点应该看作是前半部分。
步骤二可以使用反转链表](http://blog.zuquanzhi.top/2023/11/30/Leetcode0206/#more))问题中的解决方法来反转链表的后半部分。
步骤三比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点。
步骤四与步骤二使用的函数相同,再反转一次恢复链表本身。
其代码如下:
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 44 45 46 47 48
| struct ListNode* reverseList(struct ListNode* head) { struct ListNode* prev = NULL; struct ListNode* curr = head; while (curr != NULL) { struct ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } return prev; }
struct ListNode* endOfFirstHalf(struct ListNode* head) { struct ListNode* fast = head; struct ListNode* slow = head; while (fast->next != NULL && fast->next->next != NULL) { fast = fast->next->next; slow = slow->next; } return slow; }
bool isPalindrome(struct ListNode* head) { if (head == NULL) { return true; }
struct ListNode* firstHalfEnd = endOfFirstHalf(head); struct ListNode* secondHalfStart = reverseList(firstHalfEnd->next);
struct ListNode* p1 = head; struct ListNode* p2 = secondHalfStart; bool result = true; while (result && p2 != NULL) { if (p1->val != p2->val) { result = false; } p1 = p1->next; p2 = p2->next; }
firstHalfEnd->next = reverseList(secondHalfStart); return result; }
|
法三:递归
这个递归来源于Leetcode官方题解,其风骚是我至今所遇最强。
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
|
struct ListNode* frontPointer;
bool recursivelyCheck(struct ListNode* currentNode) { if (currentNode != NULL) { if (!recursivelyCheck(currentNode->next)) { return false; } if (currentNode->val != frontPointer->val) { return false; }
frontPointer = frontPointer->next; }
return true; }
bool isPalindrome(struct ListNode* head) { frontPointer = head;
return recursivelyCheck(head); }
|
isPalindrome 函数设置 frontPointer 为链表头,并调用 recursivelyCheck 函数。recursivelyCheck 函数首先检查当前节点是否为 NULL,如果是,则返回 true,因为链表的末尾已经达到。- 然后,
recursivelyCheck 递归调用自己,传递当前节点的下一个节点。 - 在递归返回之前,检查当前节点的值是否等于
frontPointer 指向的节点的值。如果不等,则返回 false,因为链表不是回文的。 - 如果值相等,将
frontPointer 移动到下一个节点。 - 最终,如果整个链表都被成功检查,并且没有发现值不相等的情况,那么整个函数返回
true,表示链表是回文的。
这种方法的核心思想是使用递归从链表的末尾开始比较节点的值,同时使用 frontPointer 从链表的头部开始。这两个指针相向移动,逐一比较节点的值,如果在整个过程中没有找到不相等的节点,则链表被认为是回文的。