C语言:数组和冒泡排序

C语言

主要内容

  • 数组
  • 一维数组
  • 数组的典型应用:冒泡排序

数组

数组的概念

什么是数组

数组是相同类型,有序数据的集合。

数组的特征
  • 数组中的数据被称之为数组的元素(数组中的每一个匿名的变量空间),是同构的。
  • 数组中的元素存放在内存空间键(char player_name[6]:申请在内存中开辟6块连续的基于char类型的变量空间)
衍生概念:下标(索引)
  • 下标或者索引代表了数组中元素距离第一个元素的(首地址所在元素)偏移量。举例:第一个元素距离第一个元素的偏移量为0,所以数组的下标是从0开始。
  • 数组中元素的地址值,下标越大,地址值越大。(每一块内存空间都有一个独有的内存地址,内存中每8个bit也就是1个字节编一个号,这个号就是我们所说内存地址)

  • 数组的下标是从0开始。

一维数组

数组的定义
c 复制代码
类型说明符或数据类型 数组名[数组容量]
  • 语法:

    • 数组的类型说明符由数组中的元素来决定,类型说明符也就是数据类型,元素是什么类型,数组就是什么类型。同一个数组中,所有元素的类型都是一致的。

    • 数组名也是标识符,我们所说的数组(名),大家可以理解为数据类型是数组的变量( 名)。命名规则与变量名相同,遵循标识符命名规则(标识符命名规则:不能以数字开头,只能包含数字、字母、下划线)。

    • 数组容量也可以叫常量表达式或者元素个数,其值必须为整型,可以包含常量和符号常量,但不能是变量。

      c 复制代码
      int size = 10;
      
      int arr[size];// 这里永远是10,因此此时数组的内存已将申请,此时数组的大小就是10,并不会因为后面对size重新赋值而改变
      
      size = 22;
      
      printf("%d",size);// 22
      • 举例:
      c 复制代码
      #define SIZE 4;// 符号常量
      
      写法1,符号常量:int arr[SIZE];
      写法2,常量:int size = 4; int arr[size]; // 重新给size赋值,并不会影响到数组的大小
      写法3,常量:int arr[4]; int lcd[800*480];
    • 类型:

      • 代表了数组中元素的类型
    • 容量:

      • 数组中能存储多少个元素,数组容量可以是一个常量、常量表达式、还可以是符号常量,但必须是整型。
    • 深入理解:

      • ①定义一个数组,相当于申请了一个可以容纳所指定元素数量的内存单元。所申请的内存单元是连续。
      • ②定义一个数组,相当于定义了多个匿名的变量,这些变量可以通过数组名[下标]来访问。
    • 范例:

      c 复制代码
      // 定义一个数组
      int arr[10];// 此时只是在内存中申请了10个元素所对应的空间,此时里面的值是随机值,大概率是0
      // 上面数组中,最小下标是0,最大下标是9
    • 经过上面的案例,分析得到:

      c 复制代码
      数组的最大下标 = 数组元素个数(数组容量)- 1;
数组元素的访问
  • 原则:数组中的元素不能一次性访问所有,只能一个一个的访问。

  • 访问方式:

    c 复制代码
    数组名[下标];
  • 举例:

    c 复制代码
    // 定义一个容纳10个元素的int数组
    int arr[10];
    // 给数组的第一个元素进行赋值(存数据)
    arr[0] = 89;
    // 访问数组中的第一个元素(取数据)
    int a = arr[0];
    
    int c = arr[9]; // 0
    int b = arr[10];// error,报下标越界异常,所以使用数组的过程中,一定要进行下标越界校验,否则报错
    • 注意:数组元素的访问一定不能越界
  • 案例:

    • 需求:利用循环给数组元素a[0]~a[9]赋值 0~9 ,并且逆序输出。

    • 代码:

      c 复制代码
      #include <stdio.h>
      
      int main(int argc,char *argv[])
      {
          // 创建一个数组,用来存放0~9的数字
          int a[10];
          
          // 使用for循环给数组元素赋值(一般数组配套的都是for循环)
          // C语言中,没有提供数组的大小,需要我们自己计算,数组大小 = 数组总字节数 / 1个元素的字节数
          int len = sizeof(a) / sizeof(a[0]); // 等价与 int len = sizeof(a) / sizeof(int)
          for(int i = 0;i < len; i++)
          {
          	a[i] = i;
          }
          
          // 逆序输出
          // 遍历:通过循环将数组中的元素一个一个取出来
          for(int j = len -1; j>= 0; j--)
          {
          	printf("%4d",a[j]);
          }
          
          printf("\n");// 纯粹换行
          
          return 0;
      }
