题目
方法一:使用栈记录遍历节点,再比较
栈有先进后出的特点,遍历一遍链表,依次入栈;再遍历一遍链表并依次对栈进行弹栈操作并比较是否满足回文要求
public boolean isPalindrome(ListNode head) {
Stack<ListNode> stack = new Stack<>();
ListNode tmp = head;
while(tmp != null){
stack.push(tmp);
tmp = tmp.next;
}
tmp = head;
while(tmp != null){
if(tmp.val != stack.pop().val)
return false;
tmp = tmp.next;
}
return true;
}
- 时间复杂度:需要两次遍历链表,时间复杂度为O(2n)=O(n)
- 空间复杂度:需要额外的栈空间存储遍历的节点信息,O(n)
方法二:转为数组,使用双指针比较
遍历一遍链表,将其转为一个数组,就可以使用首指针和尾指针依次像中间遍历比较
public boolean isPalindrome(ListNode head) {
List<ListNode> list = new ArrayList<>();
ListNode tmp = head;
while(tmp != null){
list.add(tmp);
tmp = tmp.next;
}
int i = 0 , j = list.size() - 1;
while(i < j){
if(list.get(i++).val != list.get(j--).val)
return false;
}
return true;
}
- 时间复杂度:需要遍历一遍链表和一遍数组,时间复杂度为O(2n)=O(n)
- 空间复杂度:需要额外的数组空间,O(n)
方法三:翻转半段比较
使用快慢指针的方式,找到链表的中间节点,即快指针走两步,慢指针走一步,当快指针到尾部的时候,慢指针即指向中间位置。
找到中间位置后,将后半段的链表进行翻转操作,再依次遍历前半段和后半段,比较相应的节点。
public boolean isPalindrome(ListNode head) {
if(head == null)
return true;
// 中间节点
ListNode midNode = getMidNode(head);
// 翻转后半段
ListNode rightHead = reverse(midNode.next);
ListNode tmpI = head , tmpJ = rightHead;
boolean rs = true;
while(tmpJ != null){
if(tmpI.val != tmpJ.val){
rs = false;
break;
}
tmpI = tmpI.next;
tmpJ = tmpJ.next;
}
// 还原
reverse(rightHead);
return rs;
}
/**
* 获得中间节点
* @param head
* @return
*/
public ListNode getMidNode(ListNode head){
if(head == null || head.next == null)
return head;
ListNode fast = head , slow = head;
while(fast.next != null){
fast = fast.next;
if(fast.next != null){
fast = fast.next;
slow = slow.next;
}
}
return slow;
}
/**
* 翻转链表
* @param head
* @return
*/
public ListNode reverse(ListNode head){
if(head == null || head.next == null)
return head;
ListNode newHead = head , tmp;
while(head.next != null){
tmp = head.next;
head.next = tmp.next;
tmp.next = newHead;
newHead = tmp;
}
return newHead;
}
- 时间复杂度:找到中间节点需要遍历一遍链表,O(n),翻转后半段需要O(n),比较是否为回文需要O(n),总复杂度为O(n)
- 空间复杂度:不需要额外的存储空间,为O(1)