Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
题目来源:LeetCode-回文链表
编写一个函数,检查输入的链表是否是回文的。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
二、思路分析:
思路一:
- 复制链表值到一个新的数组中
- 新建两个指针,一个指向数组头节点,一个指向数组末尾节点
- 循环判断两个指针指向的值是否相等,相等则next,不相等则return false
- 直到两个指针相遇,结束判断,返回true
思路二:
- 快慢指针求解
- 避免使用 O(n) 额外空间的方法就是改变输入。
- 将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较
- 比较完成后我们应该将链表恢复原样
- 具体流程:
找到前半部分链表的尾节点。(计算链表节点的数量,然后遍历链表找到前半部分的尾节点)
反转后半部分链表。
判断是否回文。(比较两个部分的值,当后半部分到达末尾则比较完成,可以忽略计数情况中的中间节点)
恢复链表。
返回结果。
三、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 个堆栈帧,计算机会逐个弹出进行处理。
所以在使用递归时空间复杂度要考虑堆栈的使用情况。