【leetcode】234. 回文链表( Palindrome Linked List )


题目描述

【leetcode】234. 回文链表( Palindrome Linked List )

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

示例 1:
输入: 1->2
输出: false

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

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

第一次解答

思路:
空间换时间,把head全复制到数组里,就可以两边往中间遍历了

注意:
空链表也是回文

test case:
[]
[1]
[1,2]
[1,2,2,1]

代码:

/**
 * 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(nullptr == head)
            return true;

        vector<int> values;
        while(nullptr != head){
            values.push_back(head->val);
            head = head->next;
        }

        int mid = ((int)values.size()-1) / 2;
        for(int i=0; i<=mid; ++i){
            if(values[i] != values[values.size()-i-1])
                return false;
        }
        return true;
    }
};

结果:

【leetcode】234. 回文链表( Palindrome Linked List )_第1张图片

第二次解答

为了达到进阶要求。
单链表难点在于,无法向数组那样从末尾反向迭代,在遍历链表前我们甚至不知道链表的长度。
参考之前反向链表那题,我们可以这样:

  1. 遍历一次链表,得到链表长度,时间复杂度O(n)。有了链表长度,可以计算出链表中间的那个元素位置。
  2. 遍历链表,遍历到中间那个元素后。
  3. 把剩下的部分链表反转,时间复杂度O(n)
  4. 遍历,由于链表后半部分已反转,所以可以头尾向中间遍历了,判断是否是回文。
  5. 最后,把后半部分已反转的链表恢复成原来未反转时的样子,时间复杂度O(n/2)即O(n)
    综上,总时间复杂度是O(n),空间复杂度O(1)

再进一步,上面第一第二步为了找到中间元素的指针,需要遍历2次;我们可以采用快慢指针的方法一次遍历得到中间元素指针,快指针一次递进2步,慢指针一次递进1步,快指针遍历完后,慢指针的位置就是中间元素。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseLink(ListNode* head){
        ListNode *pre = nullptr;
        while(nullptr != head){
            ListNode* temp = head->next;
            head->next = pre;
            pre = head;
            head = temp;
        }
        return pre;
    }
    bool isPalindrome(ListNode* head) {
        // 注意当节点个数为 0,1,2时的处理
        // 空链表也是回文
        if(nullptr == head)
            return true;

        // 快慢指针找中间节点,注意节点数分别为奇数和偶数时的不同
        // 节点个数为奇数时, slow指向中间节点
        // 节点个数为偶数时, slow指向两个中间节点中左边那个
        ListNode *p_slow = head;
        ListNode *p_fast = head;
        while(nullptr != p_fast->next && nullptr != p_fast->next->next){
            p_fast = p_fast->next->next;
            p_slow = p_slow->next;
        }

        // 从中间节点往后反转,注意左边链表节点数大于等于右边
        ListNode *p_head2 = reverseLink(p_slow->next);

        // 两头遍历,判断回文
        ListNode *p_head2_temp = p_head2;
        bool is_palindrome = true;
        while(is_palindrome && nullptr != p_head2_temp){
            if(head->val != p_head2_temp->val){
                is_palindrome = false;
            }
            head = head->next;
            p_head2_temp = p_head2_temp->next;
        }

        // 恢复链表结构
        reverseLink(p_head2);
        
        return is_palindrome;
    }
};

结果:
【leetcode】234. 回文链表( Palindrome Linked List )_第2张图片

相关/参考链接

官方题解 | 递归法、反转法和快慢指针反转法,都很有意思

你可能感兴趣的:(Leetcode)