自学内容网 自学内容网

C语言-函数


0.引入 

    例子: 
        int a[10];
        int i;
        for( i=0; i<10; i++ )
        {
            scanf("%d", &a[i] );
        }

        int b[5];
        for( i=0; i<5; i++ )
        {
            scanf("%d", &b[i] );
        }

        int c[7];
        int d[20];
        .... 

        在程序中,,需要给多个数组从键盘上获取值 

        以上的代码, 功能都是一样, 都是为了从键盘上获取值 
            不一样的是 数组名 和 数组元素的个数, 代码形式也是一样的 

            对于同一个功能 而 参数不同的 代码块, 我们就可以用函数 来进行概括 
                函数 --> 重复利用代码块


1.函数 
    function  功能 

    函数 是完成某一个功能的指令序列的封装 
        C语言的指令必须在函数的内部

        函数可以实现代码复用, 以及实现 模块化的设计 

        函数的特点: 
            相同功能的代码块,重复利用,模块化设计思想


2.如何来封装函数? 

    函数是用来实现某一个功能或者任务 
        实现任务: 
            之前: 我们考虑完成这个任务需要哪些资源? 是0个还是多个资源? 
            中间: 思路(算法)
            之后: 完成这个任务能给给我什么影响? 

    
    设计函数的步骤: 
        (1)明确函数的功能是什么? (函数功能) 
            起函数名:   
                符合C语言合法标识符的定义规则, 尽量做到 "见其名 知其意" 

        (2)考虑完成该函数需要哪些已知条件/资源? (输入参数)
            输入完成该任务所需要的参数 
                例如: 
                    完成一个任务"找数组中的最大值"
                    此时,编译器完成这个任务时 需要知道该数组的信息 
                        比如说这个哪个数组的最大值(数组名)? 这是一个什么样的数组(数组元素的类型)?
                            这个数组有多大(元素个数)?

        (3)考虑完成该任务之后的输出结果? (返回值)
            确定函数的返回值的类型
                例如: 
                    完成一个任务"找数组中的最大值"
                    那么当任务完成之后,如果找到了这个最大值,
                    我们是希望编译器能够告诉我这个最大值到底是多少? 

        (4)算法实现 以及 调试部分 


            例子: 
                完成一个任务: 求两个整数之和 
                    (1)明确功能 
                        起函数名:  --->  sum() 
                    (2)找出完成该任务所需要的资源
                        输入参数:  --->  两个整数 
                                  --->  sum( int a, int b )
                    (3)完成任务后的输出结果 
                        确定函数的返回值的类型 
                        结果: 之和  ---> int 
                                    ---> 
                                        int sum( int a, int b )
                                        {
                                        }
                    (4)算法实现以及调试部分 
                            int sum( int a, int b )
                            {   
                                //算法实现 
                                int s; 
                                s = a + b;
                                return s;
                            }


3.在C语言中 函数的定义语法 

    语法: 
            返回值类型 函数名(参数列表)         //函数头
            {
                C语句;
                return 表达式;
            }

                函数的三要素: 
                    "返回值类型" : 
                            return语句 后面的那个表达式的值 的类型
                                一般是"单值类型", 即基本类型和指针类型
                                函数也可以不返回值的  ---> void 
                                不指定函数的返回值类型  ---> 默认为 int 

                    "函数名"  
                            符合C语言合法标识符的定义规则, 尽量做到 "见其名 知其意"

                    "参数列表" 
                            类型1 参数名1, 类型2 参数名2, .... 
                            为void 表示该函数不带任何参数, 此时void可省略 

                    return  
                            返回的意思, return只能在函数内部, 表示函数返回/结束的意思

                            return 表达式;      //表示函数结束,带有一个返回值,这个返回值就是表达式的值 
                                                //函数的返回值类型 就是return语句后面的表达式值的类型 

                            return ;            //后面不带表达式 表示函数结束,不带任何返回值 

                注意: 
                    函数不能嵌套定义,也就是说函数不能定义在别的函数里面,只能定义在外面 
            
                练习: 
                    1)设计一个函数,求两个整数之差 

                        int sub( int a, int b )
                        {
                            return a>b ? a-b : b-a ;
                        }


