目录
题目描述
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。测试样例:
1->2->2->1
返回:true
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
题目链接:链表的回文结构_牛客题霸_牛客网 (nowcoder.com)
思路1:直接反转整个链表,反转后的链表与反转前的链表相同
关键的问题是要怎么样反转链表?
反转链表
对于单链表,反转时需要三个指针分别指向前中后三个结点,中间结点的next指向前结点完成中间结点的反转,后指针指向中间结点的原后继,防止链表丢失
代码如下:
public ListNode reverseList(ListNode head) {
//遍历,修改所有结点的next
ListNode cur=head;
ListNode pev=null; //指向cur的上一个结点
while(cur!=null){
ListNode next=cur.next; //提前保存下一个结点
cur.next=prev;
pev=cur;
cur=next;
}
return pev;
}
图解如下:
第一次循环:
会反转链表之后,这道题就变得很简单了,剩下的就是比较val的值是否相同
原代码:
public class PalindromeList {
public ListNode reverseList(ListNode A) {
//遍历,修改所有结点的next
ListNode cur = A;
ListNode prev = null; //指向cur的上一个结点
while (cur != null) {
ListNode next = cur.next; //提前保存下一个结点
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
public boolean chkPalindrome(ListNode A) {
// write code here
ListNode first = A;
ListNode last = reverseList(A); //反转后的链表
while (first != null) {
if (first.val != last.val) {
return false;
}
first = first.next;
last = last.next;
}
return true;
}
}
思路二:其实可以看出,没有必要反转整个链表
1、可以尝试找到链表的中间结点
2、反转其中一半的链表,得到两条链表
3、依次比较两个链表中的每个结点,如果全部相等true,存在不相等false。所以现在最主要的问题是如何找到中间结点。
找链表的中间结点
方法一 :求出结点个数,找中间结点
如果可以知道链表的结点个数n,无论结点是奇数个还是偶数个,向后跳 n/2步即可找到中间结点,具体解释如下图所示:
由图可知,在比较到第三次的时候,总有一个指针会为空,此时循环应该退出,因此,循环的条件可以设为cur!=null&&ans!=null
原码如下:
//计算结点个数
public int countnum(ListNode A) {
ListNode cur = A;
int count = 0;
//遍历
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
//找中间的结点
public ListNode midNode(ListNode A) {
ListNode cur = A;
int count = countnum(A) / 2;
for (int i = 0; i < count; i++) {
cur = cur.next;
}
return cur;
}
//翻转后半段
public ListNode reverseList(ListNode A) {
ListNode pev = null; //记录前一个结点
ListNode cur = A; //记录当前结点
while (cur != null) {
ListNode next = cur.next; //保存cur的后一个结点
cur.next = pev;
//改变链表方向
pev = cur;
cur = next;
}
return pev;
}
public boolean chkPalindrome(ListNode A) {
ListNode midnode = midNode(A);
//记录反转后的链表
ListNode ans = reverseList(midnode); //将中间结点传入
ListNode cur = A;
while (ans != null && cur != null) {
if (ans.val != cur.val) {
return false;
} else {
ans = ans.next;
cur = cur.next;
}
}
return true;
}
方法二:使用快慢指针找到中间结点
使用快慢指针,两指针一开始都指向head,那么fast一次2步,slow一次1步,那么fast走到最后的节点,slow刚好指向中间
//找中间的结点
public ListNode midNode(ListNode A) {
ListNode fast = A;
ListNode slow = A;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
反转链表和比较跟前面是一样的。