问题三 判断一个链表是否为回文结构
给定一个链表的头节点 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; }
在遍历的过程中,修改了链表的结构,无论结果如何,最终还是要将改变的结构再逆转回来。