• Coursera课程笔记----C程序设计进阶----Week 3


    函数的递归(Week 3)

    什么是递归

    • 引入
      • 函数可以嵌套调用:无论嵌套多少层,原理都一样
      • 函数不能嵌套定义:不能在一个函数里再定义另一个函数,因为所有函数一律平等
      • 问题:一个函数能调用它自己吗?
    • 举递归调用的简单例子
    #include<iostream>
    using namespace std;
    int fact(int n)
    {
      if(n == 1)
        return 1;
      else
        return n*fact(n-1);
    }
    int main(){
      cout<<fact(4)<<endl;
      return 0;
    }
    
    • 递归调用与普通的函数调用一样
      • 函数调用时,总是开辟新内存空间放调用的函数,递归调用同理

    深入理解递归过程

    #include<iostream>
    using namespace std;
    int recur()
    {
      char c;
      c = cin.get();
      if(c != '
    ')
        recur();
      cout<<c;
      return 0;
    }
    int main()
    {
      recur();
      return 0;
    }
    //若输入:abc 
    
    //打印顺序:
     cba
    

    递归的作用

    用递归完成递推

    • 再一次切饼
    #include<iostream>
    using namespace std;
    
    int q(int n)
    {
      if(n == 0)
        return 1;
      else
        return(n+q(n-1));
    }
    int main()
    {
      cout<<q(4)<<endl;
      return 0;
    }
    //只需递推式和边际条件就能写出递归
    
    • 递归与递推

      • 不同
        • 递推的关注点放在起始点条件(i=0 ➡️ i=n)
        • 递归的关注点放在求解目标上(i=n ➡️ i=0)
      • 相同
        • 重在表现第i次与第i+1次的关系
    • 用递归实现递推

      • 优点

        • 让程序变得简明
      • 方法

        • 把关注点放在要求解的目标

          从而

        • 找到第n次做第n-1次做之间的关系

        • 确定第1次的返回结果

    • 斐波那契数列

    #include <iostream>
    using namespace std;
    int f(int n)
    {
      if(n==1)
        return 1;
      if(n==2)
        return 2;
      else
        return(f(n-1)+f(n-2));
    }
    int main()
    {
      cout<<f(4)<<endl;
    }
    

    模拟连续发生的动作

    • 进制转换
    #include<iostream>
    using namespace std;
    void convert(int x)
    {
      if((x / 2) != 0)
      {
        convert(x / 2);
        cout<<x % 2; //把cout放在递归调用之后,就可以逆序打印出来
      }
      else
        cout<<x;
    }
    
    int main()
    {
      int x;
      cin >> x;
      convert(x);
      return 0;
    }
    
    
    
    • 汉诺塔问题
    #include <iostream>
    using namespace std;
    void move(int m, char x, char y, char z) //将m个盘子从A经过B移动到C
    {
      if(m == 1)
      {
        cout<<"把一个盘子"从<<x<<"移动到"<<z<<endl;
      }
      else
      {
        move(m-1,x,z,y);
    		cout<<"把一个盘子"从<<x<<"移动到"<<z<<endl;
        move(m-1,y,x,z);
      }
    }
    int main()
    {
      int n;
      cout<<"请输入盘数n=";
      cin >> n;
      cout<<"在三根柱子上移动"<<n<<"只盘的步骤为:"<<endl;
      move(n,'A','B','C');
      return 0;
    }
    
    • 思考的方法
      • 搞清楚连续发生的动作是什么(定义函数)
      • 搞清楚 不同次动作之间的关系(描述递归函数之间的关系)
      • 搞清楚边界条件是什么(递归退出的边际条件)

    进行“自动的分析”

    • 放苹果
      • 把M个苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
      • 注意:5,1,1和1,5,1是同一种放法
      • 输入:7,3
      • 假设:有一个函数f(m,n)能告诉我答案
        • 如果:n/盘子数 > M/苹果数: f(n>m)➡️f(m,n)=f(m,m)
        • 如果:M/苹果数>=n/盘子数
          • 有盘子空着:f(m,n) = f(m,n-1)
          • 没盘子空着:f(m,n) = f(m-n,n)
    #include <iostream>
    using namesapce std;
    int count(int m,int n)
    {
      if( m<= 1 || n <=0) return 1;
      if(m < n)
        return count(m,m);
      else
        return count(m,n-1) + count(m-n,n)
    }
    void main()
    {
      int apples,plates;
      cin >> apples >> plates;
      cout << count(apples,plates);
    }
    
    • 逆波兰表达式
      • 拟波兰表达式
        • 一种把运算符前置的算术表达式(2 + 3 ➡️ + 2 3)
        • 编写程序求解任意仅包含+ - * /四个运算符的逆波兰表达式
    #include <iostream>
    using namespace std;
    double notation()
    {
      char str[10];
      cin >> str;
      switch(str[0])
      {
          case '+' return notation()+notation();
          case '-' return notation()-notation();
          case '*' return notation()*notation();
          case '/' return notation()/notation();
        default: return atof(str);
      }
    }
    
    int main()
    {
      cout<<notation();
      return 0;
    }
    //注意这个题目的数据输入方式
    
    • 在以上复杂的场景中,递归帮我们进行了“自动分析”
      • 方法
        • 先假设有一个函数能给出答案
        • 在利用这个函数的前提下,分析如何解决问题
        • 搞清楚最简单的情况下,答案是什么

    习题

    Quiz1 单词翻转

    #include<iostream>
    using namespace std;
    int i = 0;
    char input[501];
    int recur() {
        char c = input[i];
        i++;
        if (c == ' ') {
            return 1;//*句
        }
        if (c != ' '&&c != '') {
            recur();
            cout << c;
        }
        return 1; //**句
    }
    int main() {
        cin.getline(input, 501);
        while (input[i] != '') {
            if (recur() == 1)//***句
                cout << ' ';
    
        }
        if (input[i] == '') {
            cout << endl;//最后输出换行符
            return 0;
        }
    }
    //详解:
    //以输入'Hello  World'为例,句***刚被执行时,i=0,当被执行的这次return返回来时,i=6;句***再次被执行时,i=7,当被执行的这次return返回来时,i=8(因为只取了一个空格)
    //遇到第一个空格时*句的return内容被"吞"掉了,所以需要**句的存在来补上这一个return
    //最终的输出是'olleh  dlrow',其中第一个空格来自**句,第二个空格来自*句
    

    Quiz2 角谷猜想

    #include <iostream>
    using namespace std;
    int JGG(int x)
    {
        if(x == 1)
        {
            cout<<"End"<<endl;
            return 0;
        }
        else if(x % 2 == 0)
        {
            cout << x << "/2="<< x/2<<endl;
            JGG(x/2);
        }
        else
        {
            cout << x <<"*3+1="<< (x*3+1)<<endl;
            JGG(x*3+1);
        }
        return 0;
    }
    
    int main()
    {
        int n;
        cin >> n;
        JGG(n);
        return 0;
    }
    

    Quiz3 排队游戏

    #include <iostream>
    using namespace std;
    char a[101]={''};
    int i=0,j=0;
    
    int cut()
    {
        int movement = 0;
        int flag = 0;
        for (int i = 0; i < 101; i++) {
            if (a[i] != '')
                flag = 1;
        }
    
        if(flag == 0)
            return 0;
        for (int i = 0; i < 101; i++) {
            if(a[i]!='')
            {
                for (int j = i+1; j < 101; j++) {
                    if(a[j] == '')
                        continue;
                    if(a[j] == a[i])
                        break;
                    if(a[j] != a[i])
                    {
                        cout<<i<<' '<<j<<endl;
                        a[i] = '';
                        a[j] = '';
                        movement = 1;
                        break;
                    }
                }
                if(movement == 1)
                    break;
            }
    
        }
    
        flag = 0;
        for (int i = 0; i < 101; i++) {
            if (a[i] != '')
                flag = 1;
        }
        if(flag == 1)
            cut();
    }
    
    int main()
    {
        cin.getline(a,100,'
    ');
        cut();
        return 0;
    }
    

    Quiz4 扩号匹配问题

    #include <iostream>
    #include <stack>
    
    //双返回值的递归方法我实在有点看不懂(也不知道有没有非看懂不可的必要
    //反正用堆栈就能很好的解决了……
    using namespace std;
    int main()
    {
        string str;
        while(cin >> str) {
    
    
            char simple[101];
            for (int i = 0; i < str.length(); i++) {
                if (str[i] == '(')
                    simple[i] = '$';
                else if (str[i] == ')')
                    simple[i] = '?';
                else simple[i] = ' ';
            }
    
            stack<char> s;
            stack<int> index;
            for (int i = 0; i < str.length(); i++) {
                if (str[i] == '(') {
                    s.push('(');
                    index.push(i);
                }
                if (str[i] == ')') {
                    if (!s.empty() && s.top() == '(') //&&两边的两个条件必须且顺序不能改!!!!!
                    {
                        simple[index.top()] = ' ';
                        index.pop();
                        s.pop();
                        simple[i] = ' ';
                    } else {
                        s.push(')');
                        index.push(i);
                    }
                }
            }
    
            cout << str << endl;
            for (int i = 0; i < str.length(); i++) {
                cout << simple[i];
            }
            cout<<'
    ';
    
        }
        return 0;
    }
    
  • 相关阅读:
    UI组件库Kendo UI for Vue原生组件中文教程 如何开始制作动画
    高性能HTML5/JavaScript开发框架DevExtreme v22.1.3正式发布
    界面组件Telerik UI for WPF入门指南 如何使用主题切换自定义样式
    如何在MVVM场景中使用WinUI数据网格?这个工具可以搞定
    Spring Boot设置上传文件大小
    SpringBoot SpringBoot 定时器的三种方式定时器的三种方式
    SpringBoot文件上传
    vscode快速生成vue模板
    Failed to execute goal org.apache.maven.plugins:mavensurefireplugin:2.12.4
    There are no resources that can be added or removed from the server
  • 原文地址:https://www.cnblogs.com/maimai-d/p/12824965.html
Copyright © 2020-2023  润新知