题目描述

示例 1:

输入: 1->2
输出: false 

示例 2:

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

进阶:

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

解题思路与代码

  • 这是一道有关链表操作的题,不算特别的难。考到了链表的几个基础的操作,像是反转链表,创建一个新的链表,找到链表的中间节点,用什么方式去找。
  • 所以,需要你对链表的基础操作有一定的理解,如果你对链表的操作了熟于心的话,这道题真的是简单的不在话下,但是如果对链表只是基本了解的话,这道题还是有一丢丢难度的。

方案一:构造一条新链表(原链表的反转)之后对比

  • 这里有一点十分需要注意的是:这里的反转链表,不是在原链表上进行反转操作,而是创建一个新链表,新链表的内容是反转后的原链表,之后我们才可以继续对比操作。
  • 很多新手,或者说对链表不熟悉的人来说,很容易就 ListNode * prev = head; 开始准备反转链表了,殊不知它们已经修改了原链表的内容了,拿着修改后的原链表与自己做对比,那不就100%会出错嘛~

具体的代码如下:

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        // 创建前驱节点,并且为重新创建一条新的链表(节点反转后的链表)做准备
        ListNode * tail = head;
        ListNode * newHead = nullptr;
        // 创建一条链表(节点反转后的链表)
        while(tail){
            ListNode * temp = new ListNode(tail->val);
            temp->next = newHead;
            tail = tail->next;
            newHead = temp;
        }

        // 检查两条链表是否相等,若相等则返回true,否则false
        while(newHead) {
            if(newHead->val != head->val) return false;
            head = head->next;
            newHead = newHead->next;
        }
        
        return true;
    }
};

《程序员面试金典(第6版)》面试题 02.06. 回文链表(双指针(快慢指针),查找链表中间节点,反转链表)-LMLPHP

复杂度分析

时间复杂度:
该代码包含两个循环。第一个循环遍历整个链表以创建一个反向链表。第二个循环遍历链表以比较原始链表和反向链表的节点。因此,总的时间复杂度为O(n) + O(n) = O(2n),在大O表示法中,我们忽略常数,所以时间复杂度为O(n)。

空间复杂度:
在创建反向链表的过程中,为每个节点分配了新的内存,所以空间复杂度为O(n),其中n为链表的长度。

方案二:优化,快慢指针!

  • 这个方案,我们使用快慢指针的方式,先找到这个链表的中间节点,紧接着,反转后半部分的链表,之后就进行对比就可以了。
  • 由于是在原链表上面进行操作,空间复杂度可以降低到O(1)
  • 快慢指针找到中间节点的好处是,不用去管奇数还是偶数节点。如果你还不理解的话,请你在纸上画一画草图,离开就能出来了。

具体的代码如下:

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == nullptr || head->next == nullptr) return true;
        ListNode * slow = head;
        ListNode * fast = head;
        // 找到中间节点
        while(fast->next != nullptr && fast->next->next != nullptr){
            slow = slow->next;
            fast = fast->next->next;
        }
        // 反转后半链表
        ListNode * prev = nullptr;
        while(slow != nullptr){
            ListNode * temp = slow->next;
            slow->next = prev;
            prev = slow;
            slow = temp;
        }

        // 开始逐一对比
        while(head != nullptr && prev !=nullptr){
            if(head->val != prev->val) return false;
            head = head->next;
            prev = prev->next;
        }
        return true;
    }
};

《程序员面试金典(第6版)》面试题 02.06. 回文链表(双指针(快慢指针),查找链表中间节点,反转链表)-LMLPHP

复杂度分析

时间复杂度:

  • 这段代码包含三个主要步骤:找到链表的中点,反转链表的后半部分,然后比较两个半链表。这三个步骤都需要遍历链表,所以总的时间复杂度是 O(n) + O(n) + O(n) = O(3n)。但在大O表示法中,我们会忽略常数,所以最终的时间复杂度是O(n)。

空间复杂度:

  • 这段代码并没有使用额外的空间来存储链表的节点。所有的操作都是在原来链表的基础上完成的,所以空间复杂度是O(1)。

总结

对于这道题的实际意义,可以从以下几个方面理解:

  • 对数据结构的理解和操作:链表是一种基础且常用的数据结构,理解和掌握链表的操作是非常重要的。这道题测试了你对链表的理解和操作能力。

  • 算法设计:这道题目需要你设计一个算法来解决问题。这考察了你的问题解决能力,包括如何将问题分解,如何设计算法,以及如何实现算法。

  • 空间复杂度优化:题目进阶部分要求使用 O(1) 的空间复杂度来解决问题,这就需要你考虑如何在不增加额外空间的情况下解决问题。这是对你优化代码和理解空间复杂度的一个测试。

  • 时间复杂度的理解:题目要求算法的时间复杂度为 O(n),这就需要你保证你的算法在最坏的情况下也能在线性时间内完成。

  • 实际应用:在实际的软件开发中,我们可能会遇到需要判断一个序列是否为回文的情况,例如检测字符串是否为回文。此题就可以作为实现该功能的一种思路。

所以,这道题目的主要意义在于锻炼和测试你的数据结构操作能力,算法设计能力,以及你对时间和空间复杂度的理解。同时,也可以帮助你在实际问题中更好地应用这些技巧和知识。

最后的最后,如果你觉得我的这篇文章写的不错的话,请给我一个赞与收藏,关注我,我会继续给大家带来更多更优质的干货内容

05-23 12:10