4.函数的调用 

    函数调用, 调用一个已经写好的函数 去执行 

    1)如何调用函数?
        (1)需要指定是哪个函数   ----> 函数名 
        (2)指定完成该函数的参数 ----> 实际参数, 简称:实参 

                主调函数 : 调用其他函数的这个函数 , 比如: main() 
                被调函数 : 被其他函数所调用的那个函数, 比如: sub()      "相对的概念"

                实际参数 (实参) 
                        在函数调用的过程中, 主调函数传递给被调函数的参数的值, 比如: sub( 5 , 2 );

                形式参数 (形参) 
                        被调函数 定义的时候的参数, 简称:形参, 比如: sub( int a, int b ) 

        注意: 
            (1)在函数调用时, 需要指定"实参", 并且 实参和形参 要一一对应( 数据类型, 参数个数 )

            (2)在C语言中, 函数的参数的传递 只有一种情况: "值传递" 
                            就把实参的值传递给相应的形参 
                                "值传递" : 形参 = 实参;         //赋值 


            例子: 
                int sum(int a, int b)
                {
                    return a+b;
                }

                int m=3, n=5;

                sum( 3, 5 );        // OK  8 
                sum( 2+3, 5+6 );    // OK  16   // 先会计算出实参的值, 再将实参的值 传递给 形参
                sum( m, n );        // OK  8    
                sum( m*n+3, m/n+4 );    //  OK 22 
                sum( sum(4,6), 5 );     //  OK 15 
                                        //  sum(4,6) ==> 10  "函数调用表达式" --> 该表达式的值就该函数的返回值 
                                        //  sum( 10 , 5 ) ==> 15
                int c = sum( 1, 2 );    //  Ok  c ==> 3

                sum( 4, 6, 5 );         // error 


    2)函数调用的过程 

        int sum( int a, int b )
        {
            int s;
            s = a + b;
            return s;
        }

        int main()
        {
            int m=3, n=4;
            int c = sum( m, n );
            printf("c = %d\n", c );     //7 
            return 0;
        }

        函数调用的过程: 
            (1)把实参的值传递给形参 (先会计算出实参的值, 再传递给形参)
            (2)跳转到指定的被调函数去执行, 直到遇到return或者函数执行完毕,
                再返回到主调函数 调用的位置 
                return后面的表达式的值, 将作为整个"函数调用表达式"的值 

5.数组作为函数的参数 

    当一个函数的参数是数组时, 该如何去描述? 

    1)一维数组 
            数据元素的类型 数组名[整型表达式];

            int a[10];   

                需要定义两个变量: 
                    m代码a这个数组的名字 
                    n代码a这个数组的元素个数
            ==> 
                int xxx( int m[], int n )
                {
                }

                调用时:  xxx( a, 10 ); 

    
    2)二维数组 
            int b[3][4];    //数组b实际上就是一个一维数组, 里面有3个元素, 每一个元素都是 int [4] 类型 

                需要定义两个变量: 
                    a代码b这个数组的名字 
                    n代码b这个数组的元素个数
            ===> 
                int yyy( int a[][4], int n )
                {
                }
                调用时:  yyy( b, 3 );

            ===> 
                m代表行 
                n代表列
                int zzz( int m, int n, int a[][n] )
                {
                }
                调用时: zzz( 3, 4, b );


            练习: 
                1)写一个函数, 实现对一个一维整型数组的输入(从键盘上获取值)
                    再写一个函数,实现对一个一维整型数组的输出(打印到终端上)

                        void input_array( int a[], int n )
                        {
                            int i;
                            for( i=0; i<n; i++ )
                            {
                                scanf("%d", &a[i] );
                            }
                        }

                        void output_array( int a[], int n )
                        {
                            int i;
                            for( i=0; i<n; i++ )
                            {
                                printf("%d ", a[i] );
                            }
                            putchar('\n');
                        }

                        int main()
                        {
                            int a[5]; 
                            input_array( a, 5 );
                            output_array( a, 5 );

                            int b[7];
                            input_array( b, 7 );
                            output_array( b, 7 );
                        } 

                2)设计一个函数, 求一个一维数组中的最大值,通过返回值将最大值返回

                        (1) find_array_max() 
                        (2) find_array_max( int a[], int n )
                        (3) int find_array_max( int a[], int n )
                        (4) 实现 

                    ==> 
                        int find_array_max( int a[], int n )
                        {
                            int max = a[0];     //记录最大值 
                            int i;
                            for( i=0; i<n; i++ )    //遍历数组
                            {
                                if( a[i] > max )
                                {
                                    max = a[i];
                                }
                            }
                            return max;
                        }

