题目:
链接:回文链表
题解:
方法一:将值复制到数组中后用双指针法(头尾指针)
遍历链表,将链表每一项放入数组中。接着设置两个指针index1,index2分别指向数组头部和尾部,接着index1向后移动一步,index2向前移动一步,每移动一步进行一次比较,循环进行这个步骤即可。结束的条件是循环中找到一组不同项结束说明不是回文链表,或者循环结束说明是回文链表
var isPalindrome = function(head) {
if(!head.next){
return true
}
let arr =[]
let p = head
//遍历链表,将链表每一项放入数组中
while(p){
arr.push(p)
p=p.next
}
//遍历数组,进行对比
let index1 = 0
let index2 = arr.length -1
// 循环结束的条件是:
//链表长度为偶数时:index1 == index2即可退出
//链表长度为偶数时:index1 >index2即可退出
while(index1 < index2 ){
// 找到一组不同项结束说明不是回文链表,返回false
if(arr[index1].val != arr[index2].val){
return false
}
index1++;
index2--;
}
// 循环结束说明是回文链表
return true
};
复杂度分析:
时间复杂度:O(n),空间复杂度:O(n)
方法二:递归
思路其实和第一种方式差不多,也是头尾移动进行比较。链表递归有个特点,那就是递归处理节点的顺序是相反的,即是从尾部向头部进行处理的,每一次递归,当前的节点currentNode为上一个节点向前移动一位的节点,而我们又在函数外设置一个frontPointer指针指向头部,每递归一次frontPointer指针就向后移动一位,这样也能实现类似第一种方法的头尾依次进行比较。
var isPalindrome = function(head) {
// 递归
let recursivelyCheck = (currentNode)=>{
if(currentNode != null){
if(!recursivelyCheck(currentNode.next)){
return false
}
if (currentNode.val !== frontPointer.val) {
return false;
}
frontPointer = frontPointer.next
}
return true
}
let frontPointer = head
return recursivelyCheck(head)
};
复杂度分析:
时间复杂度:O(n),空间复杂度:O(n)
方法三:快慢指针
我们可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后我们应该将链表恢复原样。虽然不需要恢复也能通过测试用例,但是使用该函数的人通常不希望链表结构被更改
整个流程可以分为以下五个步骤:
-
找到前半部分链表的尾节点。
-
反转后半部分链表。
-
判断是否回文。
-
恢复链表。
-
返回结果。
执行步骤一,我们可以计算链表节点的数量,然后遍历链表找到前半部分的尾节点。
我们也可以使用快慢指针在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。
若链表有奇数个节点,则中间的节点应该看作是前半部分。
步骤二可以使用链表逆置的解决方法来反转链表的后半部分,具体思路和代码可以参考这篇:反转链表
步骤三比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点。
步骤四与步骤二使用的函数相同,再反转一次恢复链表本身。
// 递归实现链表逆置
let reverList1 = (head)=>{
if(head == null || head.next == null){
return head
}
let newHead = reverList1(head.next)
head.next.next = head
head.next = null
return newHead
}
、// 迭代实现链表逆置
let reverList2 = (head)=>{
let pre = null
let cur = head
while(cur){
let next = cur.next
cur.next = pre
pre = cur
cur = next
}
return pre
}
// 获取链表后半部分的头部,便于后续对后半部分的逆置和遍历
let half = (head)=>{
let slow = head
let fast = head
while(fast.next && fast.next.next){
slow = slow.next
fast = fast.next.next
}
return slow
}
var isPalindrome = function(head) {
if(head == null) return true
let firstList = half(head)
let secondList = reverList2(firstList.next)
let p1=head
let p2 = secondList
let result = true
// 这里只能是 p2!=null ,而不能是p1!=null,因为前半部分的链表可能比后半部分的链表多一个节点,而这个节点是不需要参与比较的(链表长度为奇数时会多一个节点)
while(p2){
if(p1.val != p2.val){
result =false
break;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表
firstList.next = reverList2(secondList)
return result
};
复杂度分析:
时间复杂度:O(n),空间复杂度:O(1)