您好,登录后才能下订单哦!
这篇文章主要为大家展示了“LeetCode反转链表的方式有哪些”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“LeetCode反转链表的方式有哪些”这篇文章吧。
问题描述
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:
0 <= 节点个数 <= 5000
使用栈解决
链表的反转是老生常谈的一个问题了,同时也是面试中常考的一道题。最简单的一种方式就是使用栈,因为栈是先进后出的。实现原理就是把链表节点一个个入栈,当全部入栈完之后再一个个出栈,出栈的时候在把出栈的结点串成一个新的链表。原理如下
 
    代码比较简单,来看下
 1public ListNode reverseList(ListNode head) {
 2    Stack<ListNode> stack = new Stack<>();
 3    //把链表节点全部摘掉放到栈中
 4    while (head != null) {
 5        stack.push(head);
 6        head = head.next;
 7    }
 8    if (stack.isEmpty())
 9        return null;
10    ListNode node = stack.pop();
11    ListNode dummy = node;
12    //栈中的结点全部出栈,然后重新连成一个新的链表
13    while (!stack.isEmpty()) {
14        ListNode tempNode = stack.pop();
15        node.next = tempNode;
16        node = node.next;
17    }
18    //最后一个结点就是反转前的头结点,一定要让他的next
19    //等于空,否则会构成环
20    node.next = null;
21    return dummy;
22}
 
    双链表求解
双链表求解是把原链表的结点一个个摘掉,每次摘掉的链表都让他成为新的链表的头结点,然后更新新链表。下面以链表1→2→3→4为例画个图来看下。


他每次访问的原链表节点都会成为新链表的头结点,最后再来看下代码
 1public ListNode reverseList(ListNode head) {
 2    //新链表
 3    ListNode newHead = null;
 4    while (head != null) {
 5        //先保存访问的节点的下一个节点,保存起来
 6        //留着下一步访问的
 7        ListNode temp = head.next;
 8        //每次访问的原链表节点都会成为新链表的头结点,
 9        //其实就是把新链表挂到访问的原链表节点的
10        //后面就行了
11        head.next = newHead;
12        //更新新链表
13        newHead = head;
14        //重新赋值,继续访问
15        head = temp;
16    }
17    //返回新链表
18    return newHead;
19}
 递归解决
我们再来回顾一下递归的模板,终止条件,递归调用,逻辑处理。
 1public ListNode reverseList(参数0) {
 2    if (终止条件)
 3        return;
 4
 5    逻辑处理(可能有,也可能没有,具体问题具体分析)
 6
 7    //递归调用
 8    ListNode reverse = reverseList(参数1);
 9
10    逻辑处理(可能有,也可能没有,具体问题具体分析)
11}
 终止条件就是链表为空,或者是链表没有尾结点的时候,直接返回
if (head == null || head.next == null) return head;
递归调用是要从当前节点的下一个结点开始递归。逻辑处理这块是要把当前节点挂到递归之后的链表的末尾,看下代码
 1public ListNode reverseList(ListNode head) {
 2    //终止条件
 3    if (head == null || head.next == null)
 4        return head;
 5    //保存当前节点的下一个结点
 6    ListNode next = head.next;
 7    //从当前节点的下一个结点开始递归调用
 8    ListNode reverse = reverseList(next);
 9    //reverse是反转之后的链表,因为函数reverseList
10    // 表示的是对链表的反转,所以反转完之后next肯定
11    // 是链表reverse的尾结点,然后我们再把当前节点
12    //head挂到next节点的后面就完成了链表的反转。
13    next.next = head;
14    //这里head相当于变成了尾结点,尾结点都是为空的,
15    //否则会构成环
16    head.next = null;
17    return reverse;
18}
 因为递归调用之后head.next节点就会成为reverse节点的尾结点,我们可以直接让head.next.next = head;,这样代码会更简洁一些,看下代码
1public ListNode reverseList(ListNode head) {
2    if (head == null || head.next == null)
3        return head;
4    ListNode reverse = reverseList(head.next);
5    head.next.next = head;
6    head.next = null;
7    return reverse;
8}
 这种递归往下传递的时候基本上没有逻辑处理,当往回反弹的时候才开始处理,也就是从链表的尾端往前开始处理的。我们还可以再来改一下,在链表递归的时候从前往后处理,处理完之后直接返回递归的结果,这就是所谓的尾递归,这种运行效率要比上一种好很多
 1public ListNode reverseList(ListNode head) {
 2    return reverseListInt(head, null);
 3}
 4
 5private ListNode reverseListInt(ListNode head, ListNode newHead) {
 6    if (head == null)
 7        return newHead;
 8    ListNode next = head.next;
 9    head.next = newHead;
10    return reverseListInt(next, head);
11}
 尾递归虽然也会不停的压栈,但由于最后返回的是递归函数的值,所以在返回的时候都会一次性出栈,不会一个个出栈这么慢。但如果我们再来改一下,像下面代码这样又会一个个出栈了
 1public ListNode reverseList(ListNode head) {
 2    return reverseListInt(head, null);
 3}
 4
 5private ListNode reverseListInt(ListNode head, ListNode newHead) {
 6    if (head == null)
 7        return newHead;
 8    ListNode next = head.next;
 9    head.next = newHead;
10    ListNode node = reverseListInt(next, head);
11    return node;
12}以上是“LeetCode反转链表的方式有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。