筑基二层 —— 图解函数递归、数组详解

目录

一.修炼必备

二.图解递归的执行过程

三.数组

3.1 一维数组

3.2 二维数组

3.3 数组的共同问题


一.修炼必备

  1.入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)

  2.趁手武器:印象笔记/有道云笔记

  3.修炼秘籍:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)

  4.雷劫必备:leetcode 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

  注:遇到瓶颈怎么办?百度百科_全球领先的中文百科全书 (baidu.com)

 

二.图解递归的执行过程

  1.什么是递归?

   ——递归是指将复杂的问题转换成简单的问题进行解决,即是”大事化小,小事简单明了“,在编程语言中,常使用在函数进行递归调用(函数自身调用自身

  2.递归的条件

   1)递归有限制条件,一旦递归到这个限制条件就停止递归

   2)每一次递归都会接近这个限制条件

   3)进行设计递归函数的时候注意要设置好限制条件,不然会”死归了“

  3.一个小case快速了解递归

   1)递归代码

#include <stdio.h>

void printNum(unsigned int num)
{
    if (num > 9)
    {
        printNum(num / 10);//获取下一位
    }
    printf("%u ", num % 10);//得到个位的数
}

int main()
{
    unsigned int num = 0;
    scanf("%u", &num);

    printNum(num);
    return 0;
}

   2)图解:

6a1a53d969f54d3cb876bd81f501e605.png

  4.图解递归过程

   1)求阶乘

#include <stdio.h>

int fac(int n)
{
    //n <= 1是限制条件
    if (n <= 1)
        return 1;
    else
        return n * fac(n - 1);//每次递归逐渐接近限制条件
}

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

    int res = fac(n);
    printf("%d\n", res);
    return 0;
}

   图解过程:

66d944a113da4bce9c506e2b292b7ca1.png

   2)第n个斐波那契数

#include <stdio.h>

int Fib(int n)
{
    //n <= 2:限制条件
    if (n <= 2)
        return 1;
    else
        return Fib(n - 1) + Fib(n - 2);
}

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

    int fib = Fib(n);
    printf("%d\n", fib);
    return 0;
}

   图解过程:

59767d728cda46acafb891fa8a78a785.png

 

三.数组

3.1 一维数组

  1.什么是数组?

   —— 数组是一组相同类型元素的集合

  2.数组创建和初始化

   1)数组的创建:数据类型 + 数组名 +[]

int arr[10];//整型数组,数据类型:int 数组名:arr

double num[10];//浮点型数组,数据类型:double 数组名:num

char ch[10];//字符数组,数据类型:char 数组名:ch

注:[]中只能是常量表达式(c99之前,c99后可以使用【变长数组:允许使用变量给数组的大小进行指定,但是数组不能进行初始化】

   2)数组的初始化:数据类型 数组名[数组的大小] = {初始化值1, 初始化值2, ……};

i.完全初始化:数组完全初始化可不指定数组大小,数组大小是初始化值的个数

int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

ii.不完全初始化:数组只初始化部分的数据,剩下部分的值按照数据类型判断默认值:int : 0 double : 0.0 ch : '\0'(本质还是0)

int arr[10] = { 1,2,3 };//0

double num[10] = { 1.0,2.0 };//0.0

char ch[10] = { 'a','b','c' };//'\0'

注:如果数组不想指定大小,那么进行完全初始化,若是不想指定大小又不进行初始化,则会导致程序崩溃

  3.请思考以下问题

//请思考以下代码的不同
char ch[] = "abc";
char ch1[3] = { 'a','b','c' };

  4.数组的使用

   —— 数组是通过下标来进行使用的, 数组的下标可以准确的指定其值(需要使用[]操作符)

#include <stdio.h>

//数组的使用 - 逆序输出数组中的值
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int len = sizeof(arr) / sizeof(arr[0]);//计算数组的大小
    int i = 0;

    for (i = len-1; i >= 0; i--)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

  5.数组的细节讨论

   1)数组的下标是从0开始的

#include <stdio.h>

int main()
{
    //数组的下标是从0开始的
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    printf("%d\n", arr[0]);//arr[0]是数组的第一个元素
    return 0;
}

   2)数组在内存中是连续存储的

#include <stdio.h>

//数组在内存中是连续存储的
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int len = sizeof(arr) / sizeof(arr[0]);
    int i = 0;

    for (i = 0; i < len; i++)
    {
        printf("&arr[%d] = %p\n", i, &arr[i]);//&arr[i]取出arr[i]的地址
    }
    return 0;
}

   图解:

8583ced024fd481196b3a3d1576649f5.png

3.2 二维数组

  1.什么是二维数组

   —— 二维数组是以一个行和列组成的矩阵,二维数组可以看成两个一位数组的组合

  2.二维数组的创建和初始化

   1)二维数组的创建:数据类型 数组名[第一维大小][第二维大小];

#include <stdio.h>

int main()
{
    //int arr[3][3];//创建二维数组
    int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };//创建二维数组并初始化
    return 0;
}

   2)二维数组的初始化

完全初始化:把数组中的所有值都进行初始化

不完全初始化:只进行初始化数组中部分的值,剩下的值是默认值

注:二维数组在已经初始化的情况下,只可以省略第二维,不能省略第一维

#include <stdio.h>

