| 这个作业属于哪个班级 | C语言--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | C博客作业04--数组 |
| 这个作业的目标 | 学习如何设计函数、C语言基本数据类型 |

目录

0. 展示PTA总分

1. 本章学习总结

2. PTA实验作业



0.展示PTA总分



1. 本章学习总结

1.1 学习内容总结

1)查找数据

  • 顺序查找
    • 无序有序排列的数组均可以使用
    • 即按照顺序逐个查找目标数字/字符
    • 伪代码:(计数指定数据的个数)
/*计数目标数据的数量时,利用count++自增运算*/
// 跳出循环后,可以通过count的值,判断数组里是否有目标数据
//
/*需要利用目标数据的位置时(如增、删、改),将count++改为break,利用break跳出循环;或用return返回目标数据的下标*/
//可以使用flag,当找到目标数据时,flag = 1,反之 flag = 0。若未找到则及时结束函数,避免数组越界
//
            For i=0 to n-1
                  IF a[i] = target
                  then
                        count++   
                  END IF
            END For
  • 二分查找
    • 适用于有序排列的数据
    • 即判断区间中间的数据是否为目标数据。若不为目标数据,判断该数据与目标数据的关系后,进入减半的区间,继续判断中间数据
    • 代码(查找目标数据,并返回下标)(以从小到大顺序排列的数组为例)

int BinSearch(int a[99], int n, int key, int count)      
{
    int locMin = 0;           //最小下标
    int locMax = n - 1;       //最大下标
    int mid;                  //中间下标
    while (locMin <= locMax)            //当最小下标不大于最大下标时,继续遍历数组
    {
        mid = (locMax + locMin) / 2;    //取查找区间内的中间下标

        if (a[mid] == key)              //找到目标数据时,返回目标数据的下标    
        {
            return mid; 
        }
      /*根据目标数据和中间数据的关系,重新确定查找的区间*/
        else
        {
            if (key < a[mid])            
            {
                locMax = mid - 1;      
            }

            else if (key > a[mid])
            {
                locMin = mid + 1;
            }
        }
    }

    return -1;      //循环结束后,若未找到目标数据,则返回负数。通过该函数的返回值,确定是否有目标数据存在于数组之中
}

2)插入数据

  • 伪代码(以升序排列为例)
      //①先查找到需要插入的位置
      For i = 0 to n-1
            IF putNum < a[i]
               break跳出循环
            END IF
      END For
      //②再移动数组
      For j = n-1 to i
            a[j+1] = a[j]
      END For
      //③将需要插入的数据插入,并将表示数组数据个数的n加一
      a[i] = putNum
      n++
  • 流程图:

3)删除数据

  • 原数组删除:删除数据较少时使用

    • (步骤1:删除一个目标数据)
    • 利用查找数据,找到需要删除数据的位置
    • 确定删除的数据位置后,将数组左移一位,覆盖掉要删除的数据
    • 最后将数组元素个数,n--
    • (步骤2:)
    • 循环步骤1,直到在数组中找不到目标数据。(可以使用flag,判断数组中是否找到目标数据)
    • 简易流程图:
  • 新构造数组:删除数据较多时使用

    • 新构造一个数组
    • 定义两个数组下标,一个为原数组的下标i,一个为重构数组的下标j
    • 遍历原数组进行判断,若数据不为要删除的目标数据,将数据写入新构造的数组中,并且此时新构造数组的下标自增
    • 简易流程图:

4)排序方法

  • 冒泡排序
    • (步骤1:将两个相邻的数比较,先将一个最值“冒泡”,移到右边)
    • (步骤2:范围减小后,循环重复步骤1,将每个区间内的最值“冒泡”出去)
    • 代码:(以升序排列为例)
      int i,j;      //外循环控制次数,遍历的范围;内循环遍历数组
      int temp;

      for(i = 0; i < n-1; i++)                 //每执行一次冒泡,内循环的范围减小
      {
            for(j = 0; j < n-1-i; j++)         //通过逐个判断,将区间内的最大值移动到区间的最右边
            {
                  if(num[j] > num[j+1])      //逐个判断,若a[j+1] < a[j], 交换a[j] 和a[j+1]
                  {
                        temp = num[j];
                        num[j] = num[j+1];
                        num[j+1] = temp;
                  }
            }
       }
  • 选择排序
    • (步骤1:遍历数组,选择出最值,将最值放到指定位置)
    • (步骤2:范围减小,循环重复步骤1)
    • 代码:(以升序排列为例)
      int i,j;
      int minloc;           //最小值的下标       
      int temp;

      for(i = 0; i < n-1; i++)
      {
            minloc = i;      //使最小值的下标等于内循环开始时,数组的下标
            for(j = i; j < n; j++)      //从num[i]开始遍历数组,找到区间内的最小下标   
            {
                  if(num[minloc] > num[j])
                  {
                        minloc = j;
                  }
            }
            
            temp = num[i];            //交换,将范围内最小值与a[i]交换
            num[i] = num[minloc];
            num[minloc] = temp;
      }

