首页 > 编程语言 >【算法】递归

【算法】递归

时间:2024-10-24 17:48:10浏览次数:3  
标签:head ListNode 递归 next 链表 算法 return

目录


一、简单讲解递归

  1. 什么是递归百度百科给出的解释,但其实就只一句话说,函数自己调用自己。
  2. 为什么会使用递归 :就是可以将一个主问题分成与主问题相同逻辑的多个子问题,子问题又能再分。
  3. 如何理解递归:初学:递归展开的细节图,熟悉:理解二叉树中的题目,大乘:宏观看待递归过程:不在意递归的细节图,把递归函数当成一个能解决任务的东西。
  4. 如何写好一个递归
    4.1. 先找到相同的子问题,确定函数头如何设计;
    4.2. 只关心一个子问题的解决方式,确定函数的书写;
    4.3. 注意递归函数的结束条件,避免死递归。

接下来使用递归的算法题来深入理解递归。

二、⾯试题08.06.汉诺塔问题

题目链接:⾯试题08.06.汉诺塔问题
题目描述:

题目分析:
一个经典的递归问题,3根柱子,将A柱子上的盘子全部放在C上,转移过程中只能小盘子在大盘子上。

解题思路:

  1. 先找到相同的子问题:我们要将n个盘子从A借助B放去C,那我们就先将n-1个盘子从A借助C放入B,再将最后一个盘子从A放入C;以此确定函数头设计为void dfs(A,B,C, 盘子个数 )
  2. 一个子问题的解决思路:
    2.1. 先将n-1个盘子从A借助C放入B;
    2.2 将最后一个盘子从A放入C;
    2.3 将n-1个盘子从B借助A放入C;
    所以函数的书写如下:
dfs(A,C,B,n-1);
C.add(A.remove(A.size()-1));
dfs(B,A,C,n-1);
  1. 递归函数的结束条件:只有一个盘子就只要移动就行了。

解题代码如下:

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        dfs(A,B,C,A.size());
    }
    public void dfs(List<Integer> A, List<Integer> B, List<Integer> C, int n) {
        if(n == 1) {
            C.add(A.remove(A.size()-1));
            return;
        } 
        dfs(A,C,B,n-1);
        C.add(A.remove(A.size()-1));
        dfs(B,A,C,n-1);
    } 
}

三、合并两个有序链表

题目链接:合并两个有序链表
题目描述:

解题思路:

  1. 先找到相同的子问题:拿到一个节点后,剩下节点与另一个链表不就又是两个链表合并吗。函数头dfs(l1,l2)
  2. 一个子问题解决思路:
    2.1 将现在两个链表中较小的头结点作为合并后的头结点;
    2.2 在合并剩余的链表。
    函数的书写:
		if(list1.val < list2.val) {
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
  1. 递归结束条件:如果有一个链表为空就不用合并,直接返回另一个链表即可。

解题代码:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null) return list1;

        if(list1.val < list2.val) {
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }
}

四、206.反转链表

题目链接:206.反转链表

题目描述:

解题思路:

  1. 先找到相同的子问题:翻转了一个节点之后,剩下节点组成的链表又是一个反转链表问题。
    函数头设计就使用题目的函数头ListNode reverseList(ListNode head)
  2. 一个子问题的解决思路: 就使用两个节点。
    2.1 先将head的下一个节点的next域指向head即head.next.next = head
    2.2 再将head的next域指向null,在进行这一步之前需要将next域的值记录下来作为反转后的链表的头结点;
  ListNode newHead = head.next;
  head.next = null;
  1. 递归结束条件:当要反转的链表只有一个节点或者没有节点就返回。

解题代码:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

五、24.两两交换链表中的节点

题目链接:24.两两交换链表中的节点

题目描述:

解题思路:

  1. 先找到相同的子问题:我们交换了两个节点之后,剩下节点组成的链表又是一个两两交换节点问题。所以函数头设计就是题目给出的函数ListNode swapPairs(ListNode head)
  2. 一个子问题的解决思路:
    2.1 交换两个节点的思路:head的下一个节点的next域指向head,head的next执行后续链表交换后返回的头结点。
    2.2 注意在修改之前记录一下head的next的节点和head的next的next。

解题代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        
        ListNode newHead = head.next;
        ListNode node = newHead.next;
        newHead.next = head;
        head.next = swapPairs(node);
        return newHead;
    }
}

六、50.Pow(x,n)

题目链接:50.Pow(x,n)

题目描述:

