【LeetCode】206. 反转链表(简单)——代码随想录算法训练营Day03

题目链接:206. 反转链表

题目描述

给你单链表的头节点 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

文章讲解:代码随想录

视频讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

题解1:双指针

思路:一个指针指向当前节点,另一个指针指向上一个节点,用一个变量存储下个节点的位置,遍历链表的过程中反转链表。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    let pre = null, cur = head; // cur 指向当前节点,pre 指向上一个节点
    while (cur) {
        const temp = cur.next; // 临时存储下个节点位置
        cur.next = pre; // 反转当前节点
        pre = cur; // 更新 pre
        cur = temp; // 更新 cur
    }
    return pre; // pre 即为反转后链表的头节点
};

分析:遍历一次链表,时间复杂度为 O(n),空间复杂度为 O(1)。

题解2:双指针递归

思路:使用双指针法的思路,从左到右一边遍历一边反转链表。反转当前节点和上一个节点,再递归的反转下一个节点。

递归解析:

  • 函数功能:传入上一个节点和当前节点,反转整个链表。
  • 结束条件:当前节点指向 null,即已反转整个链表。
  • 递归关系:反转当前节点和上一个节点,继续递归的反转当前节点和下一个节点,即可反转整个链表。
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    const reverse = function(pre, cur) {
        // cur 指向当前节点,pre 指向上一个节点
        // cur 为空时,递归结束,返回 pre 为头节点
        if (cur === null) {
            return pre;
        }
        // cur 不为空时,执行反转操作
        const temp = cur.next; // 临时存储下个节点位置
        cur.next = pre; // 反转当前节点
        return reverse(cur, temp); // 执行函数反转链表的剩余部分
    };
    return reverse(null, head);
};

分析:递归的遍历了一次整个链表,同时调用了 n 层栈空间,时间复杂度为 O(n),空间复杂度为 O(n)。

题解3:暴力递归

思路:当链表为空链表或只有1个元素时,反转后的链表即为它本身。链表元素大于1个时,需要反转第1个元素和剩余部分反转后的链表的最后一个元素(即反转前链表的第2个元素)。

递归解析:

  • 函数功能:传入链表头节点,返回反转后的链表。
  • 结束条件:链表为空链表或只有1个元素,返回这个链表的头节点。
  • 递归关系:反转一个链表,先反转第2个节点即之后的部分(即除头节点外的部分),再反转头节点和剩余部分链表反转后的尾节点(即第2个节点),即可完成链表反转。
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    // 如果链表为空链表(即 head 指向 null)或只有一个元素,返回 head
    if (head === null || head.next === null) {
        return head;
    }

    // 递归反转第1个节点后的剩余链表
    const start = reverseList(head.next); // start 为反转后的链表的头节点

    // 反转第1个节点和第2个节点,第2个节点即反转后的链表的尾节点
    head.next.next = head;
    head.next = null;
    return start;
};

分析:递归的遍历了一次整个链表,同时调用了 n 层栈空间,时间复杂度为 O(n),空间复杂度为 O(n)。

收获

1. 学会了反转链表的多种方法,本质上是用双指针的思想,用两个指针分别指向当前元素和上一个元素,再使用临时变量存储下一个元素的位置。

2. 学会了使用递归的思想解决问题,题解2和题解3都是使用的递归的思想。区别在于题解2的思路是在正向的遍历链表的过程中同时反转链表,而题解3的思路是先遍历到链表尾,遍历途中在递归栈中存储每个节点的信息,再从链表尾部开始向头部反转链表。

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴雪月乔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值