【LeetCode 234】Palindrome Linked List 解题报告

来源:互联网 发布:特朗普移民禁令 知乎 编辑:程序博客网 时间:2024/05/23 23:47


一、 题意描述

Given a singly linked list, determine if it is a palindrome.(do it in O(n) time and O(1) space)

即判断一个单向循环链表是否为回文序列(形如“1” “33” “14341”等形式的均为回文序列)。

二、题目分析

1.若给出的形式为线性结构,则很容易写出第一种方法(显然空间、时间均符合题意要求):

bool isPalindrome (int *num){for(int i=1;i<=length/2;i++)if(num[i]!=num[length-i])return false;return true;}

但题目中所给为链式结构,且为单向链表。所以不能通过*tail指针逆向访问链表的节点,上述方法因此便无法使用了。


2.如果建立一个数组保存链表的某个元素,则问题便划归到第一种方法来解决,但因此需要额外开辟O(n)的空间,因此不能使用。

3.如果只开辟一个变量存储链表的所有信息,则可通过读取此变量来获取此链表的所有信息,则又将问题划归成第一种方法解决。

   此思路虽然不是最正确的,但在每个链表的数字均为个位数,且链表的长度较小的情况下,依然可以写出如下的方法:

bool isPalindrome(ListNode* head) {if(head==0||head->next==0)return true;unsigned long long temp=0;//将链表中的每个元素连续排在一起,形成一个十进制数 unsigned long long tpow=1;//存储 10 的n次幂 int cnt=0;for(ListNode *pos=head;pos;pos=pos->next){temp=temp*10 + pos->val;tpow*=10;cnt++;}for(int i=1;i<=cnt/2;i++){if(10*temp/tpow!=temp%10)//比较 temp的第一位和最后一位return false;temp=(temp-tpow*(temp*10/tpow)-temp%10)/10;//删去temp的第一位和最后一位 tpow/=100;}return true;}

   虽然此方法并不能成功的AC这道题,但也有几点可取之处:

(1).对原链表不作任何修改,从而维护了链表的稳定性,保护了原始数据;

(2).在一定程度上借鉴了哈希表的思路。


4.通过前3点的分析,我们基本可以认为,不能通过开辟新的空间完成此题。于是只好利用链表原有的空间。

   但在不修改链表节点的情况下,读取元素的时间复杂度为O(n),则整个方法的时间复杂度为O(n^2),不符合题目要求。于是只好修改链表节点。


三.最终方案

考虑到 ListNode 已经封装完毕,不能再添加 *last 指针和 *tail 指针(事实上,添加指针的过程依然相当于开辟了O(n) 的空间)。则容易想到用新的值覆盖原有的 *next 指针,从而将原链表拆成两个部分,并将其中一个部分反向。

由此,我们得出以下步骤:

(1)扫描整个链表,确定链表的长度并保存;

(2)将链表从正中间分成两个部分,并将其中一个部分反向(此处考虑将前半部分反向的情况);

(3)从两个链表的头结点(即原链表的中间部分)开始扫描链表并比较元素大小,如果两个链表的元素分别相同,则返回值true,否则返回false。


四.代码实现


bool isPalindrome(ListNode* head) {int sum=0;ListNode *pos=0;for(pos=head;pos;pos=pos->next)//扫描链表,得出链表长度 sum++;if(sum==0||sum==1)return true;//考虑特殊情况(一般都会单独设置测试点) //***********************将链表反向************************ListNode *temp=head->next,*tempp=head->next->next;pos=head;head->next=0;//将头结点的next值改为0,此处容易忘记 for(int i=1;i<=sum/2-1;i++){temp->next=pos;pos=temp;temp=tempp;tempp=tempp->next;}//**********************************************************ListNode  *i=pos,*j=0;if(sum%2==1)j=tempp;else j=temp;while(i&&j){if(i->val!=j->val)return false;i=i->next,j=j->next;}return true;}

五.方法复杂性及性能分析

 

1.容易看出,此方法需要扫描三次链表,时间复杂度为O(n);没有通过开辟数组或其他数据结构,因此空间复杂度为O(1)。另外经过测试,耗时为24ms,是目前提交的代码中最快的一个;

2.正如前面所提到的,此方法的缺点在于修改了链表,丢失了原始数据。因此在今后的编程中,应尽量避免采用此类方法(通过设置*tail指针等等来规避此类问题);

3.此方法可以再将第一、二次扫描过程合并,从而进一步简化过程。由于思路想法类似,在此不再给出详细实现过程;

4.此方法有若干种不同的写法,一种最常见的变形即为递归式:

bool work(ListNode *i,ListNode *j){if(i->val==j->val){if(i->next&&j->next)return work(i->next,j->next);else return true;}return false;}bool isPalindrome(ListNode* head){        //前面过程类似        return work(i,j);}
此方法与原方法本质上一致,只是写法上更加简化,时间依然为24ms。
0 0
原创粉丝点击