5)数组统计

  • 思路:
    • 循环步骤1
    • 步骤1:输入数据,将该数据作为静态数组的下标,该下标对应的数据自增
  • 代码:
      static int vote[1000];    //定义静态数组,使数组各项为0

      for(i=0; i<n; i++)
      {
          scanf("%d",&num);     //输入需要统计的数据
          vote[num]++;          //将该数据作为静态数组下标。每次出现时,vote[]对应的值加1,统计出数据出现的次数  
      }

  • 案例一:7-4 点赞
  • 案例二:7-5 调查电视节目受欢迎程度

6)哈希数组

  • 用法
    • 定义静态数组int hash[256],
    • 与投票类似,将输入的数据作为hash[]数组的下标
    • 出现的数据对应下标的数组数据 = 1
    • 遍历数组,输出值等于1,对应的下标
    • 这样就完成了删除重复的数据,并将数据按照ASCII码排序
  • 案例
    • 删除重复数据,并按照ASCII码升序排列(伪代码)
      定义 static int hash[256] //因为ASCII码的范围为[0,255],共256个元素
      输入要输入的数据数 n
      For i = 0 tp n-1
      {
            输入数据 str
            hash[str] = 1;      //将str对应下标的数组赋值为1。用于接下来判断数据是否存在
      }
      END For
      
      For i = 0 to 255          //遍历hash数组,输出出现的数据
      {
            IF hash[i] = 1
            than
                  输出i         //也可以在定义一个新的数组,将i的值放入新的数组中
            END IF
      }
      END For      

7)二维数组

  • 二维数组特点:
    • 定义: 类型名 数组名[行长度][列长度]
    • 存放:二维数组在内存中是按行连续存放的
    • 赋初值:
      分行赋初值: int num[2][2] = {{1}, {2,1}};
      顺序赋初值: int num[2][2] = {1, 0, 2, 1};
    • 省略行长度:
      对所有元素都赋了初值
      分行赋值中,列出了所有行 int num[ ][3] = {{1, 2}, { }};
    • 根据题意,理清行(i)与列(j)的关系,有利于解决关于二维数组的图形问题