6.函数声明 

    "声明" : 
        在C语言中, 声明 是用来声明一个已经存在的标识符(对象的名字)
                声明就是用来表示一个标识符(对象的名字)到底是什么东西 

        为什么需要声明? 
            C语言编译源文件时, 是从第一行到最后一行, 一行一行的进行编译
                而且在编译多个文件的时候, 也是一个文件一个文件的编译的 

                有时候1.c可能会用到2.c中定义的对象(变量/函数等)
                在编译1.c时,碰到这个对象的名字, c语言编译器就可能不认识这个标识符,
                    即使是在同一个文件中, 在前面碰到的标识符,而这个标识符的定义在后面 
                此时 编译器也会不知道这个标识符是什么东西

            一般来说, 在源文件的前面 要进行标识符的声明 
                约定: 
                    将声明 放在 使用的前面 

    声明的格式: 
        (1) 外部变量的声明 
                extern 变量的类型 变量名;       //外部变量声明时, 不能给它初始化    

        (2) 函数的声明 

            (2.1) 外部函数的声明: 
                extern  函数的返回值类型 函数名(参数列表);          //extern 函数头;

            (2.2)本文件内部的 函数的声明 
                函数的返回值类型 函数名(参数列表);                  // 函数头;
    
                    例子: 
                        04外部变量1.c    05外部变量2.c  

            注意: 
                函数声明,形参的名字是可以省略的,但是类型不能省略 
                    int sum( int x, int y );    // OK 
                    int sum( int, int);         // OK 

                    int sum( x, y );            // error 

        
            ====================================
                .h 头文件  (接口文件) 

                    头文件的格式: 
                        例子: sum.h  

                            #ifndef __SUM_H__           //防止重复包含
                            #define __SUM_H__ 

                            //头文件 一般包含 宏定义, 函数声明, 类型构造等 

                            #endif

                    头文件的引用 
                        #include <>  从系统标准的头文件路径下进行搜索   (如: /usr/include/ )
                        #include ""  先从当前的工程路径下进行搜索, 再从系统标准的头文件路径下进行搜索


        练习: 
            1)写一个函数, 判断一个一维整型数组是否有序 (升序||降序)
                返回值: 
                    1   有序 
                    0   无序 

                    (1) is_sort() 
                    (2) is_sort( int a[], int n )
                    (3) int is_sort( int a[], int n )
                    (4) 实现 


                ===> 
                        int is_sort( int a[], int n ) 
                        {
                            int flag = 1;   // 升序 标志位 
                            int sign = 1;   // 降序 标志位

                            int i;
                            for( i=0; i<n-1; i++ )
                            {
                                //升序 
                                if( a[i] > a[i+1] )     //找出一个反例 
                                {
                                    flag = 0;
                                }

                                //降序 
                                if( a[i] < a[i+1] )   //找出一个反例 
                                {
                                    sign = 0;
                                }
                            }

                            if( flag || sign )
                            {
                                return 1;   //有序
                            }
                            else 
                            {
                                return 0;   //无序
                            }
                        }


            2) 将一个整数x, 插入到一个递增数组中, 使得插入后的数组仍然有序 
                    例子: 
                        int a[10] = {1,3,5,7,9,11,13,15,17  };

                        x = 6;
                        ---> 1,3,5,6,7,9,11,13,15,17

                        思路: 
                            找到待插入的位置 (找到第一个比它大的数的前面进行插入)
                            进行插入操作 

                        算法一: 直接插入排序 

                            int main()
                            {
                                int a[10] = {1,3,5,7,9,11,13,15,17  };
                                int x;
                                scanf("%d", &x );

                                //1.找到待插入的位置 (找到第一个比它大的数的前面进行插入)
                                int i,j;
                                for( i=0; i<9; i++ )
                                {
                                    if( a[i] > x )
                                    {
                                        //找到了 
                                        break;
                                    }
                                }

                                //找到了 , 进行插入操作 
                                if( i != 9 )
                                {
                                    //先把原来的数据 往后移 
                                    for( j=9; j>i; j-- )
                                    {
                                        a[j] = a[j-1];
                                    }
                                }

                                //再进行插入  (没有找到i==9,说明x就是最大的, 直接将x放在末尾 )
                                a[i] = x;

                                for( i=0; i<10; i++ )
                                {
                                    printf("%d ", a[i] );
                                }
                                putchar('\n');
                            }

                        
                        算法二: 二分插入 

                            int main()
                            {
                                int a[10] = {1,3,5,7,9,11,13,15,17  };
                                int x;
                                scanf("%d", &x );

                                //1.找到待插入的位置 --> 二分查找 
                                int l = 0;  //最左边的元素的下标 
                                int r = 8;  //最右边的元素的下标 
                                int mid;    //中间位置 

                                while( l <= r )
                                {
                                    mid = (l+r)  / 2 ;

                                    if( x < a[mid] )
                                    {
                                        r = mid - 1;
                                    }
                                    else if( x > a[mid] )
                                    {
                                        l = mid + 1;
                                    }
                                } //当while结束时,l就指向带插入的位置
                                

                                //2.插入操作 
                                int i;
                                for( i=9; i>l; i-- )    //将较大值往后移 
                                {
                                    a[i] = a[i-1];
                                }

                                a[l] = x;   //插入  

                                for( i=0; i<10; i++ )
                                {
                                    printf("%d ", a[i] );
                                }
                                putchar('\n');
                            }

            3)写一个函数, 求一个无符号的32位整数x的二进制形式有多少个1? 
                返回值: 返回1的个数 

                    x = 7;  ---> 00000000 00000000 00000000  0000 0111  --> 3 

                    思路: 
                        分别判断这个数的每一个bit位 

                    算法一: 
                        int bit_count( unsigned int x )
                        {
                            int num = 0;    //记录 1的个数 
                            int i;
                            for( i=0; i<32; i++ )
                            {
                                if( x & (1<<i) )    //判断这个数的每一个bit位 是否为1 
                                {
                                    num++;
                                }
                            }
                            return num;
                        }

                    算法二: 
                        int bit_count_v2( unsigned int x )
                        {
                            int num = 0;    //记录 1的个数 
                            int i;
                            while( x )
                            {
                                if( x & 1 )
                                {
                                    num++;
                                }
                                x = x >> 1;
                            }
                            return num;
                        }

                    算法三: 
                        int bit_count_v3( unsigned int x )
                        {
                            int num = 0;    //记录 1的个数 
                            int i;
                            while( x )
                            {
                                if( x % 2 )     //除2取余法
                                {
                                    num++;
                                }
                                x = x / 2;
                            }
                            return num;
                        }

                    算法四: 
                        x = x & (x-1); 

                            00000000 00000000 00000000 1111 1100    x 
                            00000000 00000000 00000000 1111 1011    x-1
                         & ---------------------------------------------
                            00000000 00000000 00000000 1111 1000    x 
                            00000000 00000000 00000000 1111 0111    x-1 
                         & ----------------------------------------------
                            ....

                        int bit_count_v4( unsigned int x )
                        {
                            int num = 0;    //记录 1的个数 
                            int i;
                            while( x )
                            {
                                x = x & (x-1);
                                num++;
                            }
                            return num;
                        }    

            4) 把一个二维数组的行和列 进行互换 

                a[3][4]     ==>  b[4][3]
                1,2,3,4                 1,5,9
                5,6,7,8                 2,6,10
                9,10,11,12              3,7,11
                                        4,8,12

                    a[0][0]     ==>     b[0][0]
                    a[0][1]     ==>     b[1][0]
                    a[0][2]     ==>     b[2][0]
                    ...
                        a[i][j]     ==>     b[j][i]

                ===> 
                    int main()
                    {
                        int a[3][4];
                        int i,j;
                        for( i=0; i<3; i++ )
                        {
                            for( j=0; j<4; j++ )
                            {
                                scanf("%d", &a[i][j] );
                            }
                        }

                        //进行行列互换
                        int b[4][3];
                        for( i=0; i<4; i++ )
                        {
                            for( j=0; j<3; j++ )
                            {
                                b[i][j] = a[j][i];  //行列互换
                            }
                        }

                        for( i=0; i<4; i++ )
                        {
                            for( j=0; j<3; j++ )
                            {
                                printf("%d ", b[i][j] );
                            }
                            putchar('\n');
                        }
                    }

                    
                5) 写一个函数, 输入一个正整数n, 求距离它最近的质数 
                        如果它本身就是质数, 就打印本身 
                        如果有两个,打印较小的那个 

                            例如: 
                                7   -->     7
                                15  -->  13, 17   --> 13 

                            n 判断n是否为质数 
                            n-1 判断n-1是否质数
                            n+1 判断n+1是否质数 
                            n-2 
                            n+2 
                            ...

                        /*
                            is_primary : 判断一个数是否为质数
                                返回值: 
                                    1 是质数 
                                    0 不是质数
                        */
                        int is_primary( int n )
                        {
                            if( n < 2 )
                            {
                                return 0;
                            }

                            int i;
                            for( i=2; i<n; i++ )
                            {
                                if( n%i == 0 )
                                {
                                    return 0;   //不是质数 
                                }
                            }
                            return 1;   //是质数
                        }

                        //求距离n最近的质数 
                        int get_closest_primary_num( int n )
                        {
                            int i = 0;
                            while( 1 )
                            {
                                if( is_primary( n-i ) )     //n, n-1, n-2, ... 
                                {
                                    return  n-i;
                                }
                                if( is_primary( n+i ) )     //n, n+1, n+2, .... 
                                {
                                    return  n+i;
                                }
                                i++;
                            }
                        }

                        int main()
                        {
                            int n;
                            scanf("%d", &n );

                            int x = get_closest_primary_num( n );
                            printf("距离最近的质数为 %d \n", x );
                        }

