算法 - 链表 - 判断一个链表是否为回文结构
问题三 判断一个链表是否为回文结构
给定一个链表的头节点 head,请判断该链表是否为回文结构。
1->2->1,返回 true。
1->2->2->1,返回 true。
15->6->15,返回 true。
1->2->3,返回 false。
思路
第一次遍历,使用一个栈结构存储节点,第二次遍历,与弹出栈存储的值比较,相同为 true 不同为 false。
可以使用快慢指针得到链表的对称轴的下一个节点,然后将值压栈,然后重头开始遍历,省下一半的栈空间,但是在计算空间复杂度时还是会忽略不计系数。
// need n extra space
public static boolean isPalindromeOne(Node head) {
if (head == null || head.next == null) return true;
Stack<Node> stack = new Stack<>();
Node cur = head;
while (cur != null) {
stack.push(cur);
cur = cur.next;
}
while (head != null) {
if (head.val != stack.pop().val) {
return false;
}
head = head.next;
}
return true;
}
// need n/2 extra space
public static boolean isPalindromeTwo(Node head) {
if (head == null || head.next == null) return true;
// These steps ensure the rigth point can be the exactly position.
Node right = head.next;
Node cur = head;
while (cur.next != null && cur.next.next != null) {
right = right.next;
cur = cur.next.next;
}
Stack<Node> stack = new Stack<>();
while (right != null) {
stack.push(right);
right = right.next;
}
while (!stack.isEmpty()) {
if (stack.pop().val != head.val) {
return false;
}
head = head.next;
}
return true;
}
快慢指针举例
通过代码是可以适应链表长度为奇数和偶数的情况,right 指针都会指到右半部分的链表的初始位置。
c: point cur
r: point right
1->2->3->2->1
1.
1->2->3->2->1
↑ ↑
c r
2.
1->2->3->2->1
↑
c r
3.
1->2->3->2->1
↑ ↑
r c
1->2->3->3->2->1
1.
1->2->3->3->2->1
↑ ↑
c r
2.
1->2->3->3->2->1
↑
c r
3.
1->2->3->3->2->1
↑ ↑
r c
进阶
如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
思路
1.快慢指针,得到链表的中点,将中点后的节点逆序。
1->2->3->2->1
1->2->3<-2<-1
↑ ↑
2.两端开始遍历,当两根指针相遇说明是回文结构,当值不相等的时候说明不是回文结构
3.最后,将中点的后的节点再逆序
1->2->3->2->1
实现
public static boolean isPalindromeThree(Node head) {
if (head == null || head.next == null) return true;
Node n1 = head;
Node n2 = head;
// n1 will be the end of left part or center
while (n2.next != null && n2.next.next != null) {
n1 = n1.next;
n2 = n2.next.next;
}
// n2 will be the begin of right part
n2 = n1.next;
n1.next = null;
// convert the right part
Node n3 = null;
while (n2 != null) {
n1 = n2.next; // save next node
n2.next = n3;
n3 = n2;
n2 = n1;
}
// n3 point the end of list; n2 & n1 is null
// n3 | n2 | n1
n1 = head;
n2 = n3;
boolean res = true;
// compare n1 n3; n2 save the end of list
while (n1 != null && n3 != null) {
if (n1.val != n3.val) {
res = false;
break;
}
n1 = n1.next;
n3 = n3.next;
}
// reconvert the right part
// reconvert the end of list
n3 = n2.next;
n2.next = null;
// n1 | n3 | n2
while (n3 != null) {
n1 = n3.next; // save the node
n3.next = n2;
n2 = n3;
n3 = n1;
}
return res;
}
在遍历的过程中,修改了链表的结构,无论结果如何,最终还是要将改变的结构再逆转回来。