Leetcode - Reverse Linked List

Question

Reverse a singly linked list.


Hint

A linked list can be reversed either iteratively or recursively. Could you implement both?


Java Code

//迭代法
public ListNode reverseList(ListNode head) {
    ListNode next = null;//原链表中头节点的后继节点
    ListNode newHead = null;//反转链表的头节点

    while(head != null) {
        next = head.next;//暂存原链表中头节点的后继节点
        head.next = newHead;//head即将成为反转链表的新头节点,将其后继节点指向反转链表当前的头节点
        newHead = head;//反转链表的头节点指向新的头节点
        head = next;//原链表的头节点指针移向其下一位,准备下一轮遍历
    }

    return newHead;
}

//递归法
public ListNode reverseList(ListNode head) {
    if(head == null || head.next == null) return head;//如果链表为空或已经到达链表尾节点
    ListNode newHead = reverseList(head.next);//先反转当前头节点的后继节点
    head.next.next = head;//再将当前节点的后继节点的后继节点指向自身
    head.next = null;//当前节点的后继节点置空(解除原链表的连接关系)
    return newHead;//返回反转链表的头节点
}

说明

  • 迭代法的解题思路应该是比较清晰的,由于所有的交换操作是在原链表中进行的,所以需要用几个变量来暂存转换过程中的节点指针(java中是引用),下面画个示意图简述一下这个过程

    假设原链表如下:

      1  ->  2  ->  3  ->  4  ->  null
    head 
    

    我们从头节点head开始,第1次while循环只是得到反转链表的头节点newhead,并把原链表的head指针移向下一位,得到

       1  ->  2  ->  3  ->  4  ->  null
    newhead  head
    

    第2次while循环,我们将head指向的节点从链表中抽取出来,添加到newhead之前,使之成为新的newhead,并将head指针下移一位,得到

       2  ->  1  ->  3  ->  4  ->  null
    newhead         head
    

    如此循环下去,直到head指向null,每一轮循环后newhead总是指向反转之后链表的头节点。

  • 递归法的代码非常简洁,但是算法思想相对复杂些,下面也画个示意图简述其过程

    假设原链表如下:

       1  ->  2  ->  3  ->  4  ->  null
     head
    

    我们按照递归函数的执行顺序来说明,函数中第一句是判断递归调用的终止条件,第二句是一个递归调用,我们先不进入这个递归内部,只看其执行完之后的效果,也就是说,执行完这句之后,head节点的所有后继节点都已经被反转了,且返回的newHead指向了反转之后的链表的头节点,即

       |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯↓
       1      4  ->  3  ->  2  ->  null
     head  newhead
    

    注意,此时节点1的next仍然指向节点2,因为我们对节点1(此时的head节点)没有做任何操作,继续执行下一句head.next.next = head; 我们得到

       |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯↓
       1      4  ->  3  ->  2
       ↑____________________|
     head  newhead
    

    也就是我们把head节点的后继节点的next指向了head自身,从而节点1和2之间互为父子,再执行下一句head.next = null; 我们得到

       4  ->  3  ->  2  ->  1  ->  null
    newhead
    

    也就是解除节点1和2之间原来的父-子关系,替换成反转之后的子-父关系。

    以上就是一次完整的递归调用。至于2->3->4->null如何反转成4->3->2->null则是下一层嵌套递归调用所需完成的任务,每次递归调用执行的操作都是和上面一样的方式。

    可能有同学会问,执行完一次递归之后,head指针指向哪里了,其实head指针已经通过head.next传递给其后继节点了,转到下一层递归之中去了。

    另外,我们需要注意的是,整个反转的操作是从原链表的尾节点开始的,而不是从头节点开始的(在本例中,就是先把3->4反转为4->3)。因为递归函数的首句就是递归调用(忽略第一句终止条件的判断),所以其后的语句必须等递归调用返回之后才能执行,递归调用会一层一层嵌套深入下去,直到终止条件判断为真,即已经到达原链表的尾节点时,递归才会依次返回,所以递归返回时才会依次从尾节点开始逆向地反转各个节点。这时会有一个奇怪的现象,head指针好像每次传递给了自身的父节点,这是因为递归调用栈具有LIFO的性质,本质上就是递归逐层深入时,先把head节点入栈,再把head的子节点入栈,,,而反转操作是在出栈时执行的,所以先执行子节点的反转,再执行父节点的反转,如果不使用递归或者栈,那么我们是无法逆向遍历单向链表的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值