7.变量的作用域和生存期 

    1)作用域 
        一个对象(变量\数组\函数等) 起作用的范围 

        (1)全局变量 
            在函数外面定义的变量, 叫 全局变量 
                如果全局变量在定义的时候没有初始化, 那么编译器会自动初始化为0 

            全局变量的作用域:
                自定义起 往下到文件结束 (别的文件也可以使用, 但是要extern外部变量声明)

                但是, 如果一个全局变量被static修饰, 这个全局变量的作用域 就被限制 仅在本文件中使用 
                    被static修饰的变量, 只能初始化一次
                    如果没有被初始化, 那么编译器也会被初始化为0 


        (2)局部变量 
            在 函数体内 或者 复合语句内 定义的变量, 叫 局部变量 

            局部变量的作用域: 
                自定义起 到 函数结束 或者 复合语句结束 (即第一个右花括号的位置) 

        注意: 
            不用作用域的两个变量, 必然是两个独立的空间 
            哪怕名字重复了, 就近往上找 

            例子: 
                1)
                    //全局变量
                    int b = 5;
                    int d;

                    int main()
                    {
                        int a = 10;        //局部变量 

                        printf("b = %d\n", b );    // 5 全局变量

                        int b = 6;        //局部变量 
                        printf("b = %d\n", b );    // 6 局部变量,就近往上找

                        if(a == 10 )
                        {
                            int c;    //局部变量
                            printf("c = %d\n", c );        //未知的,不确定的
                        }
                        //printf("c = %d\n", c );    //超出作用域了

                        int c = 6;
                        printf("c = %d\n", c );

                        //int c = 6;    //error 重复定义,一个作用域下不能有两个同名

                        for( int i=0; i<5; i++ )    // i 局部变量
                        {
                            printf("d = %d\n", d );        //被自动初始化为0
                        }
                        //printf("i = %d\n", i );   //超出作用域了
                    }

                2) 
                    int a = 5;
                    int main()
                    {
                        printf("a = %d\n", a );     // 5
                        int a = 6;
                        if( 1 )
                        {
                            int a = 7;
                            printf("a = %d\n", a );     // 7
                        }
                        printf("a = %d\n", a );     // 6
                    }

                3) 
                    void exchange( int a, int b )
                    {
                        int temp = a;
                        a = b;
                        b = temp;
                    }

                    int main()
                    {
                        int a = 5, b = 6;

                        exchange( a, b );       //形不改实, 并没有交换实参的值, 实参只是把值传递给了的形参

                        printf("a = %d, b = %d\n", a, b );      // 5 6 
                    }


    2)形不改实  ☆☆☆
        当主调函数 调用 被调函数时, 且实际参数是 变量 
            则 被调函数中的形参的变化 不会影响到实参的变化 

            原因: 
                形参只是被调函数里面的局部变量, 与实际参数是两个独立的空间 
                函数调用时 只是把实参的值 传递(赋值)给了 形参而已, 二者互不影响 

    
    3)生存期 
        是指一个对象从生到死的期间(生成周期)
            一个变量过了它的生存期, 其内存空间就会被释放掉  

        全局变量的生存期: 
            随进程的持续性     (进程:正在进行程序)
                你的程序一直运行, 全局变量就会一直存在, 直到进程退出为止  

        局部变量的生存期: 
            (1)普通局部变量
                int a; 
                    普通的局部变量, 自定义起 到函数结束 或者 复合语句结束 (即第一个右花括号的位置)

                    例子: 
                        void func()
                        {
                            int a = 7;
                            a++;
                            printf("a = %d\n", a );
                        }

                        int main()
                        {
                            func();     // 8
                            func();     // 8
                        }

            (2)static 静态局部变量 
                static int a;   // 静态局部变量 

                static静态局部变量 的生存期: 
                    随进程的持续性, 且只初始化一次 

                    例子: 
                        void func()
                        {
                            static int a = 7;
                            a++;
                            printf("a = %d\n", a );
                        }

                        int main()
                        {
                            func();     // 8
                            func();     // 9
                        }

    ☆☆☆
        总结: 
            static在C语言中的作用: 
                (1)static用于 修饰 全局变量 或者 函数 的时候 
                    被修饰的全局变量或者函数的作用域, 变为 仅在本文件中使用 

                (2)static用于 修饰 局部变量 的时候 
                    使得被修饰的局部变量的生存期, 变为随进程的持续性, 且初始化只执行一次

