链表15:回文链表的7种解法

Posted 纵横千里,捭阖四方

tags:

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

这是我们线性表专题的最后一篇了,加油!

LeetCode234题,请判断一个链表是否为回文链表。这个题不难,笔者面美团时第一轮就写了,我当时心中一阵窃喜,很快就完成了,结果面试官又给我加了一道。

示例1:

输入: 1->2输出: false

示例2:

输入: 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.快慢指针法

    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;}

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

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;}

3.递归法解决

我们知道,如果对链表逆序打印可以这样写:

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;}

其他几种方式主要是开拓思路的,我们就不写了,感兴趣的话,请读者自行尝试一下。

以上是关于链表15:回文链表的7种解法的主要内容,如果未能解决你的问题,请参考以下文章

刷题15:回文链表

《手撕链表题系列-7》链表的回文结构

LeetCode JavaScript实现 回文链表(回文字符串) 题型汇总(双指针解法)

链表常见的题型(java实现)

876.链表的中间结点(两种解法)

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