20 递归算法精髓解析:基准、性质、案例(阶乘、斐波拉契、猴子吃桃、汉诺塔等)、与循环的对比

目录

1 概述

2 递归的基本组成部分

2.1 基准情况

2.2 递归步骤

2.3 案例:循环实现阶乘的计算

2.4 案例:递归函数实现阶乘的计算

3 递归的性质

3.1 自我调用

3.2 栈的使用

3.3 问题分解

3.4 性能考虑

3.5 案例:递归的回溯

4 综合案例

4.1 计算斐波那契数列的第 N 项

4.1.1 循环实现

4.1.2 递归函数实现

4.2 输出斐波那契数列的前 N 项

4.3 逆推猴子吃桃问题

4.4 汉诺塔问题

4.5 反转字符串

4.6 求数字之和

4.6.1 循环实现

4.6.2 递归函数实现

4.7 求两个数的最大公约数(辗转相除法)

4.7.1 循环实现

4.7.2 递归函数实现

5 递归与循环的区别与联系

5.1 区别

5.2 联系


1 概述

        在 C 语言中,递归是一种编程技术它允许一个函数直接或间接地调用自身。递归函数通常用于解决那些可以被分解为更小的子问题的问题,这些子问题具有与原始问题相同的结构。递归函数的设计需要特别小心,以确保递归最终能够终止,并且每一步都朝着解决最终问题的方向前进


2 递归的基本组成部分

2.1 基准情况

        基准情况(Base Case)是递归函数中的特殊条件,当满足这些条件时,函数将停止调用自身并直接返回一个结果。它是递归的出口,防止了无限递归的发生。每个递归函数都必须明确指定至少一个基准情况(即必须有一个明显的结束条件),这些情况是问题最简单或最直接的解决方案,无需进一步递归。

        如在 2.4 案例:递归实现阶乘的计算中,基准情况可以是 n == 0 或 n == 1,因为 0 的阶乘和 1 的阶乘都定义为 1,这是可以直接给出的答案。

2.2 递归步骤

        递归步骤(Recursive Step)是函数调用自身以解决更小或更简单问题的过程。在这一步中,函数通过某种方式减小问题的规模,使其更接近基准情况。递归步骤的设计至关重要,因为它必须确保每次递归调用都朝着基准情况的方向前进,即问题规模逐渐减小,复杂度逐渐降低,最终递归结束

        如在 2.4 案例:递归实现阶乘的计算中,递归步骤是 factorial(n) = n * factorial(n-1)。这里,每次调用 factorial 时,都通过减小 n 的值来缩小问题的规模,直到达到基准情况 n == 0 或 n == 1。这个过程体现了递归步骤如何确保问题规模趋近于基准条件,并最终导致递归的终止。

2.3 案例:循环实现阶乘的计算

#include <stdio.h>int factorial(int n)
{// result 初始设置为 1,因为任何数的阶乘乘以 1 都不会改变其值// 且 0 和 1 的阶乘就是 1int result = 1;// 从 2 遍历到 n(包括 n ),并在每次迭代中将当前的 result 乘以循环变量 ifor (int i = 2; i <= n; i++){result *= i;}// 返回最终结果return result;
}int main()
{int number = 0;printf("请输出一个需要计算其阶乘的非负整数:");scanf("%d", &number);// 验证输入数据的合法性if (number >= 0){printf("%d 的阶乘是: %d\n", number, factorial(number));}else{printf("对于负数,阶乘是未定义的。\n");}return 0;
}

        在 CMD 中多次运行程序,输出结果如下所示:

2.4 案例:递归函数实现阶乘的计算

#include <stdio.h>// 递归函数来计算阶乘
long factorial(int n)
{// 基准情况:递归的出口if (n == 0 || n == 1) // 或者 if(n <=1 ){return 1;}// 递归步骤:调用自身以解决更小或更简单问题的过程,使其更接近基准情况else{return n * factorial(n - 1);}
}int main()
{int number = 0;printf("请输出一个需要计算其阶乘的非负整数:");scanf("%d", &number);// 验证输入数据的合法性if (number >= 0){printf("%d 的阶乘是: %ld\n", number, factorial(number));}else{printf("对于负数,阶乘是未定义的。\n");}return 0;
}

        在 CMD 中多次运行程序,输出结果如下所示:

        以 factorial(5) 为例,递归函数结构分析如下所示:

