(C++)递归算法的学习及其案例(斐波那契数列、阶乘、二分查找)

  • 递归的定义:

递归是方法自身调用自身的一种技术,它通常用于解决需要反复执行相同操作的问题。

在递归中,一个问题被分解为更小的子问题,然后使用递归函数来逐个解决这些子问题,直到达到基本情况。

  • 递归的基本要素:

两个基本要素:递归函数终止条件

递归函数定义了如何通过调用自身来解决子问题;

终止条件则是在问题规模缩小到一定程度时停止递归调用。

  • 递归的适用场景:

递归通常用于解决树形结构分治策略的问题。

例如,二叉树的遍历、排序、搜索等问题,以及分治算法中的合并排序、快速排序等。

  • 递归的优缺点:

优点:代码简洁、易于理解、符合人的思维习惯等;

缺点:存在栈溢出、空间开销大等。

因此,在使用递归时需要注意控制递归深度避免出现栈溢出的情况。

  • 递归的调试技巧:

由于递归函数的调用自上而下的,因此调试递归函数时可以采用自下而上的方法,从终止条件开始逐步调试,直到解决完整的子问题。

  • 案例:

  • 斐波那契数列

//它是一个由0和1开始,后面的每一项都由前两项相加得到的数列。斐波那契数列的前几项为:0、1、1、2、3、5、8、13、21、34、……
#include <iostream>  
using namespace std;  
//定义 fibonacci(n) 函数:
//这个函数使用递归的方式实现。并且递归意味着一个函数调用自己。
int fibonacci(int n) {  
    if (n <= 1) {  
        return n;  //当n小于或等于1时,返回n本身。
    } else {  
        return fibonacci(n-1) + fibonacci(n-2);  
      //否则,递归调用fibonacci(n-1)和fibonacci(n-2),并将它们的结果相加返回。
    }  
}  
//在主函数中,我们使用一个循环输出斐波那契数列的前n项。  
int main() {  
    int n = 10; // 输出斐波那契数列的前n项  
    cout << "Fibonacci sequence of " << n << " numbers: ";  
    for (int i = 0; i < n; i++) {
      //这个循环从0开始,一直到n-1。这是因为在斐波那契数列中,第0项是0,第1项是1,以此类推,直到第n-1项。所以循环从0开始是正确的。如果我们想包括第n项,那么循环应该一直运行到n。
        cout << fibonacci(i) << " ";  
    }  //将返回的数字打印到屏幕上,后面跟着一个空格。
    cout << endl;  //endl作用:插入一个换行符
    return 0;  
}
  • 阶乘

#include <iostream>  
using namespace std;  
//定义一个名为factorial的函数,该函数接受一个整数参数n并返回一个整数结果。
int factorial(int n) {  
    if (n == 0) {  
        return 1;  //如果参数n为0,则返回1,因为0的阶乘定义为1。
    } else {  
        return n * factorial(n-1);  
//递归调用factorial函数,传入参数n-1,并将结果乘以n,直到递归到参数为0的情况,然后返回1。
//我的理解:从最大的开始递归:5*factorial(4),然后5*4*factorial(3)...递归至n=0时。
                                            //前面的不变,后面的疯狂展开
    }  
}  
  
int main() {  
    int num = 5;  
    cout << "The factorial of " << num << " is " << factorial(num) << endl;  
    return 0;  
}
  • 二分查找

//二分查找是一种高效的搜索算法,其时间复杂度为O(log n)。它通过将搜索范围不断缩小来找到目标元素。
#include <iostream>  
using namespace std;  
//定义一个名为binarySearch的函数,该函数接受四个参数:一个整数数组arr、左边界left、右边界right和一个目标元素target。 
int binarySearch(int arr[], int left, int right, int target) {  
    if (right >= left) {  //确保搜索范围有效。
        int mid = left + (right - left) / 2;  //计算中间元素的索引。
/*1.中间元素的索引是 left + (right - left) / 2 而不是 (left + right) / 2。原因是 (left + right) / 2 在某些情况下会产生溢出。当 left 和 right 的值非常大时,它们的和可能超过了整型数值的上限,导致整数溢出。
2.使用 (right - left) / 2;而不是 right - left 可以保证当 right 和 left 相等时,不会出现除0错误。因为当 right 和 left 相等时,表示数组中只有一个元素,此时中间元素的索引应该是 left。*/
        if (arr[mid] == target) {  //中间元素=目标元素
            return mid;  //找到了
        } else if (arr[mid] > target) {  //中间元素>目标元素
            return binarySearch(arr, left, mid - 1, target);  //将右边界的元素删除,在左边进行查找目标元素
          //排除了 arr[mid],但保留了 arr[mid - 1]。
/*传入 mid - 1 作为右边界,而不是 mid。这是因为我们希望在下一次递归调用中,将搜索区间缩小为原来的一半,即 [left, mid - 1]。
如果传入 mid,则搜索区间会变为 [left, mid],这将导致我们查找的元素被排除在外,因为我们在计算 mid 时已经将 arr[mid] 排除在外了。*/
        } else {  
            return binarySearch(arr, mid + 1, right, target);  //否之,中间元素小于目标元素,在右半部分继续搜索。
         //排除了 arr[mid],但保留了 arr[mid + 1]。同理
        }  
    }  
    return -1;  //如果没有找到目标元素,返回-1。
}  
  
int main() {  
    int arr[] = {2, 3, 4, 10, 40};  
    int n = sizeof(arr) / sizeof(arr[0]);
  //sizeof(arr) 返回整个数组的字节大小;
  //sizeof(arr[0]) 返回数组中单个元素的字节大小。将两者相除,就可以得到数组中元素的数量。
    int target = 10;  //定义目标元素
    int result = binarySearch(arr, 0, n - 1, target);  //调用binarySearch函数并传入数组、搜索范围的左右边界和目标元素作为参数。
  //左边界 0 右边界 n-1。因为:索引都是从0开始
    if (result == -1) {  
        cout << "未找到目标元素";  
    } else {  
        cout << "找到目标元素的索引为: " << result << endl;  
    }  
    return 0;  
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值