回文字符串和数组我们会经常遇到,今天讲一个相关问题,叫回文链表,还是和以前一样,先把代码提上来。
- // need O(1) extra space
- public static boolean isPalindrome3(Node head) {
- if (head == null || head.next == null) {
- return true;
- }
- Node n1 = head;
- Node n2 = head;
- while (n2.next != null && n2.next.next != null) { // find mid node
- n1 = n1.next; // n1 -> mid
- n2 = n2.next.next; // n2 -> end
- }
- // n1 中点
-
-
- n2 = n1.next; // n2 -> right part first node
- n1.next = null; // mid.next -> null
- Node n3 = null;
- while (n2 != null) { // right part convert
- n3 = n2.next; // n3 -> save next node
- n2.next = n1; // next of right node convert
- n1 = n2; // n1 move
- n2 = n3; // n2 move
- }
- n3 = n1; // n3 -> save last node
- n2 = head;// n2 -> left first node
- boolean res = true;
- while (n1 != null && n2 != null) { // check palindrome
- if (n1.value != n2.value) {
- res = false;
- break;
- }
- n1 = n1.next; // left to mid
- n2 = n2.next; // right to mid
- }
- n1 = n3.next;
- n3.next = null;
- while (n1 != null) { // recover list
- n2 = n1.next;
- n1.next = n3;
- n3 = n1;
- n1 = n2;
- }
- return res;
- }
这段代码比传统方式要快很多,下面讲一下具体的流程:
if (head == null || head.next == null)
:首先检查链表是否为空或只包含一个节点,因为这种情况下链表必然是回文,所以直接返回true
。初始化两个指针
n1
和n2
都指向链表的头节点head
。这两个指针将用于查找链表的中间节点。进入一个循环,条件是
n2.next != null && n2.next.next != null
,循环中n1
每次移动一步,而n2
每次移动两步,这样当n2
到达链表尾部时,n1
恰好指向链表的中间节点(如果链表长度为奇数,则中间节点只有一个;如果链表长度为偶数,则中间节点有两个,这里n1
指向靠左的那一个)。将
n2
移动到n1
的下一个节点,这是为了将链表分为两部分:左边部分和右边部分。断开
n1
和n2
之间的连接,即将n1.next
设置为null
,从而分割链表为两个独立的部分。接下来是将右边部分(后半部分)的链表反转。使用三个指针
n1
、n2
、n3
分别指向当前节点、下一个节点和用于保存下下一个节点。通过循环,不断将当前节点的next
指针反向指向前一个节点,完成链表的反转。在反转完成后,
n3
会指向反转后链表的头节点,而n2
则指向左边部分链表的头节点。同时,n1
指向n3
,表示将n1
移动到反转链表的头节点位置。接下来,使用两个指针
n1
和n2
同时遍历左边部分和反转后的右边部分,比较它们的值是否相等。如果有任何不相等的情况出现,将res
设置为false
,表示链表不是回文。最后,需要将右边部分的链表再次反转回来,以恢复原始链表的结构。这是通过循环和指针操作实现的。
返回
res
,表示是否判断链表为回文。
假设输入链表为:1 -> 2 -> 3 -> 2 -> 1
首先找到链表的中间节点,n1
指向节点 3。
将右边部分反转,得到链表:1 -> 2 -> 3 和 1 -> 2。
通过比较左右两边部分的值,发现它们是回文的。
最后将右边部分反转回来,得到原始链表:1 -> 2 -> 3 -> 2 -> 1。