一.题目描述
请编写一个函数,检查链表是否为回文。给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文。
链表的结点定义如下:
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
二.题目分析&实现
刚开始看到这道题目我就想如果可以将整个链表颠倒过来,此时再将原链表与反转之后的结构进行比较,如果每一个结点都一样则是回文否则就不是;其实可以借助栈这种辅助结构的特性(先进后出)来实现的,先遍历一遍链表将不为空的结点全部入栈,比较的时候每次和栈顶的元素进行比较,此时就达到了我们的目的了~~~
实现代码如下:
//方法一.先反转该链表再比较
class Palindrome {
public:
ListNode *ReverseList(ListNode * pHead) //反转单链表
{
if(pHead == NULL || pHead->next == NULL) //空链表或者只有一个结点
return pHead;
ListNode *newHead=NULL;
ListNode *cur=pHead;
while(cur != NULL)
{
ListNode *newCur=cur;
cur=cur->next;
newCur->next=newHead;
newHead=newCur; //更新头
}
return newHead;
}
bool isPalindrome(ListNode* pHead) {
// write code here
stack<ListNode *> s;
ListNode *cur=pHead;
while(cur) //先利用栈的特性反转该链表
{
s.push(cur);
cur=cur->next;
}
cur=pHead;
while(cur) //链表的每个结点都和栈中的数据进行比较
{
ListNode *top=s.top();
s.pop();
if(top->val != cur->val)
return false;
cur=cur->next;
}
return true;
}
};
不管是反转单链表还是利用栈的特性,上面方法的效率都太Low了,那仫有没有什仫高效一点的办法来解决呢?那就是利用快慢指针的思想,下面是我理解的一张流程图:
由上图可知:首先使得快指针和慢指针都指向链表的头结点,快指针一次走两步,慢指针一次走一步,每次都将慢指针所指向的结点入栈,当快指针为空或者快指针的next域为空的时候结束。此时栈中所存储的就是整个单链表的前半部分的结点了;
因为链表的结点个数可能是奇数个也可能是偶数个,所以需要判断此时fast是否为空:如果fast为空则说明该链表的结点个数是偶数,否则该链表的结点个数为奇数个。因为是回文数所以不需要考虑中间结点,如果链表是奇数个结点则slow跳过中间结点;
此时slow指向的是单链表的后半部分的第一个结点,辅助栈中存储的是单链表的前半部分结点。此时slow遍历后半部分链表与栈中的前半部分结点进行比较,如果全部相同则是回文链表,只要有一个不相等则不满足题意;
代码实现如下:
class Palindrome {
public:
//利用快慢指针法
bool isPalindrome(ListNode* pHead) {
// write code here
ListNode *fast=pHead;
ListNode *slow=pHead;
stack<ListNode *> s; //辅助栈
while(fast != NULL && fast->next != NULL) //将链表的前半部分存入栈中
{
s.push(slow);
fast=fast->next->next;
slow=slow->next;
}
if(fast != NULL){ //是奇数个结点,slow跳过中间节点
slow=slow->next;
}
//slow遍历后半部分链表与栈中的前半部分结点进行比较
while(slow != NULL)
{
int top=s.top()->val;
s.pop();
if(top != slow->val)
return false;
slow=slow->next;
}
return true;
}
};
这种快慢指针的想法虽然也使用了辅助栈,但是在栈中只存储了单链表的前半部分结点,明显比第一种想法将整个单链表全部存入栈中优化一些。
在这里就分享结束了~~~