8)字符数组

  • 字符串特点

    • 字符串是特殊的一维数组,需要用一维数组来存放

    • 字符串的末尾有结束标志'\0',而'\0'之后的其它数组元素与字符串无关

    • 与数字数组相比,字符数组并不需要明确的有效元素个数作为循环的限制条件。

    • 字符数组的限制条件一般为结束标志'\0'。当用fgets输入字符串时,字符串末尾往往会接一个'\n',再接上结束标志,这种情况结束条件要适当改变
      条件:while(str[i]) 或者 while(str[i] && str[i] != '\n')

  • 需注意事项

    • 区分"a"和'a'
      初始化:char str[6] = "a"; //其中"a"为字符串
      赋值: str[0] = 'a'; //其中'a'为字符

    备注:初始化——> str[0] = 'a'; str[1] = '\0'

    • 字符串的输入:
      注意空格:scanf(输入的字符串(%s)不能含有空格,scanf遇到空格时停止输入)
      注意回车:fgets(输入的字符串遇到回车时结束,但如果输入的元素加上结束标志没有填满数组,则数组结束标志前还会有一个换行符('\n')
      注意结束标志:循环getchar()输入(逐个输入字符构成字符数组,但在输入完成后,需记住在末尾添加结束标志


2. PTA实验作业

2.1 数组左移

  • 2.1.1 伪代码
        输入数组元素个数n,左移位数moveNum
        moveNum = moveNum % n        //解决位移位数不小于元素个数的情况。若左移位数与元素个数相等,则不进行位移。若位移位数大于元素个数,则将位移位数为原来位移位数与元素个数的余数
        定义一个新数组tempNum[moveNum]
        For i = 0 to moveNum         //原数组的前几位放入新数组中               
        {
             tempNum[i] = moveNum[i];
        }
        END For

      For  i = moveNum to n - 1
      {
		number[i - moveNum] = number[i];      //数组内后几位数前移
	}
        END For

       For  (j = 0, i = n - moveNum; i < n; i++, j++)
	{
		number[i] = temp[j];
	}
        END For
  • 2.1.2 代码截图


  • 2.1.3 同学的代码
#include <stdio.h>
#define MAXN 100

int ArrayShift( int a[], int n, int m );

int main()
{
    int a[MAXN], n, m;
    int i;

    scanf("%d %d", &n, &m);
    for ( i = 0; i < n; i++ ) scanf("%d", &a[i]);

    ArrayShift(a, n, m);

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

    return 0;
}

int ArrayShift(int a[], int n, int m)
{
	for (int i = 0; i < m; i++)
	{
		int temp = a[n - 1];
		a[n - 1] = a[0];
		for (int A = 0; A < n - 2; A++)
		{
			a[A] = a[A + 1];
		}
		a[n - 2] = temp;
	}
    return 1;
}
  • 优点:
    同学:

    • 代码思路清晰,可读性强,易于理解
    • 代码简洁明了
  • 不足:
    同学:

    • 注释较少
      自己:
    • 代码冗长
    • 定义的全局变量过多
    • 函数形参的设置有所不足,应该使用形参将需要的参数传入函数,而不是过多地定义全局变量
    • 函数的返回类型。该代码中函数的返回类型多为void,所做的仅仅是将代码切分开来,没有很好地利用函数的返回值

2.2 阅览室

  • 2.2.1 伪代码
      /*数据表达*/
/*需要输入的变量*/
      定义 dayNum bookNum ch hh mm

      定义静态数组 lend[1001][2] 
      定义静态数组 back[1001][2] 
      // lend[][0] 放置是否借书
      // lend[][1] 放置借书的时间

/*与时间计算相关的变量*/
      定义 sumtime //存放总时间
      定义 times   //存放次数
      定义 avgTime //存放平均时间     

      定义bookMax  //最大书号

      /*数据处理*/
      For i = 0 to dayNum - 1
      {
            
                  /*重置一些变量的值*/
                  清空数组lend[][]
                  清空数组book[][]
                  bookMax = 0;
                  times = 0;
                  sumTime = 0;
            
                  while(1)
                  {
                      输入书号 bookNum
                        输入借出或归还 ch
                        输入时间 hh:mm
                        
                        IF bookMax < bookNum
                        then 
                              bookMax = bookNum
                        END IF
                  
                        IF bookNum = 0
                        then 
                              break
                        END IF

                        IF ch = 'S' || ch = 'E'
                        then
                              IF ch = 'S'
                                    lend[bookNum][1] = hh * 60 + mm /*存储时间(分钟)*/
                                    lend[bookNum][0] = 1     /*已借书*/
                              END IF

                              ELSE IF ch = 'E' && (back[bookNum][0] = 0 || back[bookNum][1] < lend[bookNum][1])
                              then
                                    存储时间(分钟)
                                    back[bookNum][0] = 1     /*已还书*/
                              END IF
                        END IF
                              
                        /*借出并归还,且时间合法时*/
                        IF lend bookNum][0] == 1 && back[bookNum][0] == 1 && back[bookNum][1] >= lend[bookNum][1]		
                        then
                              sumTime += back[bookNum][1] - lend[bookNum][1]; // 计算借阅时间的总和   
               	              times++;      //次数累计

              		      lend[bookNum][0] = back[bookNum][0] = 0;      //将借阅判断归零
                        END IF
                  }           
                  END while
、
                        输出借阅总时间和次数
      }
      END For
                               
  • 2.2.2 代码截图





  • 2.2.3 说明和超星视频做法区别,各自优缺点

    • 超星:
      1.将每天借阅记录存放在二维数组之中
      2.思路清晰,代码简洁明了

    • 自己:
      不足
      1.没有使用二维数组存放阅读记录
      2.代码冗长

2.3 切分表达式

  • 2.3.1 伪代码
      定义 flag //判断是否为”首个“数字
      定义 sign //判断是否为负数、正数

      For i = 0 to str[i] = '\n'
            IF flag = 1		//判断首个数字是否为带符号的正负数
            then
                  IF str[i] = '-'
                  then 
                        sign = 1;
		  END IF

                  ELSE IF str[i] = '+'
                  then
                        sign = 2;
                  END IF
            END IF
            
            flag = 0
      
            IF str[i]为0~9的字符
            then
                  将str[i]转化为数字,赋道number中
            
            IF str[i+1]不为0~9的字符
            then 
                  IF str[i+] = '.'
                  then
                        通过sign判断首个数字有没有带有正负号
                        IF sign = 1 || sign = 2
                        then
                              通过switch分支,将输出的number前填上‘+'或’-‘,输出的number不换行
                        END IF
                        
                        ELSE       
                        then
                              输出number不换行
                        END IF
                   END IF
                   ELSE
                   then
                        直接输出number,并换行     
                        
                        number = 0;
			sign = 0;       //变量归零
                   END IF
            END IF

            ELSE
                  IF sign = 0
                  then      
                        IF str[i] = '.'
                        then      
                              输出小数点,不换行
                        END IF
                        
                        ELSE 
                        then
                              输出字符,换行
                        END IF

                        IF str[i] = 40 //左括弧的ASCII码值
                        then
                              flag = 1
                        END IF
                  END IF
            END IF
  • 2.3.2 代码截图




  • 2.3.3 说明和超星视频做法区别,各自优缺点

    • 超星:
      1.遇到数字或小数点,或是符号位的+、-,直接输出不换行
    • 自己:
      不足
      1.将字符数字转化为了数值,未考虑到可以直接输出,导致代码冗长
      2.多判断了符号位的+、-,将+、-连同数值一起输出