给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
方法一:使用数组
Ⅰ.遍历链表,将链表的节点依次放入到vector数组中
Ⅱ.这样就将判断链表是否回文的问题,转换成判断数组中的元素是否回文的问题
Ⅲ.利用双指针,left指向第一个元素的下标,right指向最后一个元素的下标,依次判断left、right下标处的元素是否相等,如果不相等返回false
class Solution {
public:
bool isPalindrome(ListNode* head) {
//先将链表各个结点的值放入vector数组中
vector<int> table;
while(head!=nullptr){
table.push_back(head->val);
head=head->next;//移动head的位置,依次让head等于它的下一个元素
}
int n=table.size();
//分别令left、right指向table数组中第一个元素的下标和最后一个元素的下标,
int left=0;
int right=n-1;
//当left<right的时候执行循环体,此处不写为left<=right是因为没有必要相等,因为相等说明left和right指向同一位置,而同一位置的元素必相等。
while(left<right){
//如果left和right所指向的元素不相等,返回false
if(table[left]!=table[right]){
return false;
}
//否则,移动left和right的位置
++left;
--right;
}
return true;
}
};
时间复杂度:O(n),其中 n指的是链表的元素个数。
第一步: 遍历链表并将值复制到数组中,O(n)。
第二步:双指针判断是否为回文,执行了 O(n/2) 次的判断,即 O(n)。
总的时间复杂度:O(2n) = O(n)。
空间复杂度:O(n),其中 n 指的是链表的元素个数,我们使用了一个数组列表存放链表的元素值。
快慢指针
class Solution {
public:
//使用快慢指针的方法,假如结点的个数为奇数:例如1,2,3,2,1的情况,那么slow就移动到第2个2的位置
//如果节点个数为偶数的情况,例如:1,2,2,1,那么slow就移动到第2个2的位置
//此题最终判断的的是以pre为头节点和以slow为头节点的链表是否相等,所以要保证pre和slow长度上相等
//并且为了能够构建出以pre为头结点的链表,要用prepre记住pre的上一个节点
bool isPalindrome(ListNode* head) {
//首先判断头节点,以及头节点的下一个节点是否为空,如果二者有一个为空,说明该链表为回文链表
if(head==nullptr||head->next==nullptr){
return true;
}
//定义需要用到的变量,prepre为空,是因为最后生成的pre链表的结尾一定指向nullptr
ListNode* slow=head;
ListNode* fast=head;
ListNode* pre=head;
ListNode* prepre=nullptr;
//如果fast不为空,并且fast的next不为空,才会执行循环体
//如果不满足条件说明已经可以确定中间节点了
//例如1,2,3,2,1的情况,循环退出时fast在最后一个1的位置,slow在3的位置
//例如1,2,2,1的情况,循环退出时fast在nullptr的位置,slow在倒数第2个2的位置
while(fast!=nullptr&&fast->next!=nullptr){
pre=slow; //更新pre位置,为了让pre能接住完成获得半个链表的使命
slow=slow->next; //慢指针走一步
fast=fast->next->next; //快指针走两步
pre->next=prepre; //让pre的next指向它的前一个节点
prepre=pre; //让它等于当前pre的位置,为了让下次的pre能够找到它的上一个节点
}
//这里if语句,是因为链表有奇数节点的情况存在
//例如1,2,3,2,1的情况,while循环退出时fast在最后一个1的位置,slow在3的位置,为了让pre为头节点的链表可以与slow为节点的链表长度相等,方便判断,所以,奇数结点的时候,要将sloe向后移动一个位置
if(fast!=nullptr){
slow=slow->next;
}
//例如1,2,3,2,1的情况
//以pre为头结点的链表为:2->1->nullptr,以slow为头结点的链表为:2->1->nullptr
//例如1,2,2,1的情况
//以pre为头结点的链表为:2->1->nullptr,以slow为头结点的链表为:2->1->nullptr
while(pre!=nullptr&&slow!=nullptr){
if(pre->val!=slow->val){
return false;
}
pre=pre->next;
slow=slow->next;
}
return true;
}
};
时间复杂度:O(n),其中 nn 指的是链表的大小。
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。