数组初始化
  • 定义数组的同时,用指定数据来给对应的元素赋值。

  • 简化数组定义后,需要对元素一一赋值操作。

  • 语法规则:

    c 复制代码
    数据类型 数组名[数组容量] = {常量1,常量2,常量3...};
  • 注意事项:

    • 数组可以部分初始化,也就是可以给数组中前几个元素初始化,未被初始化的元素系统将自动初始化,大概率是0;如果定义数组时未指定数据容量,则系统会根据初始化元素的个数来决定数组容量。

      c 复制代码
      // 1. 如果定义数组时只给数组前几个初始化,后续剩余元素会自动完成初始化,大概率赋0
      int arr[10] = {11,12,13,14,15}; // 推荐写法,等价于下面写法
      int arr[10] = {11,12,13,14,15,0,0,0,0,0};
      
      // 2. 如果定义数组时,未指定数据容量,系统根据初始化元素个数来决定容量
      int arr[] = {11,12,13,14,15}; // 推荐写法,等价于下面写法
      int arr[5] = {11,12,13,14,15};
    • 柔性数组

      • 专业理解:柔性数组的概念是在C99标准,针对结构体的最后一个成员可以是一个未指定大小的数组;
      • 广义理解:数组容量待定或者待确定的数组,举例:int arr[] = {1,2,3,4,5}
扩展
  • 在不知道数组类型的情况下,如何确定数组元素的个数

    c 复制代码
    int length = sizeof(arr) / sizeof(arr[0])
    • 说明:

      ①arr就是我们计算的数组本身,sizeof(arr)用来计算该数组中总的字节大小。

      sizeof(arr[0])用来计算数组中一个元素所占的字节大小,因为数组中的元素类型相同,所以计算哪一个都行。

      sizeof(arr)/sizeof(arr[0])就是用数组中总的字节数除以每一个元素所占的字节数,从而得到元素的个数。

一维数组的案例

案例1:

  • 需求:斐波拉契数列

  • 分析:1+1+2+3+5+...就是斐波拉契数列

  • 代码:

    c 复制代码
    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        int i;// 循环变量
        // 定义一个数组,用来存储数列,默认存储的第1和第2的值是1
        int f[20] = {1,1};
        
        // 计算数组的大小
        int len = sizeof(f) / sizeof(f[0]);
        
        // 使用for循环,将生成的数据存入数组
        for(i = 2;i < len; i++)// i=3
        {
        	f[i] = f[i-2]+f[i-1];// 给数组元素赋值,从数组的第3个元素开始 1,1,2,3,5..
        }
        
        // 遍历数组
        for(i = 0; i < len; i++)
        {
        	// 遍历的时候,要求每5个换一行,也就是1行显示5个
       		if(i % 5 == 0)
            {
            	printf("\n");
            }
            
            printf("%8d",f[i]);
        }
        
        printf("\n");
        
        return 0;
    }

