回文结构
如图回文结构指在一个链表中数据是对称的关系,第一个与最后一个相同,第二个与倒数第二相同。我们这里使用C语言判断给定单链表是否为回文结构。
判断思路
对比:判断是否为回文结构我们我需要将前半段的数据与后半段的数据进行逐一对比,若在对称的结构中数据并不相同则不是回文结构。若两两相比一直到中间最后一个则为会问结构。
如何将链表中的第一个与最后一个相对比,最简单的思路就是先遍历链表找到最后一个数据记录下来与第一个相对比,再找到倒数第二个数与第二个数据相对比循环到中间位置,此方法思路是最简介明了的,但是实际实现非常复杂所以我们不考虑此方法。
方法二:我们找到中间结点,然后重新创建一个链表,从链表中的第一个数据开始将数据依次头插到新链表中,到中间结点停止,头插操作就等于是对原链表的前半段进行了逆序。
这里我们便需要判断链表的数组个数是单还是双
若是双数的数据个数我们可以得到如上图所示的情况,这样我们无需其他操作,从第一个数据开始对比即可,若是单数,我们有一个链表是需要跳过第一个数据的。这里为什么第一图我们的逆序链表中只有两个数呢,因为这里计算机的计算方式是舍弃余数的当链表中有5个数据我们得到的除二的数为2,当链表数据个数为6是得到3,当我们跳过两个数据时如图单数图是到3的位置,而跳过3个数据时如双数图,是到第二个3的位置若我们将当前位置也加入逆序中双数的时候逆序的链表便有4个数的两边数量不对等所以我们只逆序到当前的结点的前一个结点数据。当然你也可以再得到值后做适当调整或者将单数链表和双数链表分开实现,只是没什么必要。这里逆序完成后只需要做对比就行了,这里就不进行此方法的代码实现了。
方法三:
这里我们并不进行新链表的创建而是再原链表的基础上我们实现判断回文结构,但是我们能从前半结点中由1到2但是并不能从后半的结点中由1到2,因为时单链表无法往回走,所以我们需要对链表进行修改,让后半段也可以由1到2。
如图我们只要将表链结构改成这个样子便能完成后半的由1到2移动了,这里我们便介绍一下方法三的实现方式。
实现判断回文结构
为了判断链表是否为回文结构我们需要对链表结构进行一定的修改。
如图对前半段我们不做任何的修改,支队后半段进行修改,这里我们需要先找出链表中的中间结点
这里我们定义一个指针back和一个计数器count,然后进入循环没经过一个链表结点计数器加一,当走到最后的时候跳出循环拿到链表结点数,对其进行除二,这样我们便知道了需要跳过多少个结点才能到链表的中间位置。
我们将back指针重新置为第一个结点的位置开始移动,当跳过计数器个结点时back指针就会到达中间结点的位置。
找到中间指针之后我们还需要再定义两个指针,因为再进行后半段的逆序操作时对结点指针的修改会导致我们无法找到下一个结点,这里需要先将下一个结点地址记录下来。初始我们将tmp指针指向中间结点的下一位,将cur指针指向再下一位共记录前后连续的3个结点。然后我们直接将中间结点的指针指向空以方便我们后续的对比,因为再修改了结构之后这个链表相当于时从两个头结点出发再最后一个结点相遇,这个中间结点便是我们判断最后一个结点的标识符。
在置NULL后我们开始逆序操作,将记录下一个结点(tmp记录)的指针指向中间结点(back),将下一个指针(tmp)的值赋给back指针即将back指针指向原本的下一个结点,因为下一个结点(tmp)的指针的已经修改了值无法通过此结点找到下一个结点所以我们需要第三个指针cur将其的值赋给tmp,cur再去到下一个结点,这样完成一轮循环即一个结点的逆序。
为了防止野指针的出现我们将循环条件是cur不能为NULL,所以当我们出循环的时候最后一个结点时没有完成逆序的
如图因为已经出了循环并没有进行逆序操作,这里我们需要单独给最后一个结点进行逆序操作,需要将tmp结点的指针指向back指向的结点,再将tmp的值赋给back就时后半段的头指针了。
最后我们进行一一对比
这里若是链表数据个数单双不一会影响前后指针为NULL的顺序,所以两个都需要判断,不然就可能出现野指针,我们再循环中逐一对比若有不同直接返回false表示该链表不是回文结构,正常整个程序走完证明都是一一对应的便返回true。
判断回文结构完整C语言代码
typedef struct ListNode//链表回文结构
{
int val;
struct ListNode* next
}ListNode;
bool chkPalindrome(ListNode* A) {
// write code here
ListNode* head = A;
ListNode* back = A;
ListNode* cur = NULL;
ListNode* tmp = NULL;
int count = 0;
while (back)
{
count++;
back = back->next;
}
count /= 2;
back = A;
for (int i = 0; i < count; i++)
{
back = back->next;
}
tmp = back->next;
cur = back->next->next;
back->next = NULL;
while (cur)
{
tmp->next = back;
back = tmp;
tmp = cur;
cur = cur->next;
}
tmp->next = back;
back = tmp;
while (back != NULL && head != NULL)
{
if (back->val != head->val)
return false;
back = back->next;
head = head->next;
}
return true;
}