链表题目总结 -- 递归

一. 递归反转整个链表

1. 思路简述

  • 所谓递归,就像那句歌词一样“一层一层剥开我的心”,我们从第一个节点一直向下探索,发现节点5。现在想:如果是单个节点,那反转链表其实就相当于自身本身,也就是不用动了。这里考虑一个临界情况,如果传进的参数(head指针)是null,那也不用动了,直接返回其本身就可以。
  • 来到倒数第二层,也就是节点4,现在情况变成了节点有2个的链表,现在需要反转,那么我们只需要将中间的指针做一个反转就好了,而当前传进来的指针(head),其实是节点4的head指针,那么就有head.next.next = head;,最后将节点4的后继赋值为空(head.next = null),这表示这一阶段(有2个节点的链表已经反转完成。如果链表只有两个节点,直接输出就可以。)
  • 重复上面步骤,直到最后整个链表都反转了
    在这里插入图片描述

2. 代码

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {                       
    public ListNode reverseList(ListNode head) {
        //从后向前,一点点的进行反转
        //先分析特殊情况,链表有一个节点或者没有节点,直接返回头结点
        if(head == null || head.next == null)
            return head;
        else{
        	//last为反转链表之后的头指针
            ListNode last = reverseList(head.next);
            head.next.next = head;
            head.next = null;
            return last;
        }
    }
}

3. 总结

  • 时间复杂度:o(n)
  • 空间复杂度:o(n),需要用栈
  • 第一次做的时候,还以为是逆向输出,整了半天,搞错了。
  • 对递归的边界条件掌握的还是不好,像head == null这一块,博主当时就没想到与head.next进行合并。
  • head.next = null;一定要注意,否则,会出现成环的现象
  • else语句中的前三条顺序不能乱,多去理解

二. 反转链表前 N 个节点

  • 题目链接:没有链接,给一个函数名:public static ListNode reverseN(ListNode head,int n);,自己去练吧。

1. 思路简述

  • 本质和反转链表差不多,只是在边界值的地方需要注意,

2. 代码

		//存放需要逆转链表的后继第一个节点
    	public static ListNode successor = null;
    	
        public static ListNode reverseN(ListNode head,int n){
            //逆转前n个节点
            if (n == 1) {
                successor = head.next;
                return head;
            }
            //递归,将下一个节点放进去
            ListNode last = reverseN(head.next, n - 1);
            head.next.next = head;
            head.next = successor;

            return last;
        }

3. 总结

  • 也就是反转链表,只是每次反转完,head后面要接后继节点(后面的一段不需要反转的链表),就变了这一点。
  • 时间复杂度:o(n)
  • 空间复杂度:o(n),需要用栈

三、反转链表的一部分

1. 思路简述

  • 将问题转换成反转前n个节点的问题。

2. 代码

	//存放需要逆转链表的后继第一个节点
   	public static ListNode successor = null;
   	
       public static ListNode reverseN(ListNode head,int n){
           //逆转前n个节点
           if (n == 1) {
               successor = head.next;
               return head;
           }
           //递归,将下一个节点放进去
           ListNode last = reverseN(head.next, n - 1);
           head.next.next = head;
           head.next = successor;

           return last;
       }
       
		ListNode reverseBetween(ListNode head, int m, int n) {
	  	    // 当m为1的时候,装换成了反转前面几个节点的链表的问题
		    if (m == 1) {
		        return reverseN(head, n);
		    }
		    // 将前面不需要反转的链表和后面反转过的链表接在一起
		    head.next = reverseBetween(head.next, m - 1, n - 1);
		    
	    	return head;
	   }

3.总结

  • head.next = reverseBetween(head.next, m - 1, n - 1); 为什么是head.next呢,看边界情况,m = 1时,返回的是后面已经反转过的链表,也就是说前面的链表压根不需要反转,只要把它们拼接在一起就行了。
  • 再说为什么是m - 1的问题,每递归一次,新链表就会从前面缩短一节,那么对于新链表来说,就是从第m-1个节点开始反转,到第n - 1个节点结束反转。这里的关键是链表从头开始缩短了,所以,m - 1 和 n - 1都要存在。
  • head.next = reverseBetween(head.next, m - 1, n - 1);传入递归的时候,一定是next后继进去,而不是head本身。

四、从节点M开始反转后面的链表

  • 题目链接:没有链接,给一个函数名:public static ListNode reverseP(ListNode head,int m);,自己去练吧。

1. 思路简述

  • 转换成反转单链表的问题

2. 代码

    public ListNode reverseList(ListNode head) {
        //从后向前,一点点的进行反转
        //先分析特殊情况,链表有一个节点或者没有节点,直接返回头结点
        if(head == null || head.next == null)
            return head;
        else{
        	//last为反转链表之后的头指针
            ListNode last = reverseList(head.next);
            head.next.next = head;
            head.next = null;
            return last;
        }
    }
    
    public static ListNode reverseP(ListNode head, int m){
        //转换成反转链表的问题
        if(m == 1){
            return reverseList(head);
        }
        head.next = reverseP(head.next, m - 1);

        return head;
    }

3.总结

  • 和上一题差不多,一直递归,到链表需要反转的地方(m == 1),开始反转整个单链表。

参考:https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-8f30d/di-gui-mo–10b77/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值