原题链接🔗:回文链表
难度:简单⭐️
题目
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
提示:
链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
题解
解决"回文链表"问题,我们可以采用以下几种解题思路:
1. 双指针法(快慢指针)
- 目的:找到链表的中点。
- 方法:使用两个指针,一个快指针(每次移动两个节点),一个慢指针(每次移动一个节点)。当快指针到达链表末尾时,慢指针即为中点。
2. 反转链表的后半部分
- 目的:将链表的后半部分反转,以便可以直接比较。
- 方法:从找到的中点开始,反转链表的后半部分。
3. 比较前半部分和反转后的后半部分
- 目的:判断链表是否是回文。
- 方法:使用两个指针,一个从链表头部开始,另一个从反转后的后半部分开始,逐个比较节点的值。
4. 恢复链表
- 目的:在判断完是否是回文后,恢复链表的原始结构。
- 方法:将反转的后半部分再次反转,以恢复链表的原始顺序。
5. 递归方法
- 目的:递归地比较链表的首尾元素,然后对剩余部分递归进行判断。
- 方法:定义一个递归函数,比较当前头节点和尾节点的值,如果相等,对子链表进行递归调用。
详细步骤
- 初始化两个指针:
slow
和fast
,都指向头节点。- 移动指针:
fast
每次移动两个节点,slow
每次移动一个节点,直到fast
到达链表末尾或其下一个节点。- 反转链表的后半部分:从
slow
节点开始,反转链表的后半部分。- 比较链表的前半部分和反转后的后半部分:使用两个指针,一个从链表头部开始,另一个从反转后的后半部分开始,逐个比较节点的值。
- 判断是否是回文:如果所有值都匹配,则链表是回文的;如果有不匹配的值,则链表不是回文的。
- 恢复链表:如果需要,将反转的后半部分再次反转,以恢复链表的原始顺序。
注意事项
- 在反转链表时,需要小心处理边界情况,如链表为空或只有一个节点。
- 在比较过程中,确保比较的是相同位置的节点。
- 如果使用递归方法,注意递归的终止条件和防止栈溢出。
通过上述步骤,你可以清晰地理解如何判断一个链表是否是回文,并选择适合的方法来实现算法。
快慢指针法
- 复杂度:时间复杂度O(n),空间复杂度O(1)。
- 过程:
- ListNode结构体:定义了链表的节点,包含整数值val和指向下一个节点的指针next。
- Solution类:包含isPalindrome方法,用于判断链表是否是回文。
- 快慢指针:在isPalindrome方法中,使用快慢指针找到链表的中点。
- 反转链表:使用reverse辅助函数反转链表的后半部分。
- 比较:使用两个指针比较链表的前半部分和反转后的后半部分。
- 恢复链表:在比较完成后,再次调用reverse函数恢复链表的后半部分,以保持原始链表结构。
- main函数:创建示例链表,调用isPalindrome方法,并输出结果。然后释放链表占用的内存。
- c++ demo:
#include <iostream>
// 定义链表节点
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
// 判断链表是否是回文
bool isPalindrome(ListNode* head) {
if (!head || !head->next) return true; // 空链表或只有一个节点是回文
// 使用快慢指针找到链表中点
ListNode* slow = head, * fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
// 反转链表的后半部分
ListNode* secondHalf = reverse(slow);
// 判断前半部分和反转后的后半部分是否相等
ListNode* p1 = head, * p2 = secondHalf;
bool isPalin = true;
while (p2) {
if (p1->val != p2->val) {
isPalin = false;
break;
}
p1 = p1->next;
p2 = p2->next;
}
// 恢复链表的后半部分
reverse(secondHalf);
return isPalin;
}
private:
// 反转链表的辅助函数
ListNode* reverse(ListNode* head) {
ListNode* prev = nullptr, * curr = head, * next = nullptr;
while (curr) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
// 主函数,用于演示
int main() {
Solution solution;
// 创建一个示例链表: 1 -> 2 -> 2 -> 1
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(2);
head->next->next->next = new ListNode(1);
// 判断链表是否是回文
bool isPalin = solution.isPalindrome(head);
std::cout << (isPalin ? "链表是回文" : "链表不是回文") << std::endl;
// 释放链表内存
ListNode* current = head;
while (current) {
ListNode* next = current->next;
delete current;
current = next;
}
return 0;
}
- 输出结果:
链表是回文