【数据结构】链表01:LeetCode 206.反转链表

1.题目

原题链接:LeetCode 206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

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

示例 3:

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

2.分析与题解

本题是最高频的面试题之一。

思路一:迭代

不妨设链表为1 -> 2 -> 3 -> null,最终目标是:把它反转为null <- 1 <- 2 <- 3,并返回头结点3。
迭代,意思就是遍历每一个节点,对每个节点都做相同的一系列操作,最终使链表反转。
那么,在迭代过程中,究竟要如何对每一个节点进行操作呢?

  1. 肯定要改变每个节点的next指针指向,从原来的指向下一个节点,到指向它的上一个节点(如果是第一个节点,就指向null)。
  2. 但是,对于当前节点的下一个节点,我们也要进行同样的操作,现在改变指向了,我们就找不到下一个节点的位置了,所以还要事先把下一个节点的位置存储起来。
  3. 这里的链表是单链表,不自带前驱节点,所以为了找到当前节点的的上一个节点,我们还要存储上一个节点的位置。
  4. 改变完next指针的指向后,我们就要移动到下一个节点,对其进行相同的操作了。注意,前驱节点和当前节点都要向后移动一个节点。而且必须先移动前驱节点,再移动当前节点,否则当前节点的位置会丢失。
//Java
//思路一:迭代
class Solution {
    public ListNode reverseList(ListNode head) {
        //迭代
        ListNode prev = null, curr = head, next;
        while(curr!=null){
            next = curr.next;//存储下一个节点的位置
            curr.next = prev;//改变指针方向
            prev = curr;//向后移动前驱节点
            curr = next;//向后移动当前节点
        }
        return prev;
    }
}

时间复杂度:O(N)
空间复杂度:O(1)

思路二:递归

先看代码:

//Java
//思路二:递归
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next==null) return head;//base case
        ListNode newHead = reverseList(head.next);//调用自身
        head.next.next = head;//修改2的指向关系
        head.next = null;//修改1的指向关系
        return newHead;
    }
}

递归,字面意思就是先递后归。
“递”,就是方法内部调用自身,一层层压栈。比如上面代码中,reverseList(ListNode head)方法内部调用了自身:

ListNode newHead = reverseList(head.next);

“归”,就是终于在某一次调用中,符合了方法最开头的条件:

if(head == null || head.next==null) return head;

根据题解代码可知,这次调用还没执行到调用自身的那一行,就返回了。也就是没有它再次调用自身,而是正常出栈了。这样,就引起了连锁反应,之前一层层调用自身的方法依次出栈,这便是“归”。

通过这个例子可知,写递归,要在代码的最开头写一个条件的判断,判断是不是结束“递”,开始“归”。这个条件就是所谓的“base case”。

然而,我们在思考递归的过程中,却尽量不要像实际上计算机的处理方式那样,一层一层地压栈,又一层一层地出栈,非得深入到递归关系内部。下面说一下什么才是正确的思考方式:
以示例1为例,刚输入时,链表是1 -> 2 -> 3 -> 4 -> 5 ->null,然后执行这一句调用自身:

ListNode newHead = reverseList(head.next);

reverseList(head.next)的含义是什么?反转以head.next为首节点的链表,并返回最新的头节点。以head.next为首节点的链表,也就是2 -> 3 -> 4 -> 5 ->null,反转之后就是null<- 2 <- 3 <- 4 <- 5 ,并返回新的首节点5。那么你就当作已经反转完成,不要跳进一层一层的调用里,那么现在的链表就是:

null<- 2 <- 3<- 4 <- 51

之后那两行代码就是修改一下1和2这两个节点的指向关系,最终的链表是这样的:

           2 <- 3 <- 4 <- 5null <- 1

也就是已经反转成功了,新的首节点就是刚才得到的那个newHead,返回即可。

时间复杂度:O(N)
空间复杂度:O(1)
递归方法和迭代方法的时间复杂度一样。但递归方法需要占用堆栈,所以空间复杂度是O(N)。所以相比递归操作链表来讲,迭代效率更高一些。

3.总结

本题到此结束,可以自己点击这里去LeetCode官网试一试。反转链表是面试中最高频的题目之一,迭代和递归的方法都应该熟练掌握,都比较简单。

我接下来马上会更新本题的进阶版:LeetCode 92. 反转链表 II 以及 LeetCode 25. K 个一组翻转链表,欢迎点赞、收藏、分享~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值