8.递归函数 

    递归函数: 直接或者间接调用函数本身  "自己调用自己" 

        什么是才能使用递归? 
            解决一个问题时, 解决思路转换为与问题本身类似的问题

    ☆☆☆
        递归函数的设计思想: 
            (1)问题模型本身要符合递归模型(弄清楚递推关系)
            (2)先明确函数 要实现的功能(函数名)和参数之间的关系, 暂时不管功能的具体实现
            (3)问题的解, 当递归到一定程度时, 答案是显而易见的, 并且能够结束递归(不能无限递归)
            (4)要呈现出 第n层 和 第n-1层 的递推关系

            例子: 
                1)写一个函数, 实现求第n个人的年龄 
                    第一个人的年龄是10岁, 第二个人的年龄是12岁, 第三个人14岁.... 依次类推

                        a[0]    -- 10 
                        a[1]    -- 12   --> a[0] + 2
                        a[2]    -- 14   --> a[1] + 2
                        ... 
                        a[n-1]  --> a[n-2] + 2
                        a[n]    --> a[n-1] + 2

                    算法一: 普通方法 
                        int get_age( int )
                        {
                            int a[n];
                            a[0] = 10;
                            int i;
                            for( i=1; i<n; i++ )
                            {
                                a[i] = a[i-1] + 2;
                            }
                            return a[n-1];
                        } 

                    算法二:  递归方法 
                            (1)除了第一个人以外, 其余所有人的年龄 都是前一个人的年龄加2 

                            (2) 
                                    int get_age( int n )
                                    {}

                                    得到第n个人的年龄   get_age( n-1 ) + 2
                                    得到第n-1个人的年龄 get_age( n-2 ) + 2
                                    ...
                                    得到第3个人的年龄  get_age( 2 ) + 2
                                    得到第2个人的年龄   get_age( 1 ) + 2
                                    得到第1个人的年龄   10

                                    当 n == 1 时, 
                                        return 10;

                            (3) 当n==1时, 答案是显而易见的, 是10岁 

                            (4)  get_age(n)  ==>  get_age(n-1) + 2 

                        ==> 
                            int get_age( int n )
                            {
                                if( n == 1 )
                                {
                                    return 10;
                                }
                                else 
                                {
                                    return get_age(n-1) + 2;
                                }
                            }

                
                2) 设计一个递归函数, 实现求n的阶乘 n! 

                        (1)    0! = 1
                                1! = 1          
                                2! = 1*2        --> 1! * 2
                                3! = 1*2*3      --> 2! * 3
                                4! = 1*2*3*4    --> 3! * 4
                                ...
                                n! = (n-1)! * n 

                        (2)    int factorial( int n )
                                {}

                        (3) 当 n==0 或者 n==1 时, 答案是显而易见的, 为 1 

                        (4) factorial(n)  ==>  factorial(n-1) * n  

                    ===> 
                        int factorial( int n )
                        {
                            if( n==0 || n==1 )
                            {
                                return 1;
                            }
                            else 
                            {
                                return factorial(n-1) * n;
                            }
                        }

                
                3)汉诺塔 Hanoi 
                    把n个盘子, 从A柱 移动到 C柱, 中间可以使用B柱, 大盘子不能在小盘子的上面
                        请把移动的步骤 和 总步数 打印出来 

                        (1)    
                            当 n == 1 
                                    打印 : A --> C  ,  step = 1

                            当 n == 2
                                    打印 :  
                                        A --> B  
                                        A --> C 
                                        B --> C     step = 3

                            n == 3 
                            ....

                            n 
                                1.先把A上面的 n-1个盘子 从A柱 移动到 B柱 
                                2.然后把第n个盘子 移动到 C柱 
                                3.最后 把n-1个盘子 从B柱 移动到 C柱 

                        (2)
                            int step = 0;   //全局变量 保存总步数

                            //把n个盘子, 从A柱 移动到 C柱, 中间可以使用B柱 
                            void Hanoi( int n, char A, char B, char C )
                            {
                            }

                        (3) 当n==1 和 n==2时, 答案是显而易见的 

                        (4) 
                            Hanoi( n , A, B, C )
                                ==> 
                                    //1.先把A上面的 n-1个盘子 从A柱 移动到 B柱 
                                    Hanoi( n-1, A, C, B );

                                    //2.然后把第n个盘子 移动到 C柱 
                                    printf("%c --> %c\n", A, C );

                                    //3.最后 把n-1个盘子 从B柱 移动到 C柱 
                                    Hanoi( n-1, B, A, C );

                    ====> 
                         int step = 0;   //全局变量 保存总步数

                        //把n个盘子, 从A柱 移动到 C柱, 中间可以使用B柱 
                        void Hanoi( int n, char A, char B, char C )
                        {
                            if( n == 1 )
                            {
                                printf("%c --> %c\n", A, C );
                                step++;
                                return ;
                            }
                            else if( n == 2 )
                            {
                                printf("%c --> %c\n", A, B );
                                step++;
                                printf("%c --> %c\n", A, C );
                                step++;
                                printf("%c --> %c\n", B, C );
                                step++;

                                return ;
                            }
                            
                            //1.先把A上面的 n-1个盘子 从A柱 移动到 B柱 
                            Hanoi( n-1, A, C, B );

                            //2.然后把第n个盘子 移动到 C柱 
                            printf("%c --> %c\n", A, C );
                            step++;

                            //3.最后 把n-1个盘子 从B柱 移动到 C柱 
                            Hanoi( n-1, B, A, C );

                            return ;
                        }   

        递归函数的参数: 
            (1)每次递归处理的是不同的对象       --->  对象作为参数 
            (2)每次递归处理的同一个对象         ---> 可以用全局变量, 也可以传指针作为参数 
            (3)把子问题解决之后, 需要回传解     ---> 返回值 

