回文链表【234】
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
方法一:快慢指针
思路:
1.使用快慢指针找到链表的中间点
2.反转中间点后面的链表
3.后半段反转后的链表与上半部分链表比对,如果比对完后都相同,返回true
4.恢复链表,虽然不恢复也可以通过测试,但是尽量不要改变数据结构
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null){return true;}
ListNode middle = getMiddle(head);
ListNode pre = reverse(middle.next);
// 判断是否回文
ListNode p1 = head;
ListNode p2 = pre;
boolean result = true;
while (result && p2 != null) {
if (p1.val != p2.val) {
result = false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果
middle.next = reverseList(pre);
return result;
}
//快慢指针
private ListNode getMiddle(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;
}
//反转链表
private ListNode reverse(ListNode head){
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
return prev;
}
}
复杂度分析
-
时间复杂度:O(n),其中 nn 指的是链表的大小。
-
空间复杂度:O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)
方法二:将值复制到数组中后用双指针法
该方法比较简单:
思路:
1.复制链表值到数组列表中。
2.使用双指针法判断是否为回文
代码:
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> vals = new ArrayList();
// 将链表的值复制到数组中
ListNode currentNode = head;
while(currentNode != null){
vals.add(currentNode.val);
currentNode = currentNode.next;
}
// 使用双指针判断是否回文
int first = 0;
int tail = vals.size() - 1;
while(first < tail){
if(!vals.get(first).equals(vals.get(tail))){
return false;
}
first++;
tail--;
}
return true;
}
}
复杂度分析
- 时间复杂度:O(n),其中 nn 指的是链表的元素个数。
- 第一步: 遍历链表并将值复制到数组中,O(n)。
- 第二步:双指针判断是否为回文,执行了 O(n/2) 次的判断,即 O(n)。
总的时间复杂度:O(2n) = O(n)O(2n)=O(n)。 - 空间复杂度:O(n),其中 nn 指的是链表的元素个数,我们使用了一个数组列表存放链表的元素值。