力扣 - 234.回文链表
题目
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
思路1
- 可以利用首位指针,一个从前往后,一个从后往前,直到相遇,由于数组的访问速度快,于是先将链表转化为数组,再通过索引进行访问比较
- 要注意的是,如果用ArrayList
要用equals来比较,不能用 == 来比较,因为Integer-128~127之间的数字比较是相等的,指向同一个对象,而超出此范围,虽然数字一样,但是对象的地址不一样 - 时间复杂度是O(n),空间复杂度是O(n)
代码实现
import java.util.List;
import java.util.ArrayList;
class Solution {
public boolean isPalindrome(ListNode head) {
//因为存储得是Integer,超过-128~127就会创建新的对象
List<Integer> list = new ArrayList<>();
while (head != null) {
list.add(head.val);
head = head.next;
}
int front = 0;
int back = list.size() - 1;
//一直比较到相遇
while (front < back) {
if (!list.get(front).equals(list.get(back))) {
return false;
}
front++;
back--;
}
return true;
}
}
思路2(最高效)
- 利用快慢指针,slow慢指针走一步,fast快指针走两步,找到中间结点,然后翻转链表的后半部分,然后再同时进行比较链表的后半部分和前半部分是否相同
- 最好将反转后的链表在复原回去
- 时间复杂度是O(n),空间复杂度是O(1)
代码实现
class Solution {
public boolean isPalindrome(ListNode head) {
//如果输入的是空,则直接返回true
if (head == null) {
return true;
}
//获取链表前半部分的最后一个结点
ListNode firstHalfEnd = endOfFirstHalf(head);
ListNode secondHaofStart = reverseList(firstHalfEnd.next);
//利用双指针同时进行比较
ListNode p1 = head;
ListNode p2 = secondHaofStart;
while (p2 != null) {
if (p1.val != p2.val) {
return false;
}
p1 = p1.next;
p2 = p2.next;
}
//将链表复原
firstHalfEnd.next = reverseList(secondHaofStart);
return true;
}
/**
* 获取前半部分的最后一个结点
*/
public ListNode endOfFirstHalf(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
/**
* 获取反转链表后的新的首节点
*/
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode n = cur.next;
cur.next = pre;
pre = cur;
cur = n;
}
return pre;
}
}
思路3
- 利用递归来解决,时间复杂度为O(n),不过空间复杂度也是O(n)
- 空间复杂度:O(n),其中 n 指的是链表的大小。我们要理解计算机如何运行递归函数,在一个函数中调用一个函数时,计算机需要在进入被调用函数之前跟踪它在当前函数中的位置(以及任何局部变量的值),通过运行时存放在堆栈中来实现(堆栈帧)。在堆栈中存放好了数据后就可以进入被调用的函数。在完成被调用函数之后,他会弹出堆栈顶部元素,以恢复在进行函数调用之前所在的函数。在进行回文检查之前,递归函数将在堆栈中创建 n 个堆栈帧,计算机会逐个弹出进行处理。所以在使用递归时要考虑堆栈的使用情况。
代码实现
class Solution {
ListNode preNode;
public boolean isPalindrome(ListNode head) {
preNode = head;
return recursion(head);
}
public boolean recursion(ListNode curNode) {
if (curNode != null) {
// 先一直寻找到末尾,然后开始返回值一步步回溯回来
if (!recursion(curNode.next)) return false;
// 在回溯过程中仅仅判断值是否相等,如果不相等,返回false,
// 那么递归到的上一级中的!recursion(curNode.next)就会判定为ture,然后一直返回false,
// 表的头部,然后最后返回的就是false,即非回文
if (preNode.val != curNode.val) return false;
preNode = preNode.next;
}
// 否则如果是空链表或者到达末尾就开始返回true
return true;
}
}
思路4
- 使用栈,利用栈先进后出的特性,将元素一个一个顺序入栈,在出栈,就可以完成从后往前的遍历
- 不过这种算法所用的时间复杂度和空间复杂度和思路1是一样的,因为都是复制链表然后使用首位指针进行比较
代码实现
import java.util.LinkedList;
class Solution {
public boolean isPalindrome(ListNode head) {
LinkedList<Integer> stack = new LinkedList<>();
ListNode p = head;
//定义count计算链表长度
int count = 0;
//同时将元素入栈
while (p != null) {
stack.push(p.val);
p = p.next;
count++;
}
//中点位置为count / 2
for (int i = 0; i < count / 2; i++) {
if (stack.pop() != head.val) {
return false;
}
head = head.next;
}
return true;
}
}
我走得很慢,但我从不后退!

浙公网安备 33010602011771号