【LeetCode】234. 回文链表

本文探讨了多种判断链表是否为回文的有效方法,包括列表转换、栈利用、递归及快慢指针技巧,特别关注O(n)时间与O(1)空间的解决方案。

问题描述

Given a singly linked list, determine if it is a palindrome.

Follow up:
Could you do it in O(n) time and O(1) space?

给定一个单链表,判断它是否是回文。
跟进:
你能在O(n)时间和O(1)空间内实现吗?

输入: 1->2
输出: false

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

Python 实现

实现一:将链表转换成列表(list),利用 Python 列表的特性,判断倒序的列表与原列表是否相等,从而判断链表是否为回文链表。此时,新建的 list 及其倒序的切片占用新的内存空间。

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        
        l = []
        while head != None:
            l.append(head.val)
            head = head.next
        if l == l[::-1]:
            return True
        else:
            return False

实现二:利用栈后进先出(LIFO)的特性。先遍历一次链表,把所有节点数值添加到栈中,再利用栈后入先出的特性,与原链表的节点数值比较,从而判断是否为回文链表。这种实现需要遍历两次链表,同时还要开拓新的内存空间储存栈。

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """

        stack = []
        tmp = head
        while tmp != None:
            stack.append(tmp.val)
            tmp = tmp.next
        cur = head
        while cur != None:
            if stack[-1] == cur.val:
                stack.pop()
                cur = cur.next
            else:
                return False
        return True

实现三:递归法。首先需要定义一个全局的变量 cur 来记录以比较的结点位置,对头结点调用递归函数,比较当前结点数值与 cur 指向的节点数值,相等则 cur 指向下一个值,结束当前函数调用,比较对象又返回到前一个结点,依次比较下去从而判断链表是否为回文链表。

从原理上,这是一个可行的实现思路。但是,递归调用的缺点仍然不能被避免,当链表长度过大时,多次递归调用容易导致栈溢出,运行时间过长。因此这并不是一个理想的选择。

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """

        self.cur = head
        return self.compare(head)
    
    def compare(self, node):
        if node == None:
            return True
        b = self.compare(node.next) and node.val == self.cur.val
        self.cur = self.cur.next
        return b

实现四:上述三种实现方法基本都是多次遍历了整个链表,在这过程中重复比较了一些结点。但判断回文链表只需要判断前半个链表和后半个链表对应值相等即可,所以我们可以尝试去找到链表的中点,这个过程可以用快慢指针的方法来实现。快指针走两步,慢指针走一步,慢指针同时把每次读到的数值存到一个栈中,当快指针遍历到链表末尾时,慢指针刚好遍历到链表中点。此时只需要将慢指针接下来读取到的数值,与栈的值进行比较即可。

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        
        if head == None or head.next == None:
            return True

        slow, fast = head, head
        stack = [slow.val]
        while fast.next != None and fast.next.next != None:
            slow = slow.next
            fast = fast.next.next
            stack.append(slow.val)

        if fast.next == None:
            stack.pop()  # Skip the middle val if the length of linked list is odd.
        while slow.next != None:
            slow = slow.next
            if slow.val == stack[-1]:
                stack.pop()
            else:
                return False
        return True

实现五:如果要实现 O(1) 的空间复杂度,那就不能使用 stack 相关的方法了,要替代 stack 后进先出的特性,我们可以在找到链表中点位置之后,将后半部分的链表翻转一下,就可以按照回文顺序进行比较了。这样子就是 in-place 的实现。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        
        if head == None or head.next == None:
            return True
        slow, fast = head, head
        while fast.next != None and fast.next.next != None:
            slow = slow. next
            fast = fast.next.next
        
        # Reverse the latter half linked list.
        last = slow.next
        while last.next != None:
            tmp = last.next
            last.next = tmp.next
            tmp.next = slow.next
            slow.next = tmp
        
        pre = head
        while slow.next != None:
            slow = slow.next
            if pre.val != slow.val:
                return False
            pre = pre.next
        return True
        

 

链接:https://leetcode.com/problems/palindrome-linked-list/

LeetCode 234回文链表有多种解法,以下为你介绍几种常见的解法: ### 解法一:将链表复制到数组里再从两头比对 将链表元素复制到数组中,然后使用双指针从数组的两端向中间遍历,比较对应元素是否相等。如果在遍历过程中发现不相等的元素,则链表不是回文链表;如果遍历完整个数组都没有发现不相等的元素,则链表回文链表。 ```cpp #include <vector> class Solution { public: bool isPalindrome(ListNode* head) { std::vector<int> listvec; // 把链表中元素都插入数组 while (head != nullptr) { listvec.push_back(head->val); head = head->next; } // 一个迭代器从头,一个迭代器从尾 auto it_head = listvec.begin(); auto it_back = listvec.end() - 1; // 若迭代器相遇了则说明都检查完了,可以返回true while (it_back - it_head > 0) { // 检查到值不相等就返回false if ((*it_head++) != (*it_back--)) return false; } return true; } }; ``` ### 解法二:递归法 递归地遍历链表,同时使用一个指针从链表头开始比较。递归过程中,先递归到链表的末尾,然后在回溯的过程中与链表头指针指向的元素进行比较。 ```cpp class Solution { ListNode* frontPointer; public: bool recursivelyCheck(ListNode* currentNode) { if (currentNode != nullptr) { if (!recursivelyCheck(currentNode->next)) { return false; } if (currentNode->val != frontPointer->val) { return false; } frontPointer = frontPointer->next; } return true; } bool isPalindrome(ListNode* head) { frontPointer = head; return recursivelyCheck(head); } }; ``` ### 解法三:快慢指针 + 反转链表 使用快慢指针找到链表的中点,然后将链表的后半部分反转,最后比较前半部分和反转后的后半部分是否相等。 ```cpp class Solution { public: bool isPalindrome(ListNode* head) { if (head == nullptr) return true; // 找到前半部分链表的尾节点并反转后半部分链表 ListNode* firstHalfEnd = endOfFirstHalf(head); ListNode* secondHalfStart = reverseList(firstHalfEnd->next); // 判断是否回文 ListNode* p1 = head; ListNode* p2 = secondHalfStart; bool result = true; while (result && p2 != nullptr) { if (p1->val != p2->val) result = false; p1 = p1->next; p2 = p2->next; } // 还原链表并返回结果 firstHalfEnd->next = reverseList(secondHalfStart); return result; } private: ListNode* reverseList(ListNode* head) { ListNode* prev = nullptr; ListNode* curr = head; while (curr != nullptr) { ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } return prev; } ListNode* endOfFirstHalf(ListNode* head) { ListNode* fast = head; ListNode* slow = head; while (fast->next != nullptr && fast->next->next != nullptr) { fast = fast->next->next; slow = slow->next; } return slow; } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值