实现反转链表--递归、迭代、双指针、栈

描述

2021年,新年第二天,吃过晚饭,没想到一道简简单单的题把我卡了半天,于是乎记录一下。建议自己先写写试试。

题目:

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

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

链表结构:

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

public ListNode reverseList(ListNode head) {
     
	//实现代码
}

其实开始想法是遍历链表,用栈保存其中的int值,然后再从栈中取出int生成新的链表。确实可以实现,但是效率太低,肯定不是最优的。最后再贴出栈的实现方法。

递归

相信不少同学和我一样,遇到递归的问题总是很头疼,而一看解答,却发现大佬们几行递归代码就优雅的解决了问题。遇到递归问题很容易进入思维误区,所谓递归,程序反复调用自身即是递归。既然能够调用自身说明它每一级的功能都是一样的,因此我们只需要关注一级递归的解决过程即可。

递归把握好三个关键点就可以了:

  1. 找整个递归的终止条件:递归应该在什么时候结束?
  2. 找返回值:应该给上一级返回什么信息?
  3. 本级递归应该做什么:在这一级递归中,应该完成什么任务?

这里推荐一个博客,写的非常清晰,套路解决递归问题

对于该题分析:

终止条件:节点为空或者它的下一个节点为空
返回值:返回给上一级已经反转完成的链表
本级应该做什么:反转后面的节点,并且将当前节点插入到它后面节点的后面

class Solution {
     
    public ListNode reverseList(ListNode head) {
     
    	//终止条件
        if(head == null || head.next == null){
     
            return head;
        }
        //将后面的节点反转
        ListNode next = reverseList(head.next);
        //将当前节点插入它后面的节点
        head.next.next = head;
        //防止产生循环链表
        head.next = null;
        return next;
    }
}

记得上学时候数据结构老师告诉我们,关于链表的操作,一定要在纸上把过程先画出来,再写程序。不过这次画翻车了,这里记录下我进入的误区,因为head.next.next = head这行让我想了好久,画图表示一下当时我的理解:

实现反转链表--递归、迭代、双指针、栈_第1张图片
假设当前传入的是2节点,假设它后面已经反转完成,所以画出了上面的图,而我一直再想怎么让5去指向2,想着想着就迷路了。这里就是一个思维误区了,下意识的以为2就是已经指向了5,但是实际应该是下面这样:

实现反转链表--递归、迭代、双指针、栈_第2张图片
实际上2指向的是3节点,因为2的next并没有变啊,这样再看head.next.next = head是不是就瞬间明白了。而head.next = null;也懂了吧,就是防止3指向2,2又指向3。

实现反转链表--递归、迭代、双指针、栈_第3张图片

迭代

一般来说递归能够实现的迭代也可以,可以使用双指针,也可以使用栈。先说双指针:

考虑遍历链表,并在访问各节点时修改 next 引用指向:

class Solution {
     
    public ListNode reverseList(ListNode head) {
     
        //记录前一个节点用的
        ListNode pre = null;
        //从头节点开始
        ListNode cur = head;
        //临时记录下一个节点
        ListNode temp = null;
        while(cur != null){
     
        	//记录下一个节点
            temp = cur.next;
            //将当前节点指向上前节点,实现反转
            cur.next = pre;
            //将当前节点交给pre,为下一次的循环做准备
            pre = cur;
            //移动到下一个节点
            cur = temp;
        }
        return pre;
    }
}

具体思路已经加到注释上了,不再赘述了,看下时间:

实现反转链表--递归、迭代、双指针、栈_第4张图片
栈是一种先进后出的数据结构,下面是使用栈来实现反转,思路就是遍历保存到栈中,再遍历栈,生成新的链表:

class Solution {
     
    public ListNode reverseList(ListNode head) {
     
        Stack<Integer> stack = new Stack<>();
        while(head != null){
     
            stack.add(head.val);
            head = head.next;
        }
        ListNode temp = new ListNode(0),result = temp;
        while(stack.size() != 0){
     
            temp.next = new ListNode(stack.pop());
            temp = temp.next;
        }
        return result.next;
    }
}

栈没啥好说的大家随便看看,果然耗时最长。

实现反转链表--递归、迭代、双指针、栈_第5张图片
记录完毕。
岁月流逝忽已暮,皆因惆怅不知路。
2021年加油吧,打工人!

你可能感兴趣的:(算法,递归,迭代,java,算法)