34. 回文链表
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 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) {
//方法一:数学计算链表中点+后半部翻转
//根据链表长度计算出链表中点,划分出左、右,便于使用同步双指针判断结点的值
if(head == null || head.next == null){
return true;
}
ListNode p = head;
ListNode q = head;
ListNode left = null;
int len = 0;
while(p != null){
p = p.next;
len++;
}
int min = len/2;
while(min != 0){
q = q.next;
min--;
}
//根据链表长度确定后半部分链表起点
if(len % 2 == 0){
left = reverseListNode(q);
} else{
left = reverseListNode(q.next);
}
//比较链表前半部分和后半部分结点的值,判断是否满足回文规则
p = head;
while(p != null && left != null){
if(p.val != left.val){
return false;
}
p = p.next;
left = left.next;
}
return true;
}
//翻转链表
public ListNode reverseListNode(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode p = head.next;
ListNode q = null;
ListNode r = head;
head.next = null;
while(p != null){
q = p.next;
p.next = r;
r = p;
p = q;
}
return r;
}
}
方法二:快慢双指针+后半部分翻转
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
// 如果链表长度 <=1,则直接返回 true
// 先找到链表的中点(快慢指针)
// 再以中点为起点,将后面链表翻转(迭代、遍历)
// 最后分别以头结点和中点为起点,向后遍历,比较值是否相等,如果出现不等,则不是回文链表
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) {
return true;
}
// 找到链表的中点
ListNode mid = findMid(head);
// 翻转中点后的链表
mid = reverseList(mid);
// 比较两段链表
while (mid != null) {
if (head.val != mid.val) {
return false;
}
head = head.next;
mid = mid.next;
}
return true;
}
// 翻转链表(迭代、递归)
private ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
return prev;
}
// 使用快慢指针找到链表的中间节点
private ListNode findMid(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
方法三:使用栈进行对比
- 可以按链表所有元素入栈,栈中保存于原链表的逆序链表,依次比较新链表和原链表的值是否完全相同,如果是,原链表这是回文链表。但是这种方法比较次数可以优化
- 优化:找到链表中点,将原链表前半部分或者后半部分结点入栈,再比较
class Solution {
public boolean isPalindrome(ListNode head) {
Stack<ListNode> stack=new Stack<>();
ListNode cur=head;
while(cur!=null){
stack.push(cur);
cur=cur.next;
}
cur=head;
while(cur!=null){
if(cur.val != stack.pop().val){
return false;
}
cur=cur.next;
}
return true;
}
}