法一:双指针(O(n)、O(n))
- 将链表转成数组
- 设置两个指针L和R指向数组的第一位和最后一位,
- 移动并判断下标L和R对应的数组元素是否相同
- 都相同则是回文链表返回true,不相同则不是回文链表直接返回false
// 方法一:双指针,O(n)
public boolean isPalindrome(ListNode head) {
// base case
if (head == null || head.next == null) return true;
List<Integer> list = new ArrayList<>();
while (head != null) {
// 遍历链表,将链表转为数组,这样可以动的链表的长度
list.add(head.val);
head = head.next;
}
// 左指针,位于数组的第一位,依次往右遍历
int L = 0;
// 右指针,位于数组的最后一位,依次往左遍历,-1是因为数组下标从0开始,
int R = list.size() - 1;
// 双指针移动判断
while (L < R) {// 此处 < 是不区分链表长度是奇数还是偶数
// 比较当前的下标L和R对应元素不相等的话,则不是回文链表
// 此处用equals方法比较的是对象,因为list存的是Integer对象,所以不能用==(比较的是基本数据类型)
if (!list.get(L).equals(list.get(R))) return false;
L++;// 左指针往右移动一位
R--;// 右指针往左移动一位
}
// 全部比较完成且全部相等,则是回文链表
return true;
}
法二:递归(O(n)、O(n))
- O(n)、O(n)
// 方法二:递归,
// p 指向头结点
ListNode p ;
public boolean isPalindrome(ListNode head) {
p = head;
return recursion(head);
}
// 递归方法,全部返回true才是回文,
public boolean recursion(ListNode head) {
// 递归出口,当遍历链表完时开始倒退
if (head == null) return true;
// 递归,倒退
if (!recursion(head.next)) return false;
// 当前p的值和倒退的值不一样则不是回文链表
if (p.val != head.val) return false;
// p 后移
p = p.next;
// 当前p的值和倒退的值一样则是回文链表
return true;
}
法三:栈(O(n)、O(n))
- O(n)、O(n)
// 方法三:栈
public boolean isPalindrome(ListNode head) {
// base case 空算回文
if (head == null || head.next == null) return true;
ListNode temp = head;
Stack<Integer> stack = new Stack<>();
// 链表全部入栈
while (temp != null) {
stack.push(temp.val);
temp = temp.next;
}
// 出栈并比较
while (head != null && !stack.isEmpty()) {
// 出栈和当前链表结点比较,不同返回false 不是回文链表
if (head.val != stack.pop()) return false;
// 相同,继续比较下一位
head = head.next;
}
// 比较完了,且都相等,证明是回文链表返回true
return true;
}
法四:快慢指针(推荐使用,O(n)、O(1))
- 快指针
fast
移动两步,慢指针slow
移动一步,
遍历链表完后慢指针slow
移动到了链表的中点位置 - 遍历完后,将快指针
fast
重新指向头结点,将慢指针slow
之后的链表翻转, - 一次比较快指针
fast
的值和慢指针slow
的值,如果有一个不相等直接返回false,不是回文链表;如果比较完后全部相等,那么就是回文链表,返回true。
// 法四:快慢指针
public boolean isPalindrome(ListNode head) {
// base case 空算回文
if (head == null || head.next == null) return true;
// 设置两个快慢指针
ListNode fast = head;
ListNode slow = head;
// 遍历、移动两个指针
while (fast != null && fast.next != null) {// fast移动到链表末端
// 快指针每次移动两步
fast = fast.next.next;
// 慢指针每次移动一步
slow = slow.next;
}
// 将快指针重置为链表的头结点
fast = head;
// 翻转slow之后的链表
ListNode reverseList = reverseList(slow);
// 比较 fast 和 翻转后的reverseList 的值,结束条件:比较到链表的末尾
while (reverseList != null) {
// 出现一个不相等,则不是回文链表
if (fast.val != reverseList.val) return false;
// fast 和 翻转后的reverseList的指针后移一步
fast = fast.next;
reverseList = reverseList.next;
}
// 比较完了,全部相等,则是回文链表
return true;
}
// 翻转链表
public ListNode reverseList(ListNode head) {
ListNode previous = null;
ListNode current = head;
while (current != null) {
// 记录下一结点
ListNode next = current.next;
// 前面结点的赋给后面的
current.next = previous;
// 后面结点的赋给前面的
previous = current;
// 结点后移,即开始处理下一个结点
current = next;
}
// 返回翻转后的链表
return previous;
}