LeetCode 234. 回文链表 【c++/java详细题解】

140 阅读1分钟

图片.png

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

1、题目

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1:

pal1linked-list.jpg

 输入:head = [1,2,2,1]
 输出:true

示例 2:

pal2linked-list.jpg

 输入:head = [1,2]
 输出:false

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

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

2、思路

(链表操作)

假设初始的链表是 L1→L2→L3→…→Ln。

image-20220116152034938.png

分两步处理:

  • 找到链表的中点节点,将其后半段的指针都反向,变成:L1→L2→L3→…→L⌈n/2⌉←L⌈n/2⌉+1←…←Ln;

image-20220116152243232.png

  • 然后用两个指针分别从链表首尾开始往中间扫描,依次判断对应节点的值是否相等,如果都相等,说明是回文链表,否则不是。

image-20220116152636661.png

  • 最后再将整个链表复原。

注意:

1、我们选取链表的中点节点为(n+1)/2(n+1)/2 下取整,n是链表的节点个数。

2、如果一个链表是奇数个节点(假设为5个节点),将其后半段翻转完后的链表为:

image-20220116153042336.png

3、如果一个链表是偶数个节点(假设为4个节点),将其后半段翻转完后的链表为:

image-20220116153153389.png

连接左右链表节点之间的指向是双向的

4、具体实现细节看代码

空间复杂度分析: 链表的迭代翻转算法仅使用额外 O(1)O(1) 的空间,所以本题也仅使用额外 O(1)O(1) 的空间。

时间复杂度分析: 整个链表总共被遍历4次,所以时间复杂度是 O(n)O(n)

3、c++代码

 /**
  * Definition for singly-linked list.
  * struct ListNode {
  *     int val;
  *     ListNode *next;
  *     ListNode() : val(0), next(nullptr) {}
  *     ListNode(int x) : val(x), next(nullptr) {}
  *     ListNode(int x, ListNode *next) : val(x), next(next) {}
  * };
  */
 class Solution {
 public:
     bool isPalindrome(ListNode* head) {
         int n = 0 ; //统计节点的个数
         for(ListNode *p = head ; p ; p = p->next) n++;
         if(n <= 1) return true; //节点数<=1的一定是回文链表
         //找到中点节点,由第一个节点跳(n+1)/2 -1步到达中点节点
         ListNode* a = head;
         for(int i = 0; i < (n+1)/2 - 1; i++) a = a->next; //a指针指向链表中点
         ListNode* b = a->next;  //b指针指向链表中点的下一个节点
         while(b) //将链表的后半段反向
         {
             ListNode* next = b->next; //保留b的next节点
             b->next = a;
             a = b, b = next;
         }6
         //此时a指向链表的尾节点,我们让b指向链表的头节点
         b = head;
         ListNode* tail = a; //保留一下尾节点
         bool res = true;
         for(int i = 0; i < n/2; i++) //判断是否是回文链表
         {
             if(b->val != a->val)
             {
                 res = false;
                 break;
             }
             b = b->next;
             a = a->next;
         }
         //将链表复原,后半段链表翻转
         //a指向尾节点,b指向a的下一个节点
         a = tail, b = a->next;
         for(int i = 0; i < n/2; i++)
         {
             ListNode* next = b->next;
             b->next = a;
             a = b , b = next;
         }
         tail->next = 0;
         return res;
     }
 };

4、Java代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    static ListNode reverse(ListNode head)
    {
        ListNode a = head,b = head.next;
        while(b != null)
        {
            ListNode c = b.next;
            b.next = a;
            a = b;
            b = c;
        }
        head.next = null;
        return a;
    }
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        ListNode slow = head, fast = head.next;
        while(fast.next != null && fast.next.next != null)
        {
            slow = slow.next;
            fast = fast.next.next;
        }
        //将右半部分的链表slow.next进行反转
        ListNode right = reverse(slow.next);
        ListNode left = head;
        slow.next = null;

        while(left != null && right != null)
        {
            if(left.val != right.val) return false;
            left = left.next;
            right = right.next;
        }
        return true;
    }
}

原题链接: 234. 回文链表