提示:

        还可以在程序的关键位置设置断点,通过调试工具逐步执行,来深入探究递归函数的运行情况。


3 递归的性质

3.1 自我调用

        递归函数在其定义内部调用自身。这是递归的核心特性。通过自我调用,函数能够不断地将问题分解为更小的子问题,直到达到一个基准情况(或称为基本情况、边界条件),此时函数将停止调用自身并返回一个结果

3.2 栈的使用

        在 C 语言中(以及大多数其他编程语言中),函数调用是通过栈来实现的。当函数被调用时,它的执行环境(包括局部变量、参数、返回地址等)会被压入调用栈中。递归调用也不例外,每次递归调用都会创建一个新的栈帧(stack frame),并压入调用栈。当递归函数开始返回时,这些栈帧会按照后进先出(LIFO)的顺序被弹出,从而实现了从最深递归层次开始回溯到最初调用的过程

3.3 问题分解

        递归调用的过程实际上是一个问题分解的过程。每次递归调用都将原问题分解为一个或多个更小的子问题,直到子问题变得足够简单,可以直接解决(即达到基准情况)。然后,递归函数通过组合这些子问题的解来得到原问题的解。

3.4 性能考虑

        虽然递归调用在解决某些问题时非常直观和方便,但它也可能导致性能问题。特别是当递归深度很大时,由于需要大量的栈空间来存储每次调用的执行环境,这可能会导致栈溢出错误。此外,递归调用还可能引入额外的函数调用开销

3.5 案例:递归的回溯

#include <stdio.h>// 函数声明,用于演示递归调用并打印数字
void test(int n)
{// 首先打印当前传入的数字printf("%d\n", n);// 检查 n 是否大于 1,如果是,则递归调用自身并传入 n-1if (n > 1){test(n - 1);}// 递归返回后,再次打印当前数字(此时是从最深的递归层次返回时打印)printf("%d\n", n);
}int main()
{// 调用 test 函数,传入数字 3 作为参数// 这将展示递归函数如何工作,并打印出特定的数字序列test(3);return 0;
}

        输出结果如下所示:

        程序分析如下所示:

        上面这个程序演示了一个简单的递归函数 test,它接受一个整数 n 作为参数。函数首先打印当前的 n 值,然后检查 n 是否大于 1。如果是,则递归调用自身,但传入的参数是 n-1。这导致了一个递归过程,其中数字被连续减小并打印,直到 n 不再大于 1,此时递归调用停止。但是,由于递归调用的性质,函数在返回过程中还会再次打印每个数字,这次是从最深的递归层次开始回溯到最初的调用。因此,对于给定的输入 3,输出将是 3、2、1(递减过程),然后是1、2、3(回溯过程)。


4 综合案例

4.1 计算斐波那契数列的第 N 项

        斐波那契数列是指这样一个数列:1,1,2,3,5,8,13,21,34,55,89……这个数列从第 3 项开始 ,每一项都等于前两项之和。现在要求编写一个程序,接收用户输入的一个正整数 n,然后输出斐波那契数列的第 n 项。

4.1.1 循环实现

        下面是通过 for 循环结构来实现斐波那契数列第 n 项计算的程序示例:

