Leetcode 234. 回文链表(进阶)

题目描述:

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

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

进阶:

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

解法一:(空间复杂度O(n))

遍历一遍链表压栈,借助栈把链表倒序,然后依次比较“原链表元素”和“新栈中元素”,如果都相等则返回true,否则返回false。

这样简单粗暴,代码的主体包含在解法二中了,这里不列出了。

另外,这种解法的时间要求能不能通过Leetcode的测试,我没有试过,因为觉得没必要试。

解法二:(空间复杂度O(n/2))

解题思路:使用两个指针,fast和slow指针。

(1)fast指针每次走两步slow指针每次走一步

(2)fast指针走到链表末尾的时候,slow指针走到链表的中间位置结点(链表长度n为偶数)或中间位置的前一个结点(链表长度n为奇数);

(1)——>(2)——>(3)——>(2)——>(1)
               slow           fast
(1)——>(2)——>(3)——>(3)——>(2) ——>  (1)
               slow        (fast)  多走1步 fast

  

(3)slow直接到了中间,就可以将整个链表的后半部分压栈实现逆序,依次和前半部分比较,思路同解法一。

注:就是在这里,额外的时间复杂度减少了n/2,因为只需要将链表中一半的元素压栈。

其他的细节,代码里有详细注释。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

static const auto __ = []() {
    // turn off sync
    std::ios::sync_with_stdio(false);
     // untie in/out streams
    std::cin.tie(nullptr);
    return nullptr;
}();

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        //额外空间复杂度O(n/2)

        /*使用两个指针,fast指针每次走两步,slow指针每次走一步;fast指针走到链表末尾的时候,
        slow指针走到链表的中间位置结点(链表长度n为偶数)或中间位置的前一个结点(链表长度n为奇数)
        */

        //判空
        if(head == NULL) return true;
        //单节点链表
        if(head->next == NULL) return true;

        ListNode* fast = head;
        ListNode* slow = head;//指向第一个结点

        //fast指针指向末尾结点,slow指针指向中间位置结点或中间位置的前一个结点

        ////注意:这里的结束判断主要看fast!!!
        while(fast->next != NULL && fast->next->next != NULL )
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //链表长度为偶数,fast指针最后多走一步到链表末尾
        if(fast->next)
            fast = fast->next;

         stack<int> s;
        //将链表后半部分元素压栈,通过栈来实现逆序
        while(slow->next)
        {
            s.push(slow->next->val);
            slow = slow->next;
        }

        //依次比较前半部分元素和逆序的后半部分元素
        while(!s.empty())
        {
            if(s.top() != head->val)
                return false;
            //前、后一起往后移动
            s.pop();
            head = head->next;
        }
        return true;
    }
};

解法三:(进阶:空间复杂度O(1))

 解题思路:解法三和解法二的区别在于,最后不使用栈来倒序链表后半部分的元素,而是选择直接本地操作(额外空间复杂度为O(1)),在原链表上将后半部分元素倒置(反转),比较完后得出结果后,再 还原链表,返回结果。

代码中有详细注释。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

static const auto __ = []() {
    // turn off sync
    std::ios::sync_with_stdio(false);
     // untie in/out streams
    std::cin.tie(nullptr);
    return nullptr;
}();

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        //额外空间复杂度O(1)

        //判空
        if(head == NULL) return true;
        //单节点链表
        if(head->next == NULL) return true;
        //双节点链表
        if(head->next->next == NULL)
            if(head->val == head->next->val)
                return true;
            else
                return false;

        ListNode* fast = head;
        ListNode* slow = head;//指向第一个结点

        //fast指针指向末尾结点,slow指针指向中间位置结点或中间位置的前一个结点

        ////注意:这里的结束判断主要看fast!!!
        while(fast->next != NULL && fast->next->next != NULL )
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //链表长度为偶数,fast指针最后多走一步到链表末尾
        if(fast->next)
            fast = fast->next;

        //-----区别在这里,元素不压栈,直接将链表后半部分元素逆序,比较完后得出结果后,再还原链表,返回结果--------//

        //---------------链表的后半部分元素“倒序”-------------------//
        ListNode* p = slow->next;
        ListNode* q = NULL;
        ListNode* cur = NULL;

        slow->next = NULL;
        while(p)
        {
            cur = p->next;
            p->next = q;
            q = p;
            p = cur;
        }

       //依次比较 前半部分元素 和 逆序的后半部分元素
        while(1)
        {
            if(fast->val != head->val)
            {
                //链表复原
                ListNode* m = q->next;
                ListNode* n = NULL;
                ListNode* cur2 = NULL;
                q->next = NULL;
                while(m){
                    cur2 = m->next;
                    m->next = n;
                    n = m;
                    m = cur2;
                }
                slow->next = n;

                return false;
            }

            //前、后一起往后移动
            fast = fast->next;
            head = head->next;
                        //--------在这里判断结束,是调试的结果----//            //针对这种情况:(3)——>(1)——>(2)——>(3),后半部分反转之后的链表是(3)——>(1)——>(3)——>(2)              // 如果使用while(head->next)作为结束,会少比较一次,也就是最后(1)和(2)不会比较到,从而出错
            if(fast == NULL)
                break;
        }

         //链表复原
        ListNode* m = q->next;
        ListNode* n = NULL;
        ListNode* cur2 = NULL;
        q->next = NULL;
        while(m){
            cur2 = m->next;
            m->next = n;
            n = m;
            m = cur2;
         }
        slow->next = n;

        return true;
    }
};

