LeetCode-206-反转链表


题意描述:

反转一个单链表。
进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?


示例:

示例一:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

解题思路:
Alice: 我记得你在PAT那套题目里面的反转链表用了一种比较奇怪的方法写的。没有修改链表的指向关系,对吗 ?
Bob: 你这么一说我倒是想起来了,我是先把链表中所有的值都抠了下来,然后反转存储着这些值的列表,最后修改整个链表中节点的值,而不是节点之间的指向关系。
Alice: 这样倒是很好理解,就是空间复杂度有点高,需要先把所有的值都存一遍,然后再反转这些值的。
Bob: 可以直接遍历,修改指向关系的。 取三个变量吧,pre, node, after, 用 after 遍历整个链表, 每次循环修改 pre 和 node 之间的指向关系。然后 pre = node, node = after, after = after.next , 最后再修改 链表头部和尾部的指向关系,返回原来的链表尾结点就好了。
Alice: 现在想的倒是听明白的嘛,当初怎么没有想出来呢 ?题目中不是说还有进阶的做法可以用递归实现吗?
Bob: 我。。。┓( ´∀` )┏ 还是看题解吧。

------------------------------------------------------------------------------- Alice & Bob -------------------------------------------------------------------------------
Alice: 递归就是每次调用只修改两个节点之间的指向关系,然后要注意将原来的指向关系去掉,最后注意递归的终止条件应该是遇到原始链表的尾结点。别的应该就没了,看过别人的答案才觉得自己应该也能写出来的。
Bob: hhh, 我还看到了迭代解法的更好的版本,逻辑都是一模一样的,只是在代码实现上稍有差别,原来还可以写的更简洁一些。
Alice: 是哟,别人把两个 if 语句都拿掉了哦,应该会运行的更快吧。emmm,只需要 prevcurr 两个节点,每次修改 currprev 之间的指向关系。 prev 初始是 null , curr 初始是头结点,最后curr 指向null, prev 指向原始链表的尾节点,返回 prev 。这样 就能 handle 空链表和 只有一个节点的情况了,妙啊!!
Bob: 😎😎


代码:
Python 方法一: 存储链表中的值到列表,然后反转链表,不修改链表指向关系,修改链表总的值。空间复杂度O(n),时间复杂度O(n),好处就是简单,容易写。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        data = []
        node = head

        while node != None:
            data.append(node.val)
            node = node.next

        data.reverse()

        node  = head
        index = 0
        while node != None:
            node.val = data[index]
            index += 1
            node  = node.next
            
        return head

Python 方法二: 遍历整个链表, 修改节点之间的指向关系。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:

        if head == None:
            # 空链表
            return None

        if head.next == None:
            # 仅有一个元素的链表
            return head

        # if head.next.next == None:
        #     # 只有两个元素的链表
        #     ans = head.next
        #     head.next.next = head
        #     head.next = None
        #     return ans
        # 这一段可以和下面的代码合并
        
        pre   = head
        node  = head.next
        after = head.next.next
        # 初始化 pre node after 三个遍历

        while after != None:
            # 每次循环,修改 pre 和 node 之间的指向关系。
            node.next = pre
            pre   = node
            node  = after
            after = after.next
            # 实际上是用 after 在遍历整个链表

        node.next = pre
        head.next = None
        # 搞定链表末尾 和 头部的指向关系

        return node   

Java 方法二 : 遍历整个链表,修改节点之间的指向关系。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null){
            return null;
        }
        if(head.next == null){
            return head;
        }
        ListNode pre   = head;
        ListNode node  = head.next;
        ListNode after = head.next.next;
        while (after != null){
            node.next = pre;
            pre   = node;
            node  = after;
            after = after.next; 
        }
        node.next = pre;
        head.next = null;
        return node;
    }
}

Java 方法二: 遍历链表,修改指向关系。标准答案。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {

        ListNode prve = null;
        ListNode curr = head;
        // 每次遍历两个节点,修改 prve 和 curr 之间的指向关系。
        while (curr != null){
            ListNode next = curr.next;
            // 预先保留下一个节点
            curr.next = prve;
            prve = curr;
            curr = next;
        }
        return prve;
    }
}

Java 方法三:递归修改节点之间的指向关系。每次递归修改 head.next 和 head 之间的指向关系,同时要去除掉 head.next 和 head 之间最原始的指向关系,防止形成环形链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {

        if(head == null){
            // 处理边界值
            return null;
        }

        if(head.next == null){
            return head;
            // 遇到原始链表的尾部
        }else{
            ListNode end = reverseList(head.next);
            // 反转 以 head.next为头结点的链表
            // end 在递归栈中被接力传递,它的值就是 原始链表的尾结点

            head.next.next = head;
            // 修改 head  和 head.next 之间的指向关系。
            head.next = null;
            // 两个节点之间不能互相指着对方,不然就是环形链表了。
            
            return end;
        }
    }
}

易错点:

  • 一些测试样例:
[1,2,3,4,5]
[1]
[1, 2]
[]
  • 答案:
[5,4,3,2,1]
[1]
[2,1]
[]

总结:


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值