• Leetcode 234. 回文链表(进阶)


    1.题目描述

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

    示例 1:

    输入: 1->2
    输出: false

    示例 2:

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

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

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

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

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

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

    3.解法二:(空间复杂度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;
        }
    };

    4.解法三:(进阶:空间复杂度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; } };

     参考资料:

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

  • 相关阅读:
    应用程序框架实战十三:DDD分层架构之我见
    Util应用程序框架公共操作类(三):数据类型转换公共操作类(扩展篇)
    Util应用程序框架公共操作类(二):数据类型转换公共操作类(源码篇)
    不能使用 float 和 double 来表示金额等精确的值
    JVM 字节码指令手册
    MyBatis: Invalid bound statement (not found)错误的可能原因
    Oracle:ORA-01219:database not open:queries allowed on fixed tables/views only
    手写 Spring MVC
    8080 端口被占用的解决方法 netstat -ano;taskkill (命令行)
    Java 工具类 IpUtil
  • 原文地址:https://www.cnblogs.com/paulprayer/p/9891019.html
Copyright © 2020-2023  润新知