参考资料:

https://blog.csdn.net/blioo/article/details/62050967 单向链表反转(倒置)问题

原文地址:https://www.cnblogs.com/paulprayer/p/9891019.html

时间: 2024-08-07 05:15:55

Leetcode 234. 回文链表(进阶)的相关文章

LeetCode 234——回文链表

1. 题目 请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 2. 思路 此题可以看做是反转链表 和 链表中间结点 的结合. 定义快慢两个指针,寻找中间结点,同时在慢指针移动的过程中反转前半部分子链表.当找到中间结点时,再分别向前向后比较前后两个子链表的每一个结点值是否相同. 偶数结点情况如下 此时,我们分别得到了以

Leetcode 234. 回文链表

bool isPalindrome(ListNode* head) { ListNode* fast = head; ListNode* slow = head; //如果没有元素 if(head==NULL) return true; //如果就一个元素 if(fast->next==NULL) return true; //如果就两个元素 if(fast->next->next==NULL) return fast->val == fast->next->val;

领扣(LeetCode)回文链表 个人题解

请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 一个最暴力的做法,遍历一次,内容保存在数组内,然后判断是否回文. 遇到一个比较严重的问题需要记录一下,使用Vector类时,设定的类型为Integer,必须使用Equals函数来判断两数是否相等,使用==则不行.奇怪的是,只有部分判断是错误的. 查阅百度,发现了问题所在:

234. 回文链表

请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 1 import java.util.ArrayList; 2 3 public class PalindromeLinkedList { 4 static class ListNode { 5 int val; 6 ListNode next; 7 ListNode(int x) { 8 val = x; 9 } 10 } 11 12 //

234. 回文链表 Palindrome Linked List

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? 判断一个链表是否为回文串 思路:1.找到中间点,2.反转后半部分链表,3.判断前半部分与后半部分是否相同 /** * Definition for singly-linked list. * public class ListNode { * public int v

【Leetcode链表】回文链表(234)

题目 请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 解答 两种方法: 遍历链表,用数组存值,再比较.时间复杂度O(n),空间复杂度O(n) 指针法:找到中点,反转中点之后的链表,再比较.时间复杂度O(n),空间复杂度O(1) 通过代码如下: # Definition for singly-linked list.

【leetcode 简单】 第六十七题 回文链表

请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true 进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def r

如何判断一个单向链表是否为回文链表(Palindrome Linked List)

题目:给定一个单向链表,判断它是不是回文链表(即从前往后读和从后往前读是一样的).原题见下图,还要求了O(n)的时间复杂度O(1)的空间复杂度. 我的思考: 1,一看到这个题目,大脑马上想到的解决方案就是数组.遍历链表,用数组把数据存下来,然后再进行一次遍历,同时用数组反向地与之比较,这样就可以判断是否回文.这个方法时间复杂度是O(n),达到了要求,然而空间复杂度显然不满足要求.所以,开数组这一类的方法显然不是最佳的. 2,既然要满足O(1)的空间复杂度,我就想到了用一个变量来存储这些数据,恰好

[CareerCup] 2.7 Palindrome Linked List 回文链表

2.7 Implement a function to check if a linked list is a palindrome. LeetCode上的原题,参见我之前的博客Palindrome Linked List 回文链表.