力扣234. 回文链表 一题五解让你彻底弄懂链表!反转链表法栈法双指针法快慢指针法递归法
Posted weixin_43739821
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣234. 回文链表 一题五解让你彻底弄懂链表!反转链表法栈法双指针法快慢指针法递归法相关的知识,希望对你有一定的参考价值。
回文链表
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
1 <= ListNode.size <= 100000
解法一:反转链表法
根据回文的定义,若一个数字从后往前读跟从前往后读是一样的,那么即为回文数,所以我们可以将这个链表反转,然后遍历俩个链表看看是否相同即可,这里反转链表函数为本人自己写出的,感兴趣的朋友可以自己尝试去看懂并思考其他解法,也是弄懂链表的一个很好的入门级题目,同样也是大厂面试易问的题目。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
ListNode* reverseList(ListNode* head) {//反转链表函数
if (!head) return nullptr;
ListNode* a = new ListNode(head->val);
head = head->next;
while (head) {
ListNode* b = new ListNode(head->val);
b->next = a;
a = b;
head = head->next;
}
return a;
}
bool isPalindrome(ListNode* head) {//判断是否为回文链表
ListNode* a = head;
a = reverseList(a);
while (head) {
if (head->val != a->val) return false;
head = head->next;
a = a->next;
}
return true;
}
解法二:栈法
首先将链表的所有值压栈,再从链表表头开始不断的与栈中元素对比,同样达到了从前后对比链表值的目的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
bool isPalindrome(ListNode* head) {
stack<int> x;
ListNode* a = head;
while (a) {
x.push(a->val);
a = a->next;
}
while (head) {
int p = x.top();
if (p != head->val) return false;
x.pop();
head = head->next;
}
return true;
}
解法三:双指针法
因为链表无法直接获取中间的值,只能一个个遍历,所以这种思路是我们先将链表中的数值全部存放到列表或者数组中去(列表与数组使用起来一样),然后在使用双指针法判断是否为回文。
双指针法具体实现:
在起点和结尾各放置一个指针,每一次循环判断两个指针指向的元素是否相同,若不同则退出循环返回false,相同则不断地将两个指针向内移动直到两个指针相遇为止。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
bool isPalindrome(ListNode* head) {
vector<int> x;//容器,使用数组也可
while (head) {
x.push_back(head->val);
head = head->next;
}
for (int i = 0, j = (int)x.size() - 1; i < j; i++, j--)
if (x[i] != x[j]) return false;
return true;
}
解法四快慢指针法:
这种方法是五种方法中唯一一个达到进阶要求O(1) 空间复杂度的。
首先我们应当找到前半部分链表的尾节点,随后反转后半部分链表,形成俩个链表,本质上也是从后往前遍历。再遍历俩个链表是否回文,如果有需要可以恢复链表,当然不恢复也能过题,不过作为一个优秀的程序员应当保持良好的习惯,一般使用函数的人总是不希望使用函数后链表的结构发生了变化,这里同样需要使用到第一种解法中给出的反转链表函数。
那么如何找到前半部分链表的尾节点?第一种找法是直接遍历一遍链表,然后得到链表的数目,从而得到中间结点的位置,那么聪明一点的做法就是快慢指针法,在表头设置俩个指针,快指针一次移动俩个数位,慢指针一次移动一个数位,当快指针移动到表尾,则慢指针恰好位于链表的中间。
bool isPalindrome(ListNode* head) {
// 找尾节点
ListNode* firstHalfEnd = endOfFirstHalf(head);
//反转后半部分的链表
ListNode* secondHalfStart = reverseList(firstHalfEnd->next);
ListNode* p1 = head;
ListNode* p2 = secondHalfStart;
bool result = true;
while (result && p2 != nullptr) {//判断回文
if (p1->val != p2->val) {
result = false;
}
p1 = p1->next;
p2 = p2->next;
}
//还原链表,若为了更快的提交速度可以不写
firstHalfEnd->next = reverseList(secondHalfStart);
return result;
}
ListNode* reverseList(ListNode* head) {//反转链表函数
if (!head) return nullptr;
ListNode* a = new ListNode(head->val);
head = head->next;
while (head) {
ListNode* b = new ListNode(head->val);
b->next = a;
a = b;
head = head->next;
}
return a;
ListNode* endOfFirstHalf(ListNode* head) {//寻找尾结点
ListNode* fast = head;
ListNode* slow = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
解法五:递归法
bool recursivelyCheck(ListNode* currentNode) {
if (currentNode != nullptr) {
if (!recursivelyCheck(currentNode->next)) return false;
if (currentNode->val != frontPointer->val) return false;
frontPointer = frontPointer->next;
}
return true;
}
bool isPalindrome(ListNode* head) {
frontPointer = head;
return recursivelyCheck(head);
}
以上是关于力扣234. 回文链表 一题五解让你彻底弄懂链表!反转链表法栈法双指针法快慢指针法递归法的主要内容,如果未能解决你的问题,请参考以下文章