leetcode234.回文链表

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

示例 1:

输入: 1->2
输出: false

示例 2:

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

进阶:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindrome-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

完整代码

题目分析:
本道题的重点在于如何保证代码的空间复杂度为O(N),这就表明了解题过程中不能用栈等数据结构来暂存链表的节点。
刚开始看这道题目想到了逆转链表,但感觉有点麻烦,就没有继续尝试,后来看了评论区的讨论也是用到了链表逆转,果断尝试了这种方法。
基本思想:

  • 找到链表的中间节点
  • 将中间节点之后的链表逆转
  • 从头开始和逆转后的链表的每个节点一 一比较
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return true;
        //1.找到链表最中间的节点
        ListNode * slow = head, * fast = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
        }//循环结束后,slow指向最中间的节点(链表中有奇数个节点),或者回文的开始节点(链表中有偶数个节点)
        
        //2.将slow后面的链表逆转
        ListNode * cur = slow, *pre = NULL;
        while(cur){
            ListNode * next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }//循环结束后,pre指向逆转后的链表的头节点
        //3.判断是否是回文
        
        while(head != slow){
            if(head->val != pre->val)
                return false;
            head = head->next;
            pre = pre->next;
        }
        return true;
    }
};

参考:官方题解
解法一:
定义一个和链表长度相同的数组,定义两个指针,头指针,尾指针,比较头尾指针指向的元素是否相等。
时间复杂度O(N),空间复杂度O(N)
解法二:
递归
依然是将第一个元素和最后一个元素比较,借助递归可以实现链表的从后往前的比较,递归过程中将节点先放入栈中,当走到最后一个节点时,再进行比较。
时间复杂度O(N),空间复杂度O(n)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* firstNode = head;
        return recursive(head, firstNode);
    }
private:
    bool recursive(ListNode* head, ListNode* &firstNode){
        if(head != NULL){
            if(!recursive(head->next, firstNode))//目的是递归到最后一个节点,递归过程中,若是有不满足条件的直接返回false
                return false;
            if(head->val != firstNode->val)//比较两个节点的值是否相等
                return false;
            firstNode = firstNode->next;//在之前的节点都是回文 且当前节点相等的情况下,继续比较下一个
        }
        return true;
    }
};

上述第一种方法的优化
基本思想:
逆转前半部分链表,边寻找中间节点,边进行逆转。
还有一点需要说明,在判断链表是否是回文的时候将链表进行逆转了,这是使用该函数时不希望有的情况,可以在判断结束后,将改变后的链表再逆转回来。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return true;
        //1.寻找中间节点,同时逆转前半部分
        ListNode * slow = head, * fast = head, * pre = NULL, * cur;
        while(fast && fast->next){
            cur = slow;
            slow = slow->next;
            fast = fast->next->next;
            cur->next = pre;
            pre = cur;
        }
        //循环结束 ,pre指向逆转后的头节点(原链表前半部分的尾),slow指向后半部分节点
        
        //2.判断是否回文 
        ListNode * l_head = pre;
        ListNode * r_head = slow;
        if(fast)//链表中节点是奇数的情况
            r_head = r_head->next;
        while(l_head && r_head && l_head->val == r_head->val){
            l_head = l_head->next;
            r_head = r_head->next;
        }
        bool res = (l_head == NULL ? true : false);
        
        //3.将逆转后的链表还原
        ListNode * temp = pre, * next;
        pre = NULL;        
        while(temp){
            next = temp->next;            
            temp->next = pre;
            pre = temp;
            temp = next;
        }
        pre->next = slow;
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章