Bootstrap

每日一题---反转链表(经典老题,附大量图解)

题目链接:反转链表

这是一道很经典的题,因为在许多其他的链表题中都需要使用(单独实现),所以我单独整理出来,供大家参考,互相学习。

1.迭代法---三指针(双指针)

完成迭代需要三个指针

prev:已反转的链表的头部

cur:未反转链表的头部

curtmp:未反转链表头部的下一个节点

注意:这里的curtmp可以作为临时变量,但为了便于演示就设为变量 

举例:这里有四个节点需要反转,我们为其增加到五个

相信大家可以看出prev的作用,就是不用再单独为反转的链表末尾置为null,并且不用考虑反转的链表很少的情况。

接下来说做法:

第一步:将cur指向prev

执行cur.next = prev;

第二步:进行迭代操作,将各个节点向后移动

            prev = cur;
            cur = curtmp;
            curtmp = curtmp.next;

然后重复上述操作(这里就直接上图到最后看结束条件是什么)

注意到这步时不要直接结束,等执行cur.next = prev再结束

此时结束,cur就正好指向反转后链表的头部

        while(true) {//反转链表
            cur.next = prev;
            prev = cur;
            if(curtmp == null) {
                break;
            }
            cur = curtmp;
            curtmp = curtmp.next;
        }

当然前面对curtmp赋值时肯定要判断

2.递归

递归就是解决相同的子问题,这个两步一看就是处理了相同的问题,我们自然就想到用递归解决。

上面说curtmp可以是一个临时变量,那么我们就可以在递归中创建从而减少变量的传递。

那么递归函数的参数就可以设置为两个:prev,cur,意义与迭代相同

因为要返回头节点,所以我们最好把返回值设置为ListNode

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) {
            return null;
        }
        return recur(head, null);    // 调用递归并返回
    }
    private ListNode recur(ListNode cur, ListNode prev) {
        ListNode tmp = cur.next;
        cur.next = prev;
        prev = cur;
        if(tmp == null) {
            return cur;
        }
        cur = tmp;
        return recur(cur, prev);
    }
}

递归终止条件显然是这种情况

此时在cur反转后判断tmp == null作为递归终止条件

这个是正着向后递归的,所以个人感觉是很好理解的。

下面是参看力扣-Krahets的代码,他是采用从后往前递归的,我还看了好一会,个人感觉不太好理解(也可能是我太笨了)

class Solution {
    public ListNode reverseList(ListNode head) {
        return recur(head, null);    // 调用递归并返回
    }
    private ListNode recur(ListNode cur, ListNode pre) {
        if (cur == null) return pre; // 终止条件
        ListNode res = recur(cur.next, cur);  // 递归后继节点
        cur.next = pre;              // 修改节点引用指向
        return res;                  // 返回反转链表的头节点
    }
}

他的递归展开第一层就变成从最后一个节点开始,反着向回完成递归

执行cur.next = pre;

递归回到:

并且res没改变过,返回的就是头节点

那么以上就是全部题解了,欢迎大家补充更多解题思路,如有问题也欢迎大家指正!

;