算法 - 链表 - 判断一个链表是否为回文结构

问题三 判断一个链表是否为回文结构

给定一个链表的头节点 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;
}

在遍历的过程中,修改了链表的结构,无论结果如何,最终还是要将改变的结构再逆转回来。

posted @ 2019-11-28 13:35  学习趁早  阅读(247)  评论(0编辑  收藏  举报