9.快速排序 

    分治法: 
        分而治之, 将问题分解成规模更加小的子问题

    算法: 
        对于一个数组, 从中间位置选择一个元素, 以该元素为界 将其余元素划分成两个子集
            一个子集中所有的元素都是小于该元素的, 另一个子集中的所有元素都是大于该元素的 

        再对这样的两个子集 递归执行以上的这一过程 

        当某个子集的元素个数小于2个时, 这个子集就不需要再排序了,终止递归


    时间复杂度:  O( n * log以2为底n )


    实现: 
            //交换数组的两个元素的位置
            void swap( int a[], int i, int j )
            {
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }

            //快速排序 
            void quick_sort( int a[], int left, int right )
            {
                if( left >= right )
                {
                    return ;
                }

                //1.从中间位置选择一个元素, 以该元素为界 将其余元素划分成两个子集 
                swap( a, left, (left+right)/2 ); 
                //划分 
                int i;
                int last = left;    //代表左边子集的最后一个元素的下标 
                for( i=left+1; i<=right; i++ )
                {
                    if( a[left] > a[i] )
                    {
                        swap( a, i, ++last );
                    }
                }
                swap( a, left, last );  

                //2.左边的子集
                quick_sort( a, left, last-1 );

                //3.右边的子集 
                quick_sort( a, last+1, right );

            }


