leetcode
leetcode 151 ~ 200
反转链表

反转链表

难度:

标签:

题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

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

示例 3:

输入:head = []
输出:[]

 

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

 

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

代码结果

运行时间: 36 ms, 内存: 16 MB


/*
 * Problem: Reverse a singly linked list using Java Stream API.
 * Approach: Java Streams are not typically used for linked list manipulation, but we can convert the list to a stack and then to a new list.
 */
 
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
 
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}
 
public class Solution {
    public ListNode reverseList(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        ListNode curr = head;
        while (curr != null) {
            stack.push(curr.val); // Push all elements to the stack
            curr = curr.next;
        }
        ListNode newHead = new ListNode(0); // Dummy head
        ListNode newCurr = newHead;
        while (!stack.isEmpty()) {
            newCurr.next = new ListNode(stack.pop()); // Pop from stack to new list
            newCurr = newCurr.next;
        }
        return newHead.next; // Return the new head
    }
}

解释

方法:

这个题解使用了迭代的方式来反转链表。具体做法是:用两个指针cur和pre分别指向当前节点和前一个节点。每次迭代时,先用tmp指针保存cur的下一个节点,然后将cur的next指针指向pre,完成当前节点的反转。接着把pre和cur分别往后移动一个节点,继续下一轮迭代,直到cur变为None时结束。最后pre指针就指向了新的链表头。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在迭代方法中,如果链表为空或只有一个节点,这种情况下算法的行为是怎样的?
如果链表为空(即head为None),则cur也将为None。由于循环的条件是'while cur',循环不会开始执行,因此函数直接返回pre,此时pre也是None,这与输入的空链表相符。如果链表只有一个节点,迭代会开始,但只进行一次。在这次迭代中,tmp保存cur.next(即None),cur.next被设置为pre(也是None)。然后pre更新为cur(指向这唯一的节点),cur更新为tmp(None)。循环结束,此时pre指向反转后的链表头,即原链表的唯一节点。因此,算法能正确处理长度为0或1的链表。
🦆
为什么在反转链表时选择用迭代方法而不是递归方法?这样选择的优势在哪里?
迭代方法相较于递归方法的优势主要在于空间复杂度。迭代方法只用到了几个节点指针,空间复杂度为O(1),而递归方法由于递归调用会不断增加调用栈,其空间复杂度为O(n),n是链表的长度。此外,迭代方法通常对初学者来说逻辑更直观,易于调试和理解。递归方法虽然代码更简洁,但在处理较长的链表时可能会导致栈溢出。
🦆
迭代反转链表时,pre、cur和tmp三个指针的具体作用和相互关系是什么?
在迭代反转链表的过程中,'pre'指针用于保存已经反转部分的最后一个节点,'cur'指针指向当前正在处理的节点,'tmp'是一个临时指针,用来保存下一个待处理的节点(即cur.next),防止链表断裂。迭代的每一步中,首先用tmp保存cur.next,然后将cur.next指向pre(实现反转),接着将pre移动到cur,最后将cur移动到tmp。这样,pre逐渐成为新链表的头部,cur遍历整个原链表。
🦆
在迭代过程中,如何确保所有节点都被正确反转,特别是最后一个节点的反转?
在迭代过程中,每次循环都会将当前节点cur的next指针指向前一个节点pre,从而实现节点的反转。对于最后一个节点,由于在迭代开始时已经将cur设置为头节点,因此最后一个节点也会在迭代中被处理到。当处理到最后一个节点时,cur.next将被设置为pre,完成最后一个节点的反转,然后pre更新为最后一个节点。此时cur更新为None,循环结束。函数最后返回pre,此时pre指向新的链表头,即原链表的最后一个节点。这样确保了链表中的所有节点都被正确反转。

相关问题

反转链表 II

给你单链表的头指针 head 和两个整数 leftright ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

 

示例 1:

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]

 

提示:

  • 链表中节点数目为 n
  • 1 <= n <= 500
  • -500 <= Node.val <= 500
  • 1 <= left <= right <= n

 

进阶: 你可以使用一趟扫描完成反转吗?

上下翻转二叉树

上下翻转二叉树

回文链表

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

 

示例 1:

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

示例 2:

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

 

提示:

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

 

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