练题100天——DAY44:回文链表 ★★☆☆☆

今天是休息日,所以有时间练一道题。

最近也在学习数据库,之后会更新一个图书管理系统的完整实现。

题目

234. 回文链表 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表(回文序列是向前和向后读都相同的序列)。如果是,返回 true ;否则,返回 false

我的思路

将链表每个结点的val值存放到一个数组中,然后对数组检测其是否"回文"。

这道题最开始想到的是:用两个指针指向链表的头和尾,然后比较链表的头尾值,然后向中间移动指针,不相等返回false,两个指针相遇的过程一直都相等,则返回true,但是题目的链表为单向的,无法直接获取一个结点的前驱结点,所以我想到了将链表各结点的值存放的数组中,利用这个办法实现比较。

创建数组,需要知道长度,所以需要先遍历一次链表,获取结点数作为数组的长度。

然后再次遍历链表,将各结点的值存入到数组中

最后"遍历"数组,判断是否回文

代码
cpp 复制代码
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if (head->next == NULL) {
            return true;
        }
        ListNode* p = head;
        int len = 0;
        while (p != NULL) {
            len++;
            p = p->next;
        }
        vector<int> arr(len);
        p = head;
        for (int i = 0; i < len && p != NULL; i++) {
            arr[i] = p->val;
            p = p->next;
        }
        int left = 0, right = len - 1;
        while (left < right) {
            if (arr[left] != arr[right]) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

复杂度

n为链表的结点数

时间复杂度:O(n)。三次遍历,第一、二次都是遍历整个链表,时间复杂度为O(n),第三次算是遍历半个链表O(n/2),所以总的时间复杂度为O(n)+O(n)+O(n/2)=O(n)。

空间复杂度:O(n)。数组申请的空间为n。

官方题解

方法一:快慢指针

要想让空间复杂度为O(1),就需要对链表自身做处理,将链表分为前后两部分,因为无法直接获取结点的前驱,所以可以将后半部分链表反转,这样就可以直接比较两部分各个结点的值,比较完成后,将后半部分链表再反转回来即可,因为使用该函数的人并不希望链表结构被改变。

但是该方法也有缺点:在并发环境下,函数运行时需要锁定其他线程或进程对链表的访问,因为在函数执行过程中链表会被修改。

根据以上说明,算法流程可分为以下几步:

1.找到前半部分链表的尾结点

2.反转后半部分链表:

可以直接使用练题100天------DAY43:统计前后缀下标Ⅰ+反转链表-CSDN博客的函数

3.比较结点值,判断是否回文

4.恢复链表

5.返回结果

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

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

复杂度

n 指的是链表的大小

时间复杂度:O(n)。

空间复杂度:O(1)。只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)。

方法二:递归

这种方法相较于前面两种方法,更难理解。

这里也只给出代码,感兴趣的可以去官方题解看。

代码
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);
    }
};
相关推荐
仰泳的熊猫19 分钟前
题目1535:蓝桥杯算法提高VIP-最小乘积(提高型)
数据结构·c++·算法·蓝桥杯
闻缺陷则喜何志丹1 小时前
【前后缀分解】P9255 [PA 2022] Podwyżki|普及+
数据结构·c++·算法·前后缀分解
每天吃饭的羊1 小时前
时间复杂度
数据结构·算法·排序算法
yzx9910132 小时前
Python数据结构入门指南:从基础到实践
开发语言·数据结构·python
ValhallaCoder2 小时前
hot100-堆
数据结构·python·算法·
仰泳的熊猫6 小时前
题目1531:蓝桥杯算法提高VIP-数的划分
数据结构·c++·算法·蓝桥杯
刘琦沛在进步6 小时前
如何计算时间复杂度与空间复杂度
数据结构·c++·算法
m0_672703316 小时前
上机练习第30天
数据结构·算法
云淡风轻~窗明几净9 小时前
割圆术求Pi值的重新验证
数据结构·算法
盲盒Q9 小时前
《内存之茧》
数据结构·人工智能·ruby