int main()
{
    //完全初始化的两种形式
    int arr1[3][3] = { 1,2,3,4,5,6,7,8,9 };//形式1:不加{}
    int arr2[3][3] = { {1,2,3},{4,5,6},{7,8,9} };//形式2:加{}
    //解释:{}代表第一维的长度,arr2中第一维有3个,初始化中有3个{}

    //不完全初始化的两种形式
    //说明,前面的1,2,3占据了第一维空间,4占据了第二维的第一个内存空间,剩下的是默认值
    int arr3[3][3] = { 1,2,3,4 };//形式1
    int arr4[3][3] = { {1,2,3}, {4} };//形式2

    //不初始化:二维数组中全部是默认值
    int arr5[3][3];//全部是默认值:0
    
    //省略第二维
    int arr6[][3] = { {1,2,3},{4,5,6},{7,8,9} };       
    return 0;
}

  3.二维数组的使用

   ——二位数组也是通过下标进行访问数组中的元素

#include <stdio.h>

int main()
{
    int arr[3][3];//创建数组
    int i, j;
    for (i = 0; i < 3; i++)
        for (j = 0; j < 3; j++)
            scanf("%d", &arr[i][j]);

    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

  4.思考以下的问题

   1)如何求二维数组的长度

   2)如何求二维数组中第一维的长度

   3)如何求二维数组中第二维的长度

#include <stdio.h>

int main()
{
    //二维数组的长度
    int arr[3][4] = { 1,2,3,4,5,6,7,8,9 };
    int len1 = sizeof(arr) / sizeof(arr[0][0]);//二维数组的长度
    printf("%d\n", len1);//12

    //二维数组中第二维的长度
    int len2 = sizeof(arr[0]) / sizeof(arr[0][0]);
    printf("%d\n", len2);//4

    //二维数组中第一维的长度
    //int len3 = len1 / len2;//法一
    int len3 = sizeof(arr) / sizeof(arr[0]);//法二
    printf("%d\n", len3);//3
    return 0;
}

  5.二维数组在内存中的存储

   ——跟一维数组一样,二维数组在内存中是连续存储的

#include <stdio.h>

//二维数组在内存中的存储
int main()
{
    int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
    int i, j;
    int len1 = sizeof(arr) / sizeof(arr[0]);//第一维的大小
    int len2 = sizeof(arr[0]) / sizeof(arr[0][0]);//第二维的大小

    for (i = 0; i < len1; i++)
    {
        for (j = 0; j < len2; j++)
        {    
            //&:取出对应值的地址
            printf("arr[%d][%d] = %p\n", i, j, &arr[i][j]);
        }
    }
    return 0;
}

   图解:

c69c6b5919df42a6ada6878c1cab7fa6.png

3.3 数组的共同问题

  1.数组越界

   ——假设数组有n个元素,其下标的范围是0~n-1,数组的下标是从0开始的,最后一个下标是n-1

#include <stdio.h>

//数组越界
int main()
{
    int arr[5] = { 1,2,3,4,5 };
    int i = 0;

    //错误,数组的下标范围是0-4,而不是0-5
    for (i = 0; i <= 5; i++)
    {}
    return 0;
}

  2.数组作为数组参数

   1)若是以数组名作为参数,那么则为传址调用

   2)若是以数组元素作为参数,看数组元素前面是否有&符合,若是有,则为传址调用,若是没有,则为传值调用

#include <stdio.h>

void PrintNum(int x)
{
    x = 11;
    printf("%d\n", x);
}

void Print(int* arr, int len)
{
    int i = 0;
    for (i = 0; i < len; i++)
    {
        printf("%d ", *(arr + i));
    }
    printf("\n");
}

void ChangeNum(int* x)
{
    *x = 11;
}



int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int len = sizeof(arr) / sizeof(arr[0]);

    PrintNum(arr[0]);//传值:数组元素作为实参
    Print(arr, len);//传址:数组名作为实参
    ChangeNum(&arr[0]);//传址:数组元素的地址作为实参
    Print(arr, len);//传址:数组名作为实参
    return 0;
}

  3.冒泡排序

   1)思想:将数列中的相邻两个值进行比较,如果顺序有误则交换这两个值的位置,然后再用这个值进行比较,一直比较到最后一个值,得到第一个数中最大/最小的值,在进行下一趟的比较

   2)流程详解

9246fcbdea9d4564b3d37c6dc4cb24d0.gif

   3)代码详解

#include <stdio.h>

//冒泡排序
void bubble_sort(int arr[], int len)
{
    int i, j;
    //趟数
    for (i = 0; i < len - 1; i++)
    {
        //一趟比较的次数
        for (j = 0; j < len - i - 1; j++)
        {
            //比较
            if (arr[j] > arr[j + 1])
            {
                int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
            }
        }
    }
}

int main()
{
    int arr[10] = { 10,5,1,7,3,9,6,4,2,8 };
    int len = sizeof(arr) / sizeof(arr[0]);//数组长度

    bubble_sort(arr, len);

    int i = 0;
    for (i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

   4)执行结果:

28cf304ec55b4b759b1a34945545fac4.png

  4.数组名详解

   1)数组名是数组首元素的地址(两个例外)

   2)sizeof(数组名)计算的是整个数组的大小,sizeof内部放数组名,数组名表示整个数组

   3)&数组名:取出的是整个数组的地址

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5 };

    //sizeof(数组名)计算的是整个数组的大小
    printf("%d\n", sizeof(arr));//40个字节的大小
    //数组名是数组首元素的地址(两个例外)
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);//数组首元素的地址
    //& 数组名:取出的是整个数组的地址
    printf("%p\n", &arr);
    printf("%p\n", &arr + 1);//跳过了40个字节
    return 0;
}

   图解: b73d0d378157442e841c503ff312194f.png

  !!!恭喜你,突破至筑基二层!!!

  注:由于CSDN中的排本不够美观,若是想要看更美观的排版,请参加有道云笔记:有道云笔记

猜你喜欢

转载自blog.csdn.net/qq_63200557/article/details/128748891