关于递归的一些简单想法

我的主力博客:半亩方塘

递归是我们在编程过程中用到的一种思想,当一个函数自身调用自身的时候,无论是直接或者间接地调用,都属于递归,下面对于什么时候用到递归以及怎么用递归,谈一点我个人初步的想法。

  1. 什么时候用到递归

    当我们要解决的问题有着 重复执行的基本操作 的时候,可以考虑使用递归

  2. 用递归思想进行编程的时候需主要需要注意的几点内容

    首先是 递归上限 ,通常是一个指出递归开始位置的 有效范围内 的对象,一般作为主调函数传输的参数;其次是递归下限 ,主要用来确定结束递归的操作,一定要有退出递归的标志性操作,否则,递归将无限期地进行下去;最后就是确定实现递归的 基本操作 和 由递归上限到递归下限的变化过程 ,基本操作在确定使用递归的时候已经考虑好了,需要特别考虑的是由递归上限到递归下限的变化过程,极易出错

下面通过两个例子来加深对上面这段话的理解(C++ 实现):

(1)求一个数的阶乘

首先,是不是可以用到递归?求一个数的阶乘的时候,重复涉及到两个数的乘法,这是一个 重复执行的基本操作,由此确定此问题可以用递归实现;其次,递归上限是什么?因为阶乘实现的是从
1 到这个数之间所有数的累乘,所以, 递归上限 就是这个数本身,同样可以得到递归下限是 1,最后,需要确定从递归上限到递归下限的变化过程,显然,这个变化过程就是这个数本身(递归上限)到
1(递归下限) 之间不断减 1 的过程,也就是主调函数传输的参数不断减 1 的过程,经过以上分析后,很轻松地就可以写出下面的代码

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

// 实现阶乘操作的递归函数
int factorial (int val)
{
    if (val > 1)
        return factorial(val - 1) * val;  // val - 1 使得递归上限向递归下限不断接近
    else                // 其实 else 也可以不写,想想为什么?
        return 1;
}

int main()
{
    int ival = 0;
    cout << "输入要求阶乘的数: ";
    cin >> ival;
    int res = factorial(ival);
    cout << ival << "! 等于 "
         << res << endl;

    return 0;
}

对于上面的代码,实现阶乘操作的递归函数中,else 可以不写,因为,当传递给 factorial 的参数等于 1 时,函数返回 1 后,结束递归,进入倒数第二层的 factorial 函数的执行,倒数第二层 return
factorial(val - 1) * val
 之后,即结束倒数第二层的函数调用,在没有 else 的情况下,并不会执行到 return 1,其他层的调用同理,所以,不需要
else ,程序也是正确的,这里写上 else ,只不过为了使得程序的逻辑结构更加清晰一些,另外,return factorial(val - 1) * val 中的 val
- 1
 切不可换为 --val,如果换成 --val 的话,改变的不仅仅是传递给下一层
factorial 的参数 val,连同本层的 val 也一起改变了,无疑这将引起错误。

(2) 输出 vector 对象的内容

首先,是不是可以用递归呢?要不断输出 vector 对象的内容,这就是一个 重复执行的基本操作 ,因此可以用递归思想进行实现,其次,递归的上限是什么呢?有人说,是
vector 对象尾后元素的指针,可是,真的是这个吗?这是不对的,尾后元素的指针并不指向一个有效范围内的对象,它指向的存储空间存储的值是未定义的,这一点尤其要注意,所以,真正的递归上限应该是 vector对象的尾后指针减
1 ,这才是真正有效范围内的对象,显然,递归下限是指向 vector 对象首元素的指针,最后,从递归上限到递归下限的变化过程是递归上限不断减 1 向递归下限逼近,直至最后等于递归下限的值为止,同样轻松地写出了以下的代码

#include <iostream>
#include <vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;

// 实现打印 vector 对象元素的递归函数
void print(const vector<int> &vec,
    decltype(vec.begin()) ibeg, decltype(vec.begin()) iend)
{
    if (iend != ibeg)
        print(vec, ibeg, iend - 1);  // iend - 1 使得递归上限向下限不断逼近
    cout << *iend << " ";   // 在这之前不能有 else
} 

int main()
{
    vector<int> ivec;
    int ival;
    cout << "请依次输入存放到vector对象中的 10 个整数: " << endl;
    for (unsigned i = 0; i != 10; ++i) {
        cin >> ival;
        ivec.push_back(ival);
    }

    // 打印这 10 个数
    print(ivec, ivec.begin(), ivec.end() - 1);  // 真正的递归上限应该是 vector 对象的尾后指针减 1
    cout << endl;

    return 0;
}

同理,在实现打印 vector 对象元素的递归函数中 iend - 1 不能换成 --iend,因为这样会改变本层函数中的
iend 参数的值,会得不到期望中的结果,形参中 vector 对象的类型是引用类型,避免了拷贝所引起的时间效率低下和存储空间的浪费,同时,使用 const 是因为不需要改变元素的值,仅仅对元素进行输出操作

以上是我对于递归思想运用的一些简单想法,随着以后逐步深入的了解会有相应的博文推出,欢迎提供宝贵意见。

关于递归的一些简单想法

时间: 2024-10-26 03:17:59

关于递归的一些简单想法的相关文章