案例2:

  • 需求:从键盘输入年、月、日,计算并输出该日是该年的第几天

  • 分析:

    • 首先创建一个数组,用来存放每一个月的天数,因为二月比较特殊,初始化的时候设置为0,int t[] = {31,0,31...}
    • 从控制台输入年、月、日
    • 闰年判断,修改数组中二月对应的天数(闰年:29,平年:28)
    • 定义一个变量,用来记录天数,默认值是我们输入的天数:int sum = 14
    • 将当前月之前所有的天数,从数组中取出来进行相加 sum += t[0]+t[1]...
    • 最后打印输出sum
  • 代码:

    c 复制代码
    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 定义一个数组,用来存放1~12月的天数,二月比较特殊,暂时不初始化
        int t[] = {31,0,31,30,31,30,31,31,30,31,30,31};
        // 定义三个变量,用来接收控制台输入的年,月,日
        int year,month,day;
        
        printf("请输入年份、月份、天:\n");
        scanf("%d-%d-%d",&year,&month,&day);
        
        // 润年的判断,给数组中的二月份赋值天数
        if((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
        	t[1] = 29;// 润年
        else
        	t[1] = 28;// 平年
        
        // 创建一个变量,用来记录当前是第几天,默认位当前输入的天数
        int sum = day;
        
        // 将对应月份之前的所有月份天数加起来
        for(int k = 0; k < month - 1; k++)
        {
        	sum += t[k]; // 叠加前几个月的天数
        }
        
        printf("%d月%d日是%d年第%d天。\n",month,day,year,sum);
        return 0;
    }

数组的典型应用:冒泡排序

向后冒泡

  • 思想:

    • ①一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好
    • ②每次排序将相邻数据两两比较,将较大或者较小的数据向后交换,等所有数据比较完成,较大或者较小的数就会出现在最后,这也是该数应该有的位置。
    • ③在余下的数中,再次应用第②步的操作,直到只剩下1个数。
  • 分析:

    原始数列:1 2 3 4 5 → 5 4 3 2 1

    排序轮数: 5 1 2 3 4、 5 4 1 2 3、 5 4 3 1 2、 5 4 3 2 1 得到:轮数 = 元素个数 - 1 = 4

    比较次数:

    ① 1 2 3 4 5 → 5 1 2 3 4(第1轮比4次)5 -1 -0= 4

    ② 1 2 3 4 5 → 5 4 1 2 3(第2轮比3次)5 - 1 -1 = 3

    ③ 1 2 3 4 5 → 5 4 3 1 2 (第3轮比2次)5 -1 -2 = 2

    ④ 1 2 3 4 5 → 5 4 3 2 1 (第4轮比1次)5 - 1 -3 = 1

    得到每一轮的比较次数:每一轮比较次数 = 元素个数 - 1 - 轮数,轮数从0开始

  • 代码:

    c 复制代码
    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 创建一个数组,用来存储排序的序列
        int arr[10];
        // 定义三个变量 i:比较的轮数(0~len-1)j:每一轮比较的次数(0~len-1-i)temp:临时变量,用来实现两个变量值的交换
        int i,j,temp;
        
        printf("请输入10个整数:\n");
        
        // 计算数组的大小
        int len = sizeof(arr) / sizeof(arr[0]);
        
        // 通过循环录入数据
        for(i = 0; i < len; i++)
        {
        	scanf("%d",&arr[i]);
        }
        
        printf("\n");
        
        // 冒泡排序
        // 第一次循环:控制比较的轮数:轮数 = len -1;
        for(i = 0; i < len - 1; i++)
        {
            // 第二层循环:控制每一轮的比较次数:次数 = len - 1 - i
            for(j = 0; j < len - 1 - i; j++)
            {
                // 相邻两个数进行比较,满足条件交换位置
                if(arr[j] > arr[j+1]) // 1 2
                {
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        printf("冒泡排序后的数列:\n");
        for(i = 0; i < len; i++)
        {
            printf("%4d",arr[i]);
        }
        
        printf("\n");
        return 0;
    }

向前冒泡

  • 思想:
    • ①一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好
    • ②每次排序假定第一个元素是最大或者最小的,用第一个元素的后面的元素一一与第一个元素比较, 遇到较大或者较小的和第一个元素交换,访问完数组的最后一个元素,就排好了一个数。
    • ③在余下的数中,再次应用第②步的操作,直到只剩下1个数。
相关推荐
烂蜻蜓9 分钟前
前端已死?什么是前端
开发语言·前端·javascript·vue.js·uni-app
老猿讲编程11 分钟前
安全C语言编码规范概述
c语言·开发语言·安全
OrangeJiuce14 分钟前
【QT中的一些高级数据结构,持续更新中...】
数据结构·c++·qt
萌の鱼3 小时前
leetcode 2826. 将三个组排序
数据结构·c++·算法·leetcode
Buling_03 小时前
算法-哈希表篇08-四数之和
数据结构·算法·散列表
Biomamba生信基地4 小时前
两天入门R语言,周末开讲
开发语言·r语言·生信
RAN_PAND4 小时前
STL介绍1:vector、pair、string、queue、map
开发语言·c++·算法
Bio Coder4 小时前
R语言安装生物信息数据库包
开发语言·数据库·r语言
Tiger Z4 小时前
R 语言科研绘图第 27 期 --- 密度图-分组
开发语言·程序人生·r语言·贴图