六种方法判断链表是否为回文序列

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六种方法判断链表是否为回文序列相关的知识,希望对你有一定的参考价值。

这也是一道不难,但是很经典的链表题,请判断一个链表是否为回文链表。

示例1:
输入: 1->2->2->1
输出: true

进阶
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

看到这个题你有几种思路解决,经过前面这些题目的蹂躏,我现在看到这个题瞬间就想到6种解法。虽然有几个是相同的,但是仍然可以是新的方法。

方法1:将链表元素都赋值到数组中,然后可以从数组两端向中间对比。

方法2:将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较,只要有一个不相等,那就不是回文链表了。

方法3:上面方法的改造,先遍历第一遍,得到总长度。之后一遍历链表,一遍压栈。当到达链表长度一半的位置之后,就不再压栈,而是一边出栈,一遍遍历,一遍比较,只要有一个不相等,就不是回文链表。

方法4:反转链表法, 先创建一个链表newList,然后原始链表oldList的元素值逆序保存到newList中,然后重新遍历newList和oldList,同时比较元素的值,只要有一个位置的元素值不一样,就不是回文链表。

方法5:将4进行优化,我们其实只反转一半的元素就行了。步骤是:先遍历一遍链表,得到长度,然后重新遍历链表,一边遍历,一边将链表反转。之后到达一半的位置后,就不再反转,而是比较两个链表,只要有一个元素不一样,就不是回文链表。

方法6:还是对4的改进,我们使用快慢指针 ,fast一次走两步,slow一次走一步。当fast到达表尾的时候,slow正好到达一半的位置,那么接下来可以从头开始逆序一半的元素,或者从slow开始逆序一半的元素,都可以。

方法7:假如使用递归法等等,我们还能相处更多的方法,但是这个没啥意义了。

上面这些解法中,方法1.即使你说了,面试官一般也不会让你写。方法2和3,如果能写出来,算及格,但是面试官问这个题一般就想考察你的链表操作能力,所以链表反转是绕不过去的问题。

但是这里我们尽量不要真的将原始链表给改了,在创建新链表的时候,采用头插法就行了,也就是每次都将要逆序的元素添加到新链表的head后面。

(1)快慢指针法

这个实现略有难度,主要是在while循环中pre.next = prepre;和prepre = pre;实现了一边遍历一遍将访问过的链表给反转了,所以理解起来有些难度,可以在学完链表反转之后再看这个问题。

public boolean isPalindrome(ListNode head) 
        if(head == null || head.next == null) 
            return true;
        
        ListNode slow = head, fast = head;
        ListNode pre = head, prepre = null;
        while(fast != null && fast.next != null) 
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
            //将前半部分链表反转
            pre.next = prepre;
            prepre = pre;
        
        if(fast != null) 
            slow = slow.next;
        
        while(pre != null && slow != null) 
            if(pre.val != slow.val) 
                return false;
            
            pre = pre.next;
            slow = slow.next;
        
        return true;
    

(2) 使用栈:全部压栈

将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较,只要有一个不相等,那就不是回文链表了。代码:

public boolean isPalindrome(ListNode head) 
    ListNode temp = head;
    Stack<Integer> stack = new Stack();
    //把链表节点的值存放到栈中
    while (temp != null) 
        stack.push(temp.val);
        temp = temp.next;
    
    //然后再出栈
    while (head != null) 
        if (head.val != stack.pop()) 
            return false;
        
        head = head.next;
    
    return true;

(3) 使用栈:部分压栈

改造上面的方法,先遍历第一遍,得到总长度。之后一遍历链表,一遍压栈。当到达链表长度一半的位置之后,就不再压栈,而是一边出栈,一遍遍历,一遍比较,只要有一个不相等,就不是回文链表。代码就是这样:

public boolean isPalindrome(ListNode head) 
    if (head == null)
        return true;
    ListNode temp = head;
    Stack<Integer> stack = new Stack();
    //链表的长度
    int len = 0;
    //把链表节点的值存放到栈中
    while (temp != null) 
        stack.push(temp.val);
        temp = temp.next;
        len++;
    
    //len长度除以2
    len >>= 1;
    //然后再出栈
    while (len-- >= 0) 
        if (head.val != stack.pop())
            return false;
        head = head.next;
    
    return true;

(4) 递归法

如果对链表逆序打印有没有简单的放呢?有的,而且这个本身就是一个可以考察的算法题,可以这样写:

private void printListNode(ListNode head) 
    if (head == null)
        return;
    printListNode(head.next);
    System.out.println(head.val);

也就是说最先打印的是链表的尾结点,是从后往前打印的,如果以后谁再给你说单向链表不能从后往前遍历,你就甩出这段代码。看到这里是不是有灵感了,我们来对上面的对面进行改造一下:

ListNode temp;
public boolean isPalindrome(ListNode head) 
    temp = head;
    return check(head);

private boolean check(ListNode head) 
    if (head == null)
        return true;
    boolean res = check(head.next) && (temp.val == head.val);
    temp = temp.next;
    return res;

上面的temp就是反转之后的链表。除此之外,还可以单独写个链表反转等方法,但是有点大材小用了,我们就不写了。

以上是关于六种方法判断链表是否为回文序列的主要内容,如果未能解决你的问题,请参考以下文章

左神算法书籍《程序员代码面试指南》——2_06判断一个链表是否为回文结构

Leecode 请判断一个链表是否为回文链表。

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

请判断一个链表是否为回文链表。

随手练——S(n)=O,判断一个链表是否为“回文”

判断一个链表是否是回文结构