《算法笔记知识点记录》第四章——算法初步3——递归

简介: 《算法笔记知识点记录》第四章——算法初步3——递归

☘前言☘

最近发现自己还是太菜了,所以计划刷一遍算法笔记。 最近的计划是花两个月的时间,在回所之前把算法笔记刷完,不知道能不能实现了了,希望大家监督我,最近疫情好严重,被封校了,大家也要注意安全呀。




今天继续更新,昨天的hash表大家都用明白了么,今天我们来看看递归,希望大家能和我一起学习呀。每篇文章后面都有对应的练习题哦,我自己会写题解给大家作为参考,好了不bb了,我们开始把!

🧑🏻作者简介:一个从工业设计改行学嵌入式的年轻人

✨联系方式:2201891280(QQ)

📔源码地址:https://gitee.com/xingleigao/algorithm-notes

⏳全文大约阅读时间: 120min(有点长,大家忍一下 ,一定会有蜕变的)


文章目录

☘前言☘

👻1.分治

😶‍🌫️2.递归

2.1 求阶乘

2.2 Fibonacci数列求解

2.3全排列的求解

2.4 n皇后问题

推荐专栏

🐳课后习题

👻1.分治

我们经常听到大事化小、小事化了,这就是分治思想。准确表述是将原问题划分成若干个规模小而结构与原问题相同或相似的子问题,然后分别解决这些子问题,最后合并子问题的解,即可得到原问题的解,从上面的描述我们可以将这类问题解法分为三步,打开冰箱,塞进冰箱,关闭冰箱(不是)


1.分解: 将原问题分解为若干和原问题拥有相同或相似结果的子问题。(递归入口)

2.解决: 递归求解所有子问题。如果存在子问题的规模小到可以直接解决,就直接解决。(递归出口)

3.合并: 将子问题的解合并为原问题的解。(递归返回值)

一般来说分治思想既可以用递归实现,也可以通过非递归的手段去实现。


😶‍🌫️2.递归

如果你要理解递归,你要先理解递归,直到你能理解递归。感觉这句话很有意思,哈哈哈,递归的实现就是反复调用自身函数,直到问题减小到可以直接求解,返回求解结果。

这里面有两个重要的概念:


递归边界(我喜欢叫递归出口)

递归式(递归调用)

2.1 求阶乘

说了这么多概念,其实没有那么复杂 我们看一个求n ! n!n!的例子:


int F(int n){
  if(n == 0); return 1;  //(1)
  else return F(n - 1)*n;  //(2)
}


(1) . 递归出口 也就是问题的可求解的边界

(2). 递推式 其实就是f(n)=n∗f(n−1)

以求f ( 3 ) f(3)f(3)为例,求解过程中其实就是f(3)=f(2)∗3=f(2)∗2∗3=f(1)∗1∗2∗3=f(0)∗1∗1∗2∗3=1∗1∗2∗3


2.2 Fibonacci数列求解

F i b o n a c c i FibonacciFibonacci数列,就是求解F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−2),其中F ( 1 ) = 1 , F ( 0 ) = 1 F(1) = 1, F(0) = 1F(1)=1,F(0)=1。

这就是典型的递归应用,看看代码:


int F(int n){
  if(n == 0 || n == 1)  return 1; //(1)
  return F(n-1) + F(n-2)    //(2)
}


同样对应有递归出口和递推式,所以递归的灵魂就是递归出口和递推式。

2.3全排列的求解

求一个序列的全排列,就可以划分一下以1元素开头的,以2元素开头....blabla,我们先填第一个,然后接下来利用上节课的hash表来记录已经插入的元素。然后递归求解。

示例题目:46. 全排列


int  P[7],HashTable[22];
void generteP(int index,int* nums, int numsSize, int* returnSize, int** >returnColumnSizes,int** ans){
   if(index == numsSize+1){ //(1)
       ans[*returnSize] = malloc(sizeof(int) * numsSize);
       memcpy(ans[*returnSize],P + 1,sizeof(int) * numsSize);//拷贝数据
       (*returnColumnSizes)[*returnSize] = numsSize;
       (*returnSize)++;
       return;
   }
   for( int i = 0; i < numsSize;++i){
       if(HashTable[nums[i] + 10] == false){
           P[index] = nums[i];   //加入当前全排列
           HashTable[nums[i] + 10] = true;//(2)
           generteP(index + 1, nums, numsSize,returnSize,returnColumnSizes, ans);
           HashTable[nums[i] + 10] = false;//(3)
       }
   }
}
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
   memset(HashTable, 0 ,sizeof(HashTable));
   int **ans = malloc(sizeof(int *) * 720); //最大的数量
   (*returnColumnSizes) = malloc(sizeof(int) * 720);//最大的数量
   *returnSize = 0;
   generteP(1,nums, numsSize,returnSize,returnColumnSizes, ans);
   return ans;
}


其实力扣看思想还是舒服的,就是对于OJ可能不适应。

(1). 出口函数 处理结果的返回

(2). 插入hash表

(3). 处理完了 还原状态


