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

本文探讨了如何高效地在O(n)时间复杂度和O(1)空间复杂度下判断链表是否为回文,介绍了多种链表操作技巧,如快慢指针法、栈的应用以及链表反转策略。深入解析了利用pre和prepre指针实现链表反转的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

示例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就是反转之后的链表。除此之外,还可以单独写个链表反转等方法,但是有点大材小用了,我们就不写了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵横千里,捭阖四方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值