题目描述
请判断一个链表是否为回文链表。
样例
输入: 1->2
输出: false
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
算法分析
核心思路:找到链表右半部分链,并将右半部分链进行反转,对左半部分链表 和 反转后的右半部分链表 的元素进行一一比较,若都相同则是回文链表
具体方法
- 1、通过快慢指针
slow
和fast
,slow
每次走一步,fast
每次走两步,找到右半部分链,由于不知道链表的元素个数是奇数个还是偶数个,因此统一规则让 右半部分链 比 左半部分链 的元素个数最多多1
个,初始化slow = head
,fast = head.next
,则slow.next
就是右半部分链的开头 - 2、将右半部分链进行反转,由于题目要求空间复杂度是O(1),因此只能用迭代的方式对链表进行反转,反转后记得将
slow.next
指向null
,让左右链表隔离开 - 3、假设左边链表的个数是
x
个,右边链表的个数可能是x
个,也可能是x + 1
个,因此比较x
次,若x
次中链表的元素都一一对应则是回文链表,否则不是回文链表
时间复杂度 O(n)
空间复杂度 O(1)
Java 代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
static ListNode reverse(ListNode head)
{
ListNode a = head,b = head.next;
while(b != null)
{
ListNode c = b.next;
b.next = a;
a = b;
b = c;
}
head.next = null;
return a;
}
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
ListNode slow = head, fast = head.next;
while(fast.next != null && fast.next.next != null)
{
slow = slow.next;
fast = fast.next.next;
}
//将右半部分的链表slow.next进行反转
ListNode right = reverse(slow.next);
ListNode left = head;
slow.next = null;
while(left != null && right != null)
{
if(left.val != right.val) return false;
left = left.next;
right = right.next;
}
return true;
}
}
class Solution { public boolean isPalindrome(ListNode head) { int n = 0; for(ListNode p = head; p != null; p = p.next) n++; //n个点 if(n <= 1) return true; int half = n / 2; //想一下奇数个和偶数个的情况 // if(n % 2 == 0) int half = n / 2 + 1; // else int half = n / 2; ListNode a = head; //head的next不变的 for(int i = 0; i < n - half; i++) a = a.next; ListNode b = a.next; //结束时b == null for(int i = 0; i < half - 1; i++){ ListNode c = b.next; b.next = a; a = b; b = c; } // while(b != null){ // ListNode c = b.next; // b.next = a; // a = b; // b = c; // } ListNode p = head; ListNode q = a; //a的next其实还是没变的,因为是q.next也没变只是在往前走 boolean success = true; for(int i = 0; i < half; i++){ if(p.val != q.val){ success = false; break; } p = p.next; q = q.next; } ListNode tail = a; //a的next那一段都没变,a还是指向了最后一个节点 ListNode tmp = a.next; //tail指向的节点一直都是最后一个,因为是虽然说a = tmp但tmp是a.next //tail = a指向的这个节点的next并没有被动过 //tail.next = null; //将链表恢复原状 //System.out.println(tail.val); for(int i = 0; i < half - 1; i++){ ListNode c = tmp.next; tmp.next = a; a = tmp; tmp = c; } //相当于把a赋给了tail暂存了一个节点的 //tail不会跟着a往前走吗??????????????????????????????????????????????? tail.next = null; //System.out.println(tail.val); return success; }
我想问一下最后这个tail不应该跟着a往前移动吗(ListNode tail = a;传的不是地址吗),为什么这个tail最后还是一开始ListNode tail = a;的最后一个节点的位置
tail
和a
都是一个指针,ListNode tail = a
表示tail
指针和a
指针都指向某个地址,当a
经过移动后指向了其他的地址,与tail
指向的地址就不同了。并不能说a
指向某个地址,就可以看成是将a
指向的原来的地址修改了,这是错误的明白了,谢谢~