练题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);
    }
};
相关推荐
-Try hard-2 小时前
数据结构|概念及单向有头链表
数据结构·算法·vim
郝学胜-神的一滴2 小时前
Python List操作:+、+=、extend的深度解析
开发语言·数据结构·python·程序人生·架构·list
皮皮哎哟3 小时前
夯实基础:数据结构核心概念与线性表(顺序表&链表)C语言全解析 数据结构篇
c语言·数据结构·顺序表·单向链表·有头链表
蜡笔小马3 小时前
8.Packing 算法
数据结构·b树
划破黑暗的第一缕曙光3 小时前
[数据结构]:4.二叉树_堆
c语言·数据结构·二叉树·
浅念-3 小时前
C语言——双向链表
c语言·数据结构·c++·笔记·学习·算法·链表
轩情吖3 小时前
数据结构-图
数据结构·c++·邻接表·邻接矩阵·最小生成树·kruskal算法·prim算法
Prince-Peng3 小时前
技术架构系列 - 详解Redis
数据结构·数据库·redis·分布式·缓存·中间件·架构
码农水水4 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展