请判断一个链表是否为回文链表。
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
解法一
数组
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
// 链表为空或者链表节点为1时,为回文链表
if (head == null || head.next == null) return true;
// 数组
// 循环得到链表的长度
int count = 0;
ListNode temp = head;
while (temp != null) {
temp = temp.next;
count++;
}
// 创建数组,把链表中的val都放到数组中
int[] arr = new int[count];
temp = head;
for (int i = 0; i < count; i++) {
arr[i] = temp.val;
temp = temp.next;
}
// 定义两个指针,从两头比较,往中间走
int l = 0, r = count - 1;
while (l < r) {
if (arr[l++] != arr[r--]) {
return false;
}
}
return true;
}
}
解法二
list,思路和第一种解法一样,只是少了前面的循环获取链表的长度。
class Solution {
public boolean isPalindrome(ListNode head) {
// 链表为空或者链表节点为1时,为回文链表
if (head == null || head.next == null) return true;
// list
List<Integer> list = new ArrayList<>();
// 把链表的val都放到list中
ListNode temp = head;
while (temp != null) {
list.add(temp.val);
temp = temp.next;
}
// 双指针比较
int l = 0, r = list.size() - 1;
while (l < r) {
// 这里要取反
if (!list.get(l).equals(list.get(r))) {
return false;
}
l++;
r--;
}
return true;
}
}
解法三
栈
class Solution {
public boolean isPalindrome(ListNode head) {
// 链表为空或者链表节点为1时,为回文链表
if (head == null || head.next == null) return true;
// 栈
Deque<Integer> stack = new ArrayDeque<>();
// 快慢指针,把前面一半放入栈中
ListNode s = head, f = head;
// 链表个数双数时保持慢指针的值,然后跳出,如果是单数,直接跳出
while (f.next != null) {
// 如果f的下下个节点为空,说明链表的个数为双数
if (f.next.next == null) {
stack.offerLast(s.val);
break;
}
// 把慢指针的val保存到栈中,指针移动
stack.offerLast(s.val);
s = s.next;
f = f.next.next;
}
// 慢指针指向下个节点,然后从栈中取值比较
s = s.next;
while (s != null) {
// 不相等,返回false
if (s.val != stack.pollLast()) {
return false;
}
s = s.next;
}
return true;
}
}
前两种做法都不满足进阶的条件,特别是第二种解法,存入取出慢。
解法四
递归
class Solution {
private ListNode frontPointer;
public boolean isPalindrome(ListNode head) {
// 递归
frontPointer = head;
return recursivelyCheck(head);
}
private boolean recursivelyCheck(ListNode node) {
if (node == null) return true;
// 会递归到最后一个节点,然后和 frontPointer 比较
// 如果不是回文,返回false
if (!recursivelyCheck(node.next)) {
return false;
}
// 和 frontPointer 比较,如果不相同,说明不是回文,返回false
if (node.val != frontPointer.val) {
return false;
}
// frontPointer 指向下一位
frontPointer = frontPointer.next;
return true;
}
}
解法五
快慢指针,反转后半部分,满足进阶条件,不过会破坏原链表。
class Solution {
public boolean isPalindrome(ListNode head) {
// 链表为空或者链表节点为1时,为回文链表
if (head == null || head.next == null) return true;
//快慢指针,反转后半部分
ListNode slow = head, fast = head;
// 找出中点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 如果快指针不为空,说明链表的个数为单数,中间不用比较,慢指针指向下一个节点
if (fast != null) {
slow = slow.next;
}
// 反转后半部分
ListNode pre = slow, prepre = null;
while (slow != null) {
// 保存当前慢指针的位置
pre = slow;
// 移动慢指针
slow = slow.next;
// 反转
pre.next = prepre;
prepre = pre;
}
// 跳出循环后,head为前半部分的头节点,pre为后半部分的头节点
while (head != null && pre != null) {
if (head.val != pre.val) {
return false;
}
head = head.next;
pre = pre.next;
}
return true;
}
}
解法六
快慢指针,反转前半部分,也满足进阶条件,不过同样会破坏原链表,思路来着题解区芜湖大司码
的题解。
class Solution {
public boolean isPalindrome(ListNode head) {
// 链表为空或者链表节点为1时,为回文链表
if (head == null || head.next == null) return true;
// 快慢指针,反转前半部分
ListNode slow = head, fast = head;
ListNode pre = head, prepre = null;
while (fast != null && fast.next != null) {
// 保存当前慢指针的位置
pre = slow;
// 移动慢指针、快指针
slow = slow.next;
fast = fast.next.next;
// 反转
pre.next = prepre;
prepre = pre;
}
// 跳出循环后,前半部分链表的头节点为pre,后半部分的头节点为slow
// 跳出循环后判断,如果快指针所指节点不为空,说明这条链表长度为单数,最中间节点不用比较,慢指针再移动一个节点
if (fast != null) {
slow = slow.next;
}
// 比较两条链表
while (slow != null && pre != null) {
if (slow.val != pre.val) {
return false;
}
slow = slow.next;
pre = pre.next;
}
return true;
}
}