#include <stdio.h>// 函数原型声明
int fibonacci(int n);int main()
{int n;printf("请输入一个正整数n,以获取斐波那契数列的第n项: ");scanf("%d", &n);// 检查输入值是否合法if (n < 1){printf("输入的数字必须大于等于1。\n");return 1; // 退出程序}// 调用函数并打印结果printf("斐波那契数列的第%d项是: %d\n", n, fibonacci(n));return 0;
}// 使用循环实现斐波那契数列第 n 项的函数
int fibonacci(int n)
{if (n == 1 || n == 2){// 如果 n 是 1 或 2,则直接返回 1return 1;}// 初始化第一项和第二项int first = 1, second = 1, third;// 从3 开始循环遍历for (int i = 3; i <= n; i++){third = first + second; // 计算前两项之和first = second;         // 更新 first 为前一项second = third;         // 更新 second 为当前项(即前两项之和)}return second; // 循环结束后,second 中存储的就是第 n 项的值
}

        在 CMD 中多次运行程序,输出结果如下所示:

4.1.2 递归函数实现

        下面是通过递归函数来实现斐波那契数列第 n 项计算的程序示例: 

#include <stdio.h>// 函数原型声明
int fib(int n);int main()
{int n;printf("请输入一个正整数n,以获取斐波那契数列的第n项: ");scanf("%d", &n);// 检查输入值是否合法if (n < 1){printf("输入的数字必须大于等于1。\n");return 1; // 退出程序}// 调用 fib 函数计算斐波那契数列的第 n 项,并输出结果printf("%d 的斐波那契数是:%d\n", n, fib(n));return 0;
}// 函数声明:计算斐波那契数列的第 n 项
int fib(int n)
{// 基准情况:如果 n 是 1 或 2,则斐波那契数为 1if (n == 1 || n == 2) // 或者 if (n <= 1){return 1;}// 递归情况:斐波那契数列的第 n 项是第 n-1 项和第 n-2 项之和else{return fib(n - 1) + fib(n - 2);}
}

        在 CMD 中多次运行程序,输出结果如下所示:

4.2 输出斐波那契数列的前 N 项

        现在要求编写一个程序,程序接收一个整数 n 作为输入,并输出斐波那契数列的前 n 项。

#include <stdio.h>// 函数声明:计算斐波那契数列的第 n 项
int fib(int n);int main()
{int n;printf("请输入一个正整数n,以获取斐波那契数列的第n项: ");scanf("%d", &n);// 检查输入值是否合法if (n < 1){printf("输入的数字必须大于等于1。\n");return 1; // 退出程序}// 循环输出斐波那契数列的前 n 项for (int i = 1; i <= n; i++){printf("%d ", fib(i)); // 调用 fib 函数并打印结果}printf("\n");return 0;
}// 函数声明:计算斐波那契数列的第 n 项
int fib(int n)
{// 基准情况:如果 n 是 1 或 2,则斐波那契数为 1if (n == 1 || n == 2) // 或者 if (n <= 1){return 1;}// 递归情况:斐波那契数列的第 n 项是第 n-1 项和第 n-2 项之和else{return fib(n - 1) + fib(n - 2);}
}

        在 CMD 中多次运行程序,输出结果如下所示:

4.3 逆推猴子吃桃问题

        有一堆桃子,猴子第一天吃了其中的一半,并多吃一个。以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天早晨,猴子想再吃时(注意:此时还没吃),发现只有 1 个桃子了。问:最初共多少个桃子?

        题目分析:根据题意,第 10 天早晨还剩下最后 1 个桃子,这意味着第 9 天猴子吃完桃子后,实际上只剩下了这 1 个桃子。因此,在第 9 天早晨(猴子还未吃桃前),桃子的数量应当是第 10 天早晨剩余桃子数量(1 个)加上 1,然后乘以 2 的结果。据此,第 9 天早晨桃子的数量为 (1+1)*2=4 ;同样地,第 8 天早晨(猴子还未吃桃前)的桃子数量可以通过第 9 天早晨(猴子还未吃桃前)的桃子数量来计算:(4+1)*2=10。以此类推,【当天桃子的初始数量=(明天桃子的初始数量 + 1)* 2 ,即 f(day) = [f(day+1)+1]*2】我们可以通过这种方法一直逆推回去,直到求得第 1 天早晨最初的桃子数量。

        为了实现这一逻辑,我们可以编写一个递归函数来帮助计算。

#include <stdio.h>// 定义递归函数,参数 day 表示当前计算的是第几天的桃子数量
int peachCount(int day)
{if (day == 10){ // 如果达到第 10 天,直接返回 1(题目已知条件)return 1;}else{// 前一天的初始桃子数量=(当天的初始桃子数量 + 1)* 2 ,即 f(day) = [f(day+1)+1]*2return (peachCount(day + 1) + 1) * 2;}
}int main()
{printf("第十天开始时桃子的数量(验证基准情况): %d \n", peachCount(10));// 第九天开始时的桃子数量printf("第九天开始时桃子的数量: %d \n", peachCount(9));// ... 以此类推,直到第一天printf("最初共有桃子 %d 个。\n", peachCount(1));return 0;
}

        输出结果如下所示:

4.4 汉诺塔问题

        汉诺塔(Tower of Hanoi)是一个源于印度古老传说的经典递归问题。它包含三根柱子和一系列不同大小的圆盘,这些圆盘原本按照大小顺序穿在一根柱子上,并且大的圆盘在下面,小的圆盘在上面。目标是将这些圆盘移动到另一根柱子上,同时满足以下规则:

  • 每次只能移动一个圆盘。
  • 在任何时候,较大的圆盘都不能放在较小的圆盘上面。
  • 可以使用第三根柱子作为辅助。

#include <stdio.h>// 函数声明
void hanoi(int n, char from_rod, char to_rod, char aux_rod);int main()
{int n;printf("请输入圆盘的数量: ");scanf("%d", &n);// 假设有三个柱子分别命名为 'A', 'B', 'C'// 我们需要将圆盘从柱子 A 移动到柱子 C,使用柱子 B 作为辅助printf("\nA:起始柱子 B: 辅助柱子 C:目标柱子\n");printf("A:起始柱子上的圆盘,从上到下(小盘到大盘),编号为: 1 2 3 …… n\n");printf("步骤如下所示:");hanoi(n, 'A', 'C', 'B');return 0;
}// 汉诺塔问题的递归实现
/*** @brief 使用递归函数解决汉诺塔问题** @param n 圆盘的数量* @param from_rod 起始柱子* @param to_rod 目标柱子* @param aux_rod 辅助柱子,递归时可以拿其他柱子当辅助,即参数顺序可以按照需要变化*/
void hanoi(int n, char from_rod, char to_rod, char aux_rod)
{if (n == 1){// 当只有一个圆盘时,直接将其从起始柱子移动到目标柱子printf("\n  移动圆盘 1 号从 %c 到 %c", from_rod, to_rod);return;}// 将上面的 n-1 个圆盘从起始柱子移动到辅助柱子hanoi(n - 1, from_rod, aux_rod, to_rod);// 将最大的圆盘(第 n 个)移动到目标柱子printf("\n  移动圆盘 %d 号从 %c 到 %c", n, from_rod, to_rod);// 最后将 n-1 个圆盘从辅助柱子移动到目标柱子hanoi(n - 1, aux_rod, to_rod, from_rod);
}

        当只有一个圆盘时,直接将其从起始柱子移动到目标柱子。

        程序输出结果如下所示:

        当只有两个圆盘时,圆盘移动步骤如下图所示:

        程序输出结果如下所示:

        当只有三个圆盘时,圆盘移动步骤如下图所示:

        程序输出结果如下所示:

4.5 反转字符串

        编写一个递归函数来反转一个字符串。例如,输入字符串 "hello",输出 "olleh"。

#include <stdio.h>
#include <string.h>// 声明递归函数
void reverseString(char *str, int start, int end);int main()
{char str[] = "hello";int length = strlen(str);printf("反转之前的字符串为: %s\n", str);// 调用递归函数,注意结束索引为 length-1reverseString(str, 0, length - 1);printf("反转之后的字符串为: %s\n", str);return 0;
}// 递归函数定义
/*** @brief 反转字符串** 该函数使用递归方式反转字符串中从 start 索引到 end 索引(包含)之间的字符。* 注意,字符串的索引从 0 开始,因此 start 通常是 0,而 end 是字符串长度减 1。** @param str 指向要反转的字符串的指针* @param start 反转开始的索引(包含)* @param end 反转结束的索引(包含)** 注意:该函数会直接修改传入的字符串。*/
void reverseString(char *str, int start, int end)
{// 递归终止条件if (start >= end){return;}// 交换字符char temp = str[start];str[start] = str[end];str[end] = temp;// 递归调用,只改变 start 的值reverseString(str, start + 1, end);
}

        输出结果如下所示:

4.6 求数字之和

        输入一个非负整数,返回组成它的数字之和。例如,对于输入 1234,输出为 1+2+3+4=10。

4.6.1 循环实现

#include <stdio.h>// 函数声明
int sumOfDigits(int n);int main()
{int num;printf("请输入一个非负整数: ");scanf("%d", &num);                            // 读取用户输入的非负整数printf("数字之和为: %d\n", sumOfDigits(num)); // 调用函数并打印结果return 0;
}// 使用循环实现计算数字之和的函数
int sumOfDigits(int n)
{int sum = 0; // 初始化总和为 0while (n > 0){                  // 当 n 大于 0 时循环sum += n % 10; // 将 n 的最低位加到 sum 上n /= 10;       // 去掉 n 的最低位}return sum; // 返回总和
}

        输出结果如下所示:

4.6.2 递归函数实现

#include <stdio.h>// 函数声明
int sumOfDigits(int num);int main()
{int num;printf("请输入一个非负整数: ");scanf("%d", &num);                            // 读取用户输入的非负整数printf("数字之和为: %d\n", sumOfDigits(num)); // 调用函数并打印结果return 0;
}// 使用递归实现计算数字之和的函数
/*** @brief 计算一个非负整数的各位数字之和** @param num 要计算的整数* @return 整数各位数字之和*/
int sumOfDigits(int num)
{// 递归终止条件:当 num 为 0 时,说明所有位数都已经被加过了if (num == 0){return 0;}// 递归调用,传入 num 除以 10 的结果(去掉最低位),并加上 num 的最低位(num % 10)return sumOfDigits(num / 10) + (num % 10);
}

        程序分析图如下所示:

4.7 求两个数的最大公约数(辗转相除法)

        最大公约数是两个或多个整数共有的最大的那个正整数约数。欧几里得算法(也称为辗转相除法)是求解两个或多个整数的最大公约数(GCD,Greatest Common Divisor)的一种高效算法。

        欧几里得算法(也称为辗转相除法)的基本步骤是:对于两个正整数 a 和 b(假设 a > b),

  1. 计算 a 除以 b 的余数,记为 r。
  2. 如果 r 等于 0,则 b 就是 a 和 b 的最大公约数。
  3. 如果 r 不等于 0,则将 b 的值赋给 a,将 r 的值赋给 b,然后回到步骤 1。

        这个过程会重复进行,直到余数为 0,此时,最后的非零除数就是两个数的最大公约数。

以求 98 56 的最大公约数为例:98 / 56 = 1……42(余数)
56 / 42 = 1……14(余数)
42 / 14 = 3……0(余数)或者56 / 98 = 0………56(余数)
98 / 56 = 1……42(余数)
56 / 42 = 1……14(余数)
42 / 14 = 3……0(余数)所以 98 和 56 的最大公约数是 14

4.7.1 循环实现

        根据欧几里得算法的基本思想,可以通过在循环内部动态交换变量的值,不断地用较大的数除以较小的数,并取其余数,直到余数为零,从而逐步求出最大公约数。

#include <stdio.h>// 使用循环实现欧几里得算法
int gcd(int a, int b)
{while (b != 0){// 计算余数int r = a % b;// 交换数据a = b;b = r;}return a;
}int main()
{int num1, num2;// 输入两个数printf("请输入两个整数(用空格分隔): ");scanf("%d %d", &num1, &num2);// 调用 gcd 函数并打印结果printf("%d 和 %d 的最大公约数是: %d\n", num1, num2, gcd(num1, num2));return 0;
}

        输出结果如下所示:

4.7.2 递归函数实现

        使用递归函数实现欧几里得算法来计算两个数的最大公约数(GCD)是一种既经典又高效的解决方案,它通过递归调用简化问题规模,直至找到最大公约数。

#include <stdio.h>// 递归函数实现欧几里得算法
int gcd(int a, int b)
{// 基本情况:当 b 为 0 时,a 即为两数的最大公约数if (b == 0){return a;}// 递归调用,计算 b 和 a%b 的最大公约数return gcd(b, a % b);
}int main()
{int num1, num2;// 输入两个数printf("请输入两个整数(用空格分隔): ");scanf("%d %d", &num1, &num2);// 调用 gcd 函数并打印结果printf("%d 和 %d 的最大公约数是: %d\n", num1, num2, gcd(num1, num2));return 0;
}

        输出结果如下所示:


5 递归与循环的区别与联系

5.1 区别

语法结构:

  • 循环:使用诸如 for, while, do-while 等控制结构来反复执行一段代码块,直到满足某个终止条件
  • 递归:通过函数调用自身来解决问题的一部分,并逐步减少问题规模,直到达到基本情况

内存使用:

  • 循环:通常占用较少的栈空间,因为循环变量保存在栈上的开销相对较小。
  • 递归:可能导致大量的栈空间消耗,尤其是在递归层次较深的情况下,因为每次函数调用都需要分配新的栈帧。

执行效率:

  • 循环:一般而言,循环的执行效率较高,因为它没有函数调用的开销。
  • 递归:可能会有较高的函数调用开销,尤其是当递归层数较多时。

可读性和调试:

  • 循环:通常更容易理解和调试,因为其逻辑较为直观。
  • 递归:虽然简洁,但在某些情况下可能难以理解和调试,特别是当递归逻辑复杂时。

5.2 联系

        解决问题的能力:循环和递归都是解决需要重复执行某一任务的有效手段,它们可以用来实现相同的功能。

        转换可行性:大多数可以通过递归解决的问题,也可以通过循环来实现。实际上,任何递归算法都可以转换为等效的迭代算法(尽管可能不如递归版本那么直观)。

        设计模式:在设计算法时,选择使用循环还是递归往往取决于问题的具体需求和个人偏好。递归通常更适合于自然地递归分解的问题,而循环则适合于有明确边界条件的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/424355.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习结构体的使用

对于结构体的创建&#xff0c;首先要创建一个结构体类型&#xff0c;像下面这样&#xff1a; 结构体对成员的访问&#xff0c;就是变量名.成员就可以了 当然对于结构体的声明还有一些特殊的声明 结构体的自引用 结构体的重命名

RK3562/3588系列之5—其他实用工具

RK3562/3588系列之5—其他实用工具 1. vmware 虚拟机与主机之间共享文件夹2.RK3588开发板与电脑进行adb连接参考文献1. vmware 虚拟机与主机之间共享文件夹 使用此功能前需要提前安装好vmware tools。 启动虚拟机,依次进行如下操作,在主机创建共享文件夹。 然后就可以在如下…

Java | Leetcode Java题解之第404题左叶子之和

题目&#xff1a; 题解&#xff1a; class Solution {public int sumOfLeftLeaves(TreeNode root) {if (root null) {return 0;}Queue<TreeNode> queue new LinkedList<TreeNode>();queue.offer(root);int ans 0;while (!queue.isEmpty()) {TreeNode node que…

记录一下,Vcenter清理/storage/archive空间

一、根因 vpostgres&#xff1a;这个目录可能包含与 vCenter Server 使用的 PostgreSQL 数据库相关的归档文件过多&#xff0c;导致空间被占用。 二、处理过程 1、SSH登陆到Vcenter. 2、df -Th **图中可以看到 /storage/archive 使用占比很高。 /storage/archive 目录通常用…

三明儿童自闭症寄宿制学校:关爱、教育、成长一站式服务

三明儿童自闭症寄宿制学校的启示&#xff1a;广州星贝育园——自闭症儿童的一站式成长乐园 在探讨自闭症儿童教育的道路上&#xff0c;寄宿制学校以其独特的优势&#xff0c;为孩子们提供了集关爱、教育、成长于一体的全方位服务。虽然文章开头提及了“三明儿童自闭症寄宿制学…

Leetcode面试经典150题-79.搜索单词

题目比较简单&#xff0c;回溯最基础的题&#xff0c;记得除非覆盖&#xff0c;否则一定要恢复现场就行 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public boolean exist(char[][] board, String word) {int m board.length; int n board[0].length;i…

C++速通LeetCode简单第15题-有效的括号(全网最易懂代码注释)

解法分析&#xff1a; 越远的左括号匹配越远的右括号&#xff0c;越近的左括号匹配越近的右括号&#xff0c;正符合堆栈先进后出性质 。 因此利用堆栈保存左括号&#xff0c;通过循环逐个括号判断&#xff1a; class Solution { public:bool isValid(string s) {stack<…

Ubuntu20+Noetic+cartographer_ros编译部署

1 准备工作 &#xff08;1&#xff09;准备Ubuntu20系统。 &#xff08;2&#xff09;安装ROS系统,参考 https://blog.csdn.net/weixin_46123033/article/details/139527141&#xff08;3&#xff09;Cartographer相关软件包和源码下载&#xff1a; https://gitee.com/mrwan…

彻底理解浅拷贝和深拷贝

目录 浅拷贝实现 深拷贝实现自己手写 浅拷贝 浅拷贝是指创建一个新对象&#xff0c;这个对象具有原对象属性的精确副本 基本数据类型&#xff08;如字符串、数字等&#xff09;&#xff0c;在浅拷贝过程中它们是通过值传递的&#xff0c;而不是引用传递&#xff0c;修改值并不…

Java抽象类和接口的学习了解

目录 1. 抽象类 1.1 抽象类概念 1.2例子 1.3 抽象类语法 1.被 abstract 修饰的类--抽象类 2.抽象类中被 abstract 修饰的方法--抽象方法&#xff0c;该方法不用给出具体的实现体 3.当一个类中含有抽象方法时&#xff0c;该类必须要abstract修饰 4.抽象类也是类&#xff…

删除有序数组中的重复项(同向指针(快慢指针))

题目&#xff1a; 算法分析&#xff1a; 快慢指针从0出发若快慢指针不相同&#xff0c;快指针替换慢指针&#xff08;即慢指针后一位&#xff09;快指针每次都会增加题目求不重复的元素个数&#xff08;slow 为对应元素索引&#xff0c;故个数为slow1&#xff09; 算法图解…

算法练习题27——疫情下的电影院(模拟)

其实思路还好 就是输入有点难搞 Java import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);String input scanner.nextLine();// 去掉输入字符串的方括号if (input.…

html+css+js网页设计 旅游 大理旅游7个页面

htmlcssjs网页设计 旅游 大理旅游7个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

爬虫逆向学习(六):补环境过某数四代

声明&#xff1a;本篇文章内容是整理并分享在学习网上各位大佬的优秀知识后的实战与踩坑记录 引用博客&#xff1a; https://blog.csdn.net/shayuchaor/article/details/103629294 https://blog.csdn.net/qq_36291294/article/details/128600583 https://blog.csdn.net/weixin_…

浅谈Spring Cloud:认识微服务

SpringCloud就是分布式微服务架构的一站式解决方案&#xff0c;是微服务架构落地的多种技术的集合。 目录 微服务远程调用 Eureka注册中心 搭建Eureka Server 注册组件 服务拉取 当各种各样的服务越来越多&#xff0c;拆分的也越来越细&#xff0c;此时就会出现一个服务集…

【Vue】2

1 Vue 生命周期 Vue生命周期&#xff1a;一个 Vue 实例从 创建 到 销毁 的整个过程 创建(create)阶段&#xff1a;组件实例化时&#xff0c;初始化数据、事件、计算属性等挂载(mount)阶段&#xff1a;将模板渲染并挂载到 DOM 上更新(update)阶段&#xff1a;当数据发生变化时…

Python基础语法(3)上

函数 函数是什么 编程中的函数和数学中的函数有一定的相似之处. 数学上的函数&#xff0c;比如 y sin x&#xff0c;x 取不同的值&#xff0c;y 就会得到不同的结果 编程中的函数是一段可以被重复使用的代码片段 代码示例&#xff1a;求数列的和&#xff0c;不使用函数 …

【计算机网络 - 基础问题】每日 3 题(六)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

C++:STL详解(一)string类的基本介绍与使用方式

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;实践是检验真理的唯一标准&#xff01;&#xff01;&#xff01;敲代码需要勤快点&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff0…

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用&#xff0c;大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境&#xff0c;确保代码的环境一致性&#xff0c;从而不会在出现这段代码在我机器上没问…