回文链表
回文结构即字符串正序逆序完全一致,如“1 2 3 4 3 2 1”,那么我们就要想办法同时比较链表头和链表尾的元素,看其是否相等。
下面介绍一种最常用的方法:
思路
如果我们仔细观察回文结构,就会得到一个结论:
将一个回文结构从正中间分隔,再将后半部分逆序,那么前半部分就一定等于后半部分。
我们可以分链表长度为奇数和偶数讨论:
当长度为偶数:
当长度为奇数:
- 那么**,第一步就先要得到链表的中间节点。我们可以用快慢指针**来实现:
定义两个指针fast、slow
,同时指向链表头,slow
每次走一个,fast
每次走两个节点,当fast->next == NULL 或者 fast == NULL
时,slow
就走到中间节点了
struct ListNode* slow = head;
struct ListNode* fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
- 第二步,就是反转以
slow
节点为头的链表,如果对单链表反转还不太了解的朋友,建议先看看👉反转单链表
struct ListNode* reverseList(struct ListNode* head)
{
if (head == NULL)
return NULL;
struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
newHead->next = head;
struct ListNode* cur = head;
while (cur->next)
{
struct ListNode* curNext = cur->next;
cur->next = curNext->next;
curNext->next = newHead->next;
newHead->next = curNext;
}
struct ListNode* retHead = newHead->next;
free(newHead);
return retHead;
}
- 第三步,我们用指针
mid
来接受slow
后链表反转过后的头,接下来,从原来链表的头和mid
开始比较,只要遇到不相等的情况,就返回false
,否则返回true
。
struct ListNode* mid = reverseList(slow->next);
struct ListNode* cur1 = head;
struct ListNode* cur2 = mid;
while (cur1 && cur2)
{
if (cur1->val != cur2->val)
{
return false;
}
cur1 = cur1->next;
cur2 = cur2->next;
}
return true;
- 最后一步,**在返回之前,需要将链表还原,**毕竟在现实生活中,没有人想再调用这个函数时改变原来的链表结构。但是,如果我们就按上面的操作进行还原,那么就会出现一系列的问题,我们拿下图说明
判断过后,链表的结构是这样的:
如果我们要将链表还原,那么问号处的节点的后面应该链接到reverseList(mid)
的返回值,但问题是之前我们并没有保存问号处的节点。所以,我们可以对找到链表中间节点这一操作进行改进:
struct ListNode* slow = head;
struct ListNode* fast = head;
while (fast->next && fast->next->next)
{
slow = slow->next;
fast = fast->next->next;
}
这样slow
就会停留在问号处的位置,反转链表的时候就反转以slow->next
为头的链表就行了
实现代码
struct ListNode* reverseList(struct ListNode* head) //反转链表
{
if (head == NULL)
return NULL;
struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
newHead->next = head;
struct ListNode* cur = head;
while (cur->next)
{
struct ListNode* curNext = cur->next;
cur->next = curNext->next;
curNext->next = newHead->next;
newHead->next = curNext;
}
struct ListNode* retHead = newHead->next;
free(newHead);
return retHead;
}
bool isPalindrome(struct ListNode* head){
struct ListNode* slow = head;
struct ListNode* fast = head;
while (fast->next && fast->next->next) //找到中间节点的前一个节点
{
slow = slow->next;
fast = fast->next->next;
}
//反转以中间节点为头的链表
//将返回值赋给mid
struct ListNode* mid = reverseList(slow->next);
struct ListNode* cur1 = head;
struct ListNode* cur2 = mid;
bool ret = true; //设置返回值
while (cur1 && cur2)
{
if (cur1->val != cur2->val)
{
ret = false; //只要出现不相等的情况,就将返回值设为false,推出比较
break;
}
cur1 = cur1->next;
cur2 = cur2->next;
}
//还原链表
mid = reverseList(mid);
slow->next = mid;
//返回
return ret;
}