【LeetCode刷题记录】21.回文链表

124 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

题目来源:LeetCode-回文链表

编写一个函数,检查输入的链表是否是回文的。

 

示例 1:

输入: 1->2

输出: false

示例 2:

输入: 1->2->2->1

输出: true  

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

二、思路分析:

思路一:

  1. 复制链表值到一个新的数组中
  2. 新建两个指针,一个指向数组头节点,一个指向数组末尾节点
  3. 循环判断两个指针指向的值是否相等,相等则next,不相等则return false
  4. 直到两个指针相遇,结束判断,返回true

思路二:

  1. 快慢指针求解
  2. 避免使用 O(n) 额外空间的方法就是改变输入。
  3. 将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较
  4. 比较完成后我们应该将链表恢复原样
  5. 具体流程:

找到前半部分链表的尾节点。(计算链表节点的数量,然后遍历链表找到前半部分的尾节点)

反转后半部分链表。

判断是否回文。(比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点)

恢复链表。

返回结果。

三、AC 代码:

思路一:

    class Solution {
        public boolean isPalindrome(ListNode head) {
            List<Integer> values = new ArrayList<Integer>();

            ListNode currentNode = head;
            while (currentNode != null) {
                values.add(currentNode.val);
                currentNode = currentNode.next;
            }

            int headPointer = 0;
            int backPointer = values.size() - 1;
            while (headPointer < backPointer) {
                if (!values.get(headPointer).equals(values.get(backPointer))) {
                    return false;
                }
                headPointer++;
                backPointer--;
            }
            return true;
        }
    }

时间复杂度:O(n)

思路二:

    class Solution {
        public boolean isPalindrome(ListNode head) {
            if (head == null) return true;

            ListNode firstHalfEndNode = endOfFirstHalfNode(head);
            ListNode secondHalfStartNode = reverseList(firstHalfEndNode.next);

            ListNode pointerNode1 = head;
            ListNode pointerNode2 = secondHalfStartNode;
            boolean result = true;
            while (result && pointerNode2 != null) {
                if (pointerNode1.val != pointerNode2.val) result = false;
                pointerNode1 = pointerNode1.next;
                pointerNode2 = pointerNode2.next;
            }
            firstHalfEndNode.next = reverseList(secondHalfStartNode);
            return result;
        }

        private ListNode reverseList(ListNode head) {
            ListNode previousNode = null;
            ListNode currentNode = head;
            while (currentNode != null) {
                ListNode nextTempNode = currentNode.next;
                currentNode.next = previousNode;
                previousNode = currentNode;
                currentNode = nextTempNode;
            }
            return previousNode;
        }

        private ListNode endOfFirstHalfNode(ListNode head) {
            ListNode fastNode = head;
            ListNode slowNode = head;
            while (fastNode.next != null && fastNode.next.next != null) {
                fastNode = fastNode.next.next;
                slowNode = slowNode.next;
            }
            return slowNode;
        }
    }

时间复杂度:O(n)

四、总结:

计算机如何运行递归函数:在一个函数中调用一个函数时,计算机需要在进入被调用函数之前跟踪它在当前函数中的位置(以及任何局部变量的值),通过运行时存放在堆栈中来实现(堆栈帧)。

在堆栈中存放好了数据后就可以进入被调用的函数。在完成被调用函数之后,他会弹出堆栈顶部元素,以恢复在进行函数调用之前所在的函数。

在进行回文检查之前,递归函数将在堆栈中创建 nn 个堆栈帧,计算机会逐个弹出进行处理。

所以在使用递归时空间复杂度要考虑堆栈的使用情况。