LeetCode刷题:反转链表

leetCode真题

206. 反转链表

属于基础简单题目

常见的做法有递归和while循环
在这里插入图片描述

递归

    // 1. 递归参数和返回值
    public static ListNode reverseList(ListNode head) {
        // 1. 递归终止条件
        if (head == null || head.next == null) {
            return head;
        }

        // 递归逻辑
        ListNode last = reverseList(head.next);// last是未反转链表的最后一个节点
        head.next.next = head;// head和last构成双向
        head.next = null;// head的向last的指向取消
        return last;
    }

不要用自己的大脑想象递归过程,人的大脑记不住几个栈!

这段代码我们要分析其中的实现逻辑

  1. 我们要反转整个链表(head节点),就要先反转链表的子链表(head.next),然后将head修改到整个链表的第一个位置(head.next.next = head;head.next = null;)

  2. 而要反转子链表head.next,就需要反转head.next的子链表,也就是head.next.next

  3. ……直到head.next为null时,此时整个子链表只有一个节点(节点5),单独的一个节点显然已经完成了反转,此时直接return返回,return返回之后,head指向4节点,last指向5节点在这里插入图片描述在这里插入图片描述

  4. 然后是反转倒数第二个节点(4),4节点的子链表显然已经完成了反转,此时只要将4节点拼接到5节点之后即可完成4,5节点的反转
    head.next.next = head;
    在这里插入图片描述

head.next = null;
在这里插入图片描述

return last;
在这里插入图片描述

  1. head指向3节点,重复第四步,直到最后整个链表完成反转

测试demo

ListNode类

public class ListNode {
    public int val;
    public ListNode next;


    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

    public void loop(ListNode head) {
        while (head != null) {
            System.out.print(head.val + "  ");
            head = head.next;
        }

    }
}

反转实现

public class ListTest {


    public static void main(String[] args) {
        ListNode _5 = new ListNode(5);
        ListNode _4 = new ListNode(4, _5);
        ListNode _3 = new ListNode(3, _4);
        ListNode _2 = new ListNode(2, _3);
        ListNode head = new ListNode(1, _2);

        System.out.print("====反转前:");
        head.loop(head);
        System.out.println();
        head = reverseList1(head);
        System.out.print("====反转后:");
        head.loop(head);
        System.out.println();
    }

    // 1. 递归参数和返回值
    public static ListNode reverseList1(ListNode head) {
        // 1. 递归终止条件
        if (head == null || head.next == null) {
            return head;
        }

        // 递归逻辑
        ListNode last = reverseList1(head.next);// last是未反转链表的最后一个节点
        head.next.next = head;// head和last构成双向
        head.next = null;// head的向last的指向取消
        return last;
    }
}

在这里插入图片描述

while循环

使用递归的好处是,代码简洁,但是当链表数量特别大的时候,递归可能会引起java的虚拟机栈栈溢出

我们提出了第二种方法,就是while循环

	public static ListNode reverseList2(ListNode head) {
        // head节点拷贝,pre和later节点用于暂存cur节点的前节点和后节点
        ListNode pre = null, cur = head, later = null;
        while (cur != null) {// cur节点代表了要反转的节点,cur节点为null说明所有节点反转完毕
            later = cur.next;// 1. later节点指向下一节点
            cur.next = pre;// 2. cur节点的next节点指向pre节点
            pre = cur;// 3. pre节点指向cur节点
            cur = later;// 4. cur节点指向later节点
        }
        return pre;

    }
  1. ListNode pre = null, cur = head, temp;
    head节点拷贝,pre和later节点用于后续暂存cur节点的前节点和后节点

  2. while (cur != null) {}

cur节点代表了要反转的节点,cur节点为null说明所有节点反转完毕
在这里插入图片描述

  1. later = cur.next;
    记录cur节点的后节点later

在这里插入图片描述

  1. cur.next = pre;(最核心的一步)

有人此时会说了,pre不是null吗,为什么不能直接cur.next = null;呢?

这行代码的真正含义是新增一条反向的路径,只是第一个节点反转的时候pre恰好为null

在这里插入图片描述

  1. pre = cur;
    记录要反转的节点的上一个节点
    在这里插入图片描述

  2. cur = temp;
    相当于cur = cur.next,cur节点遍历到下一个节点位置

在这里插入图片描述

  1. 重复上述6步
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    反转节点3
    在这里插入图片描述

测试demo

同上

总结

递归的代码逻辑清晰,但是消耗内存会比较多,而且可能会栈溢出;
while循环不会栈溢出;

两者的时间复杂度都是O(n)级别的

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值