自行学习XAML控件后的简单想法(作业一)

由于自身专业水平的欠佳,我对于XAML控件的学习并不深刻,只在简单了解过后产生了一二想法,也许十分荒谬,就减省地谈谈.以下五种控件,是我在学习后,并不十分看好或有所疑虑的. Canves 在浏览XAML Controls Gallery上各种控件的简单介绍时,这个控件引起我注意,它通过坐标轴控制每个内容的分布,是一个用于布局的控件. 首先,这个Canvas控件的展示样例图,让我想起了很多应用中的画面,例如:                              然而在我查询相关资料后,我发现

权限的简单想法

1.权限每个人进来看到不同的内容,有着不同的功能.比如boss可以设置下面人的权限,权限放大,权限缩小.开辟新权限,分配角色,角色分配人员,权限分配这些等等. 2.具体的shiro还是喜欢自己做权限的还是自己决定吧,用了shiro,退出之后,起作用啦.自己做权限ajax即使的做刷新,用着是方便,就是太费时间. 3.举个例子,boss给一个部门经理一些权限,部门经理可以把自己的权限发放给其他人,比如手下的高管..等等.这个比较的细致.(我自己的权限可以分派给其他的人员) 4.sql语句.如下: 2

母函数的一些简单想法(HDU2110)

// 母函数解决的问题 // n 种物品,每个有一个wi,组合成total价值有多少种组合方案 // 将组合问题转换为 幂级数上的相乘问题(important) (Orz) // #include<iostream> // #include<cstdio> // #include<cstring> // using namespace std; // int n,a[105],b[105],m,s[10010],t[10010]; // int main() // {

c++,继承,也称基类,派生类,自己研究吧,很简单想法

#include <iostream>using namespace std;//基类Peopleclass People{public: People(char *name, int age); void display();protected: char *m_name; int m_age;};People::People(char *name, int age): m_name(name), m_age(age){}void People::display(){ cout<<

由递归思想处理问题的基本原则

我的主力博客:半亩方塘 在我的博文关于递归的一些简单想法,我用自己的理解谈了一些关于递归的看法,下面用 <数据结构与算法分析--C语言描述> 一书中第 9 页的四条基本原则将我的思想加以规范化,并在后续的不断加强学习中不断完善本文的内容. 在编写递归程序的时候,要牢记递归的四条基本原则: 基准情形:必须总有某些基准情形,它无需递归就能解出 -- 构成递归终止条件 不断推进:对于那些需要递归求解的情形,每一次递归调用都必须要使求解状况朝接近基准情形的方向推进 -- 由初始情形通过递归调用不断向递

分治策略结合递归思想求最大子序列和

我的主力博客:半亩方塘 对于 <数据结构与算法分析--C语言描述> 一书第 20 页所描述的算法 3,相信会有很多人表示不怎么理解,下面我由具体问题的求解过程出发,谈谈我自己的理解: 首先,什么是分治法呢?所谓 分治法,就是 将一个问题的求解过程分解为两个大小相等的子问题进行求解,如果分解后的子问题本身也可以分解的话,则将这个分解的过程进行下去,直至最后得到的子问题不能再分解为止,最后将子问题的解逐步合并并可能做一些少量的附加工作,得到最后整个问题的解.在求解原来整个问题的算法思想,与求解每一

C#基础:用简单的文件管理程序来理解应用递归

最近在紧张的学习C#,说实话对C#之前没有太多的接触过,只知道C#的特性与java很相似,接触了之后才发现C#跟java相比区别不是很多,但它是一门实现程序能力比Java还要好的语言(仅代表个人观点). 有许多新手在学习编程语言的时候,都会在递归上面卡住,理解和应用起来会十分的吃力,所以我就自己尝试用递归写了一个很简单很简单很简单的文件管理程序,说它简单是因为他真的没有什么难度,都是很底层的循环和递归,也就只有130多行代码,只是希望能够帮助大家理解应用递归.如果你一点编程基础木有,那请不要直接

关于递归和动态规划的简单理解

1.递归的定义 简单的来说,递归就是一个概念能够用自身来解释,比如说一本字典,每个字词的解释是依靠字典中其他的字词来解释的.一般来说,计算机中遇到的递归问题大多是把一个问题分解成规模更小的子问题求解,再进行合并. 递归的性质 一个具有递归性质的问题,大多具有两个特征,第一个是状态转移方程也就是递归方程,比如在求解阶乘时,n!=n*(n-1)!,就将求解n的阶乘转换为求解n-1的阶乘.第二个特征就是终止条件,一个递归是一类问题的求解,必定有一个结果 无法一只递归下去,有一个结束条件,也就是当问题规

二叉树序言、为了、经过非递归措辞预订透彻的分析

前言 前两篇文章二叉树和二叉搜索树中已经涉及到了二叉树的三种遍历.递归写法,仅仅要理解思想,几行代码.但是非递归写法却非常不easy.这里特地总结下,透彻解析它们的非递归写法.当中.中序遍历的非递归写法最简单,后序遍历最难.我们的讨论基础是这种: //Binary Tree Node typedef struct node { int data; struct node* lchild; //左孩子 struct node* rchild; //右孩子 }BTNode; 首先.有一点是明白的:非