2.4 n皇后问题

可以看一下:51. N 皇后,就是国际象棋里面摆放皇后,一共有多少个放法。

如果按照常规思路,是需要枚举n2个位置中选出n个的方案,也就是bd72fd8e0354561cffde90455c29eb8.png,这个数据量非常大。但是n皇后其实不能放同一行同一列,其实能放的就是n的一个全排列。比如例子给的就是2413


void generateP(int index){
  if(index == n+1){
  bool flag = true;
  for(int i = 1;i <= n;++i)
    for(int j = 1;j <= n;++j)
    if(abs(i-j) == abs(P[i] - P[j]) flag = false; //不合法
  if(flag) count++;
  return;
  }
  for(int x =1; x<= n;x++){
  if(hashTable[x] == false){
    P[index] = x;
    hashTable[x] = true;
    generateP(index+1);
    hashTable[x] = false;
  }
  }
}


上面的其实不是原题目的解答,是统计有多少种方案的写法,其实放上来的主要目的是为了告诉大家这里的在最终出现结果的时候进行结果判定是否合法的方式称为暴力法,没想到吧,这就是暴力,是不是日常暴力???

更常见的方式是回溯法,就是在执行过程中进行判定,不合法直接返回上一层,更合理。


void generateP(int index){
  if(index == n+1){
  count++;  //一定合法
  return;
  }
  for(int x =1; x<= n;x++){
  if(hashTable[x] == false){
    bool flag = true;
    for(int pre = 1; pre < index;++pre){
    if(abs(index - pre) == abs(x - P[pre]){
      flag = false;
      break;
    }
    }
    if(flag){   //回溯法
    P[index] = x;
    hashTable[x] = true;
    generateP(index+1);
    hashTable[x] = false;
    }
  }
  }
}


那么接下来就是我们的力扣ac代码:


int P[10],hashTable[11];
const char *s = ".........";
void generateP(int index,int n, int* returnSize, int** returnColumnSizes,char *** ans){
  if(index == n){
  ans [*returnSize] = malloc(sizeof(char *) * n);
        for(int i = 0;i < n;++i){
            ans[*returnSize][i] = malloc(sizeof(char) * (n + 1));
            strncpy(ans[*returnSize][i],s,n);
            ans[*returnSize][i][n] = '\0';
            ans[*returnSize][i][P[i]] = 'Q';
        }
        (*returnColumnSizes)[*returnSize] = n;
        ++(*returnSize);
  return;
  }
  for(int x =0; x< n;x++){
  if(hashTable[x] == false){
    bool flag = true;
    for(int pre = 0; pre < index;++pre){
    if(abs(index - pre) == abs(x - P[pre])){
      flag = false;
      break;
    }
    }
    if(flag){   //回溯法
    P[index] = x;
    hashTable[x] = true;
    generateP(index + 1,n,returnSize,returnColumnSizes,ans);
    hashTable[x] = false;
    }
  }
  }
}
char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){
    memset(hashTable, 0, sizeof(hashTable));    //全局变量初始化
    char *** ans = malloc(sizeof(char **) * 10000);//返回的结果
    (*returnColumnSizes) = malloc(sizeof(int) * 10000);
    (*returnSize) = 0;
    generateP(0,n,returnSize,returnColumnSizes,ans);
    return ans;
}

715e29c516994f209e5768513977cfdd.png


Ohhhhhhhhh~,过啦,哈哈哈哈。算是比较难的了。三级指针要命,建议回头用c艹写。。。


推荐专栏

🍭《算法笔记》记录🍭

👻算法笔记刷题👻

🐳课后习题

我写完会放题解,大家写完了可以在评论区打卡哟!题解我放评论区吧,这样不用修改文章。

9ee3721f394e19c520d469f0164ea27.png


9ee3721f394e19c520d469f0164ea27.png

题解:评论区见,置顶没有就是我没写完0.0,大佬们刷完打个卡

大家加油冲!!!!


相关文章
|
13天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
30 2
|
1月前
|
算法 API 计算机视觉
人脸识别笔记(一):通过yuface调包(参数量54K更快更小更准的算法) 来实现人脸识别
本文介绍了YuNet系列人脸检测算法的优化和使用,包括YuNet-s和YuNet-n,以及通过yuface库和onnx在不同场景下实现人脸检测的方法。
49 1
|
1月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
64 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
1月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
101 0
|
1月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
14 0
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
31 0
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-26、删除有序数组的重复项)
❤️算法笔记❤️-(每日一刷-26、删除有序数组的重复项)
23 0
|
29天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
6天前
|
算法 数据安全/隐私保护 索引
OFDM系统PAPR算法的MATLAB仿真,对比SLM,PTS以及CAF,对比不同傅里叶变换长度
本项目展示了在MATLAB 2022a环境下,通过选择映射(SLM)与相位截断星座图(PTS)技术有效降低OFDM系统中PAPR的算法实现。包括无水印的算法运行效果预览、核心程序及详尽的中文注释,附带操作步骤视频,适合研究与教学使用。
|
14天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
下一篇
无影云桌面