LeetCode 234.回文链表的四种解法

题目概述

在这里插入图片描述
链接:点我做题

题解

一、数组接收链表值的前后指针法

  单链表不支持随机访问,也不像双向循环链表一样支持 O ( 1 ) O(1) O(1)的回溯操作,所以我们可以先用一个vector容器遍历一遍链表,把链表的值都放到数组中,然后用判断数组是否为回文数组的前后指针法,直接ko。

class Solution {
public:
    bool isPalindrome(ListNode* head) 
    {
        if (head->next == nullptr)
        {
            return true;
        }
        vector<int> vec;
        ListNode* cur = head;
        while (cur)
        {
            vec.push_back(cur->val);
            cur = cur->next;
        }
        int left = 0;
        int right = vec.size() - 1;
        while (left < right)
        {
            if (vec[left] != vec[right])
            {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

二、利用栈“翻转”链表顺序

  好吧,多少有点标题党,其实这个方法并没有真正的去逆置链表,而是利用栈这种数据结构"先进后出"的特点,先遍历一遍链表,让每个结点依次入栈,然后再遍历一遍链表,此时链表顶的节点就是最后一个结点,如果这个节点的值不等于当前遍历的链表的结点位置的值,就返回false,否则就pop掉这个节点,遍历指针进入链表的下一个位置,如果能走出循环,则返回true。

class Solution {
public:
    bool isPalindrome(ListNode* head) 
    {
        if (head->next == nullptr)
        {
            return true;
        }
        stack<int> st;
        ListNode* cur = head;
        while (cur)
        {
            st.push(cur->val);
            cur = cur->next;
        }
        cur = head;
        while (cur)
        {
            int val = st.top();
            st.pop();
            if (val != cur->val)
            {
                return false;
            }
            cur = cur->next;
        }
        return true;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

三、利用递归判断回文链表

  不妨这样思考,既然栈可以实现判断回文链表,那么用递归也一定可以实现,因为递归本身就是利用函数栈帧的建立和销毁来实现的,它本身就是一种栈算法,仿照栈的思路,我们设置一个全局变量 f r o n t p o i n t e r frontpointer frontpointer从head开始走,一个局部变量 h e a d head head表示当前栈帧中的结点位置,先通过建立栈让 h e a d head head走到链表尾巴,当 h e a d head head走到 n u l l p t r nullptr nullptr后,返回true到上一层,这时就返回了到了倒数第一个结点,此时 f r o n t p o i n t e r frontpointer frontpointer指向正数第一个结点,然后检查 f r o n t p o i n t e r − > v a l frontpointer->val frontpointer>val h e a d − > v a l head->val head>val,如果不相等就返回false,如果相等就让 f r o n t p o i n t e r = f r o n t p o i n t e r − > n e x t ; frontpointer = frontpointer->next; frontpointer=frontpointer>next;,然后向上一层返回true,思路大概如此,我们照着代码讲解。
代码:

class Solution {
public:
    ListNode* frontpointer;
    //此指针作为全局变量 来从头遍历链表
    bool isPalindrome(ListNode* head) 
    {
        frontpointer = head;
        return CheckList(head);
    }
    bool CheckList(ListNode* head)
    {
        //如果head不为空 就往前走
        if (head)
        {
            //利用这里同时做到了让head走到链表尾
            //和如果上一层返回false
            //这一层也返回false 表示不是回文链表
            if (!CheckList(head->next))
            {
                return false;
            }
            //检查head->val和frontpointer->val
            //如果不相等 在这里就向上一层返回false
            if (head->val != frontpointer->val)
            {
                return false;
            }
            //没在上面返回的话 说明这一层通过测验
            //应该让全局变量frontpointer往前走一步
            frontpointer = frontpointer->next;
        }
        //同时完成了往前走一步后向上一层返回true
        //和head走到空了 向上一层返回true
        return true;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n),建立了n数量级的栈帧

四、快慢指针找中点+逆置链表判回文

  终于来到了一个空间复杂度是 O ( 1 ) O(1) O(1)的算法了,思路很简单,首先利用快慢指针法,找到链表的中点,然后去逆转链表中点mid后的部分,即逆转 m i d − > n e x t mid->next mid>next,并且返回逆转后的头结点用check和cure接收,然后cur从链表头开始走,check也开始走,如果 c h e c k − > v a l ! = c u r − > v a l check->val != cur->val check>val!=cur>val,说明不是回文链表,就终止遍历,如果相等则继续,直到check走到nullptr为止。然后修补原链表,就是把cure链表逆置,然后用 m i d − > n e x t mid->next mid>next去接收返回值以修补链表.
  逆置链表据我所知有两种方法:一种是原地逆置法,需要需要逆置的结点 c u r cur cur和它的前置结点 p r e v prev prev,对链表头,其前置结点就设为 n u l l p t r nullptr nullptr,然后先以局部变量next存储起来 c u r − > n e x t cur->next cur>next,然后修改 c u r − > n e x t cur->next cur>next为prev,然后 p r e v = c u r ; c u r = n e x t prev = cur; cur = next prev=cur;cur=next走到下一结点进行逆置;另一种方法是头插法,我们创建一个新链表,其头结点为 n e w h e a d newhead newhead,初始时设置为 n u l l p t r nullptr nullptr,然后不断把新节点头插入当前链表,就实现了逆置链表。
代码:

class Solution {
public:
    bool isPalindrome(ListNode* head) 
    {
        //边界条件
        if (head->next == nullptr)
        {
            return true;
        }
        //快慢指针找中点
        ListNode* fast = head;
        ListNode* slow = head;
        while (fast != nullptr 
        && fast->next != nullptr 
        && fast->next->next != nullptr)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* mid = slow;
        ListNode* check = reverseList(mid->next);
        ListNode* cure = check;
        bool ret = true;
        ListNode* cur = head;
        while (check)
        {
            if (check->val != cur->val)
            {
                ret = false;
                break;
            }
            check = check->next;
            cur = cur->next;
        }
        mid->next = reverseList(cure);
        return ret;
    }
    ListNode* reverseList(ListNode* head)
    {
        //遍历一遍链表 把结点头插进新链表就是逆置了
        ListNode* newnode = nullptr;
        ListNode* cur = head;
        while (cur)
        {
            ListNode* next = cur->next;
            cur->next = newnode;
            newnode = cur;
            cur = next;
        }
        return newnode;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值