链表的回文结构
这是一道经典链表的oj题,有兴趣的可以在下面链接实践一下
链表结点原型如下:
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
1.首先分析题目:
可以百度查到,把相同的部分,在下文中调换位置或颠倒过来,叫做回文,也叫回环。
如:101,111,121,131,141,151,161,171,181,191等都是回文数,而链表的回文结构可以看作是每个结点中数据的回文结构。
而回文的结点数也有奇偶之分,奇数以中间数分隔,偶数以中间两个数之间的位置分隔,如1->2->2->1,以两个2中间的位置分隔,1->2->3->2->1,则以3所在的结点作为分隔。在做题时这两种情况都要考虑到。
2.设计算法:
因为题目没有没有说明不可以破坏链表,所以我们可以将原链表从中间的回文开始初分成两个链表,然后比较两个链表的回文部分是否相等即可。
(1)将原链表分为两个链表:
这个过程可以分为两部分,找到回文开始的结点,将之后的结点一头插的方式形成一个新链表;
如1->2->2->1 分为1->2 和1->2 ; 1->2->3->2->1分为1->2 和1->2->3。
找到回文开始的结点:
我们可以用快慢指针找中间结点的方式来查找回文开始的结点;(我们将同时考虑奇数和偶数两种情况)
首先我们让fast和slow两个指针同时指向链表的头结点
然后我们让fast指针一次走一步,slow指针一次走两步
当fast==NULL 或者 fast->next==NULL时停止寻找
此时slow 指针之后的结点便是我们新链表的结点。
(在此过程中我们定义一个front指针保存每次slow移动的前一个结点的地址,这样在我们移动slow指针指向的结点产生新的链表时,令front->next = NULL便还原原链表的尾结点的下一个指向NULL。在图例中为在3或第二个2结点移动离开后第一个2结点的next为NULL)
将之后的结点一头插的方式形成一个新链表:
(头插为链表的基础操作这里就不再对头插过程进行图例讲解了)
(另外在头插时新链表中有结点和一个结点都没有操作不同,需要分情况讨论,此时可惜malloc一块新空间让NewHead指针指向它,这样新链表中原本就有一个结点,就不需要分情况讨论了。最后记得free掉NewHead,虽然oj不一定会检查但还是释放点比较好)
最后结果为
至此我们已经有了两个新的链表,只需遍历这两个链表比较每个结点是否一样即可。
(2)比较两个链表的回文部分是否相等:
遍历两个链表比较每一个结点中的数据很简单但什么时候结束呢?
我们观察之前生成的两个链表会发现,因为是判断回文结构所以我们从中间分链表,那么因为有奇数个和偶数个之分所以原链表要么比新链表少一个结点,要么与新链表结点个数相同,。
那么当我们维护原链表的指针slow指向NULL且之前两个链表结点中的数据一直相等时,我们可以说原链表是回文结构的并且返回1。一旦有一个结点不同或者维护新链表的指针NewTail指向NULL时说明链表不是回文结构,返回0。
至此本题全部结束。
代码如下仅供参考:
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
// write code here
ListNode* fast = A;
ListNode* slow = A;
ListNode* newhead = (ListNode*)malloc(sizeof(ListNode));
ListNode* newtail = newhead;
ListNode* front = newhead;
newhead->next = A;
//找到链表回文开始的地方
while (fast && fast->next){
front = front->next;
fast = fast->next->next;
slow = slow->next;
}
//将链表分为两个部分
front->next = NULL;
newhead->next = NULL;
while (slow){
if (newhead->next == NULL){
newtail = slow;
slow = slow->next;
newhead->next = newtail;
newtail->next = NULL;
}
else{
newhead->next = slow;
slow = slow->next;
newhead->next->next = newtail;
newtail = newhead->next;
}
}
//判断两个链表回文部分是否相等
newtail = newhead->next;
slow = A;
while (slow){
if (newtail && (slow->val != newtail->val)){
free(newhead);
return 0;
}
newtail = newtail->next;
slow = slow->next;
}
free(newhead);
return 1;
}
};