10.总结 

    (1)掌握函数的基本语法, 设计步骤, 函数调用, 数组作为函数的参数 

    (2)声明 

    (3)作用域和生存期

    (4)递归函数的设计思想(步骤)

    (5)快速排序的算法思想

    

练习:
        1)设计一个递归函数, 实现求一个一维数组的所有元素之和 

                (1)
                    s1 = a[0]
                    s2 = a[0] + a[1]            == s1 + a[1]
                    s3 = a[0] + a[1] + a[2]     == s2 + a[2]
                    ...
                    Sn = S(n-1) + a[n-1]

                (2)
                    int array_sum( int a[], int n )
                    {}

                (3) 当 n==1 时,  答案是显而易见的, 元素之和为 a[0]

                (4) array_sum( a, n )  ==>  array_sum( a, n-1 ) + a[n-1]

            ===> 
                int array_sum( int a[], int n )
                {
                    if( n == 1 )
                    {
                        return a[0];
                    }
                    else 
                    {
                        return array_sum(a, n-1) + a[n-1];
                    }
                }


        2)设计一个递归函数, 判断一个一维整型数组是否递增 
            返回值: 
                1   是 
                0   不是 

                (1)
                    a[0]          1个元素
                    a[0] < a[1]            2个元素         
                    a[0] < a[1] && a[1] < a[2]      3个元素
                    a[0] < a[1] && a[1] < a[2] && a[2] < a[3]   4个元素
                    ...
                     a[0] < a[1] && a[1] < a[2] && a[2] < a[3] && .... && a[n-2] < a[n-1]           n个元素  

                (2) 
                        int is_up( int a[], int n )
                        {}

                (3) 当 n==1 时, 答案是显而易见的,  return 1;    //是

                (4) 
                    n个元素 要满足的关系是: 
                        前面n-1个元素都满足递增关系 && a[n-2] < a[n-1] 

            ===> 
                    int is_up( int a[], int n )
                    {
                        if( n == 1 )
                        {
                            return 1;
                        }
                        else 
                        {
                            return  is_up( a, n-1 ) && a[n-2] < a[n-1];
                        }
                    }


原文地址:https://blog.csdn.net/m0_69032433/article/details/142585198

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!