解题思路:递归实现快速幂

  1. 先找到相同的子问题:有两种看法:x的n次方等于x的n-1次方乘x,但是这种的时间复杂度是O(n),还有就是x的n次方等于x的n/2次方乘x的n/2次方,根据n是不是偶数判断需要再乘一个x不,这样的时间复杂度是O(logN)。所以设计函数头跟题目一样double myPow(double x, int n)
  2. 一个子问题的解决方法:就是上面的第二种方法,但是我们需要考虑n是否为负数,是否为奇数,所以我们将递归函数单独拿出来,两个函数中分别对n进行一次判断。
  3. 递归结束条件:n为0的时候就返回0。

解题代码:

class Solution {
    public double myPow(double x, int n) {
        return (n < 0) ? (1 / pow(x,-n)) : pow(x,n); 
    }
    public double pow(double x, int n) {
        if(n == 0) return 1.0;
        double tmp = pow(x,n/2);
        return (n % 2 == 0) ? (tmp * tmp) : (x * tmp * tmp); 
    }
}

标签:head,ListNode,递归,next,链表,算法,return
From: https://blog.csdn.net/yj20040627/article/details/143187469

相关文章

  • 【两阶段鲁棒微网】【不确定性】基于关键场景辨别算法的两阶段鲁棒微网优化调度(Matlab
     ......
  • LeetCode常用算法模板
    代码模板  1、DFS:适用于树和图的遍历、组合问题。2、BFS:适用于树和图的层次遍历、最短路径问题。3、二分查找:适用于有序数组的搜索问题。4、动态规划:适用于最优化问题、序列问题。5、贪心算法:适用于局部最优问题、调度问题。6、回溯算法:适用于组合、排列、子集问题。......
  • 过路车辆识别视频分析服务器智慧园区/智慧城市算法简介及应用
    视频分析服务器是一款集成了软硬件的一体化解决方案,它适用于城市管理部门、环境卫生、教育领域、水利工程、工业园区以及住宅小区等多个行业和场景。这款智能化的一体机设备为用户提供了高清视频监控的接入能力、智能视频分析、告警功能以及数据资源的共享服务。一、概要1、功能......
  • 【数据结构与算法】之栈详解
    栈(Stack)是一种基本的线性数据结构,遵循后进先出、先进后出的原则。本文将更详细地介绍栈的概念、特点、Java实现以及应用场景。1.栈概念概述想象一摞叠放的盘子,你只能从最上面取盘子,放盘子也只能放在最上面。栈就像这样一摞盘子,它只有一个开口,称为栈顶(Top)。另一端封闭,称......
  • 【数据结构与算法】之队列详解
    队列(Queue)是一种重要的线性数据结构,遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java实现以及应用场景。模运算小复习:a%b的值总是小于b5%4=1  5 %2=11%5=1  4%5=41.队列概念概述想象一下排队买票,先排队的人总是先买......
  • 机器学习中验证两个算法之间是否存在显著差距的t-test检验
    同一主题的简单分析版本,建议查看:机器学习领域中假设检验的使用本文内容为在上文基础上进一步分析版本。相关:t检验t检验应用条件t检验(t-test)t-test终极指南一文详解t检验t检验,亦称studentt检验(Student'sttest),主要用于样本含量较小(例如n<30),总体标准差σ未知的正......
  • 使用分水岭算法实现分割图像
    #导包:cv2视觉、numpy数组importcv2importnumpyasnp#加载图片img=cv2.imread('mourse.jpg')cv2.imshow('ori',img)#转换图片为黑白色gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将图片阈值分割ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)......
  • 最强总结!十大回归类算法模型 !!!
    1.线性回归线性回归是一种用于描述两个或多个变量之间线性关系的统计模型。假设  是响应变量(目标变量), 是解释变量(特征),线性回归模型通过拟合一条直线来预测目标变量。原理线性回归的基本假设是:其中, 是截距, 是回归系数, 是误差项(即残差),假设其服从正态分布。核心公式......
  • 蓝牙室内定位算法-蓝牙ibeacon室内定位技术方案
    随着科技的飞速发展,室内定位技术已成为现代生活中的重要组成部分。在众多室内定位技术中,蓝牙技术凭借其低功耗、高精确度以及广泛的设备兼容性,逐渐在室内定位领域崭露头角。其中,蓝牙iBeacon技术更是凭借其独特的优势,成为室内定位领域的佼佼者。本文将深入探讨蓝牙iBeacon室内定......
  • 常用距离算法
    常用距离算法对于两个点$(x_1,y_1)$和$(x_2,y_2)$的距离大致有$3$种:欧氏距离曼哈顿距离切比雪夫距离三维情况下表示为$(x_1,y_1,z_1)$和$(x_2,y_2,z_2)$。多维情况下表示为$(x_1,x_2,...,x_d)$和$(y_1,y_2,...,y_d)$,其中$d$表示维数(与二、三维表示有所不同)......