C语言笔记-数列例题

数组例题解析

文章目录

  • 数组例题解析
    • @[toc]
    • 一、例7-2:打印Fibonacci数列
      • 1.题目描述
      • 2.了解Fibonacci数列的特性
      • 3.解题思路
      • 4.代码示例
      • 5.例题小结
    • 二、例7-3:顺序查找法
      • 1.题目描述
      • 2.解题思路
      • 3.代码示例
      • 4.例题小结
    • 三、例7-4:数组元素交换
      • 1.题目描述
      • 2.解题思路
      • 3.代码示例
      • 4.例题小结
    • 四、例7-5:选择法排序
      • 1.题目描述
      • 2.关于选择排序
      • 3.解题思路
      • 4.代码示例
      • 5.例题小结
    • 五、例7-6:调查电视节目的热度
      • 1.题目描述
      • 2.解题思路
      • 3.代码示例
      • 4.例题小结
    • 六、例7-7:二分查找法
      • 1.题目描述
      • 2.关于二分查找
      • 3.解题思路
      • 4.代码示例
      • 5.例题小结
    • 七、总结说明
      • 1.数组的重要作用
      • 2.一些关于数组的细节

一、例7-2:打印Fibonacci数列

1.题目描述

用数组计算Fibonacci数列的前10个数,并按每行打印5个数的格式输出。

输入样例

输出样例

1 1 2 3 5 
8 13 21 34 55 

注意:

只需打印Fibonacci数列的前10个数

每行最多只能打印5个数


2.了解Fibonacci数列的特性

在解答本题前,我们需要明白什么是Fibonacci数列

Fibonacci数列(以下简称fi数列),即斐波那契数列,这个数列有一个特点:从数列的第三项开始*(包括第三项)*,每个项都等于它前两个项的和

fi数列的第一、第二项的值分别为:1、1,由此可往后递推出后面项的值。


3.解题思路

利用fi数列的特性,我们可以借助循环递归来进行推算第n个项的值(n ≥ 3, n∈Z),这里我们使用循环的方法进行解答;
每推算一个项,就将其储存到数组中*(即对数组的元素逐个赋值)*;
赋值结束之后,再利用循环逐个将数组里的值打印输出,并结合取模运算,按照题目要求,每打印5个数字就进行一次分行。


4.代码示例

#include
#define N 10//利用宏定义,定义一个值为10的常量N

int main() {
	int fi[N];//定义一个有10个int型元素的数组,储存fi数列
   	fi[0] = fi[1] = 1;
   	/*<数组赋值>*/
	for (int i = 0; i < N; i++) {
   		if (i < 2) { fi[i] = 1; }//第一、第二项的值均为1
   		else { fi[i] = fi[i - 1] + fi[i - 2]; }//从第三项(i=2)开始推算,第i项的值等于第i-1项与第i-2项(即前两项)之和
 	}
   	for (int i = 0; i < N; i++) {
 		printf("%d ", fi[i]);
   		if (i % 5 == 4) { printf("\n"); }//每输出五个数之后,进行换行操作
   	}
 	return 0;
   }

对于<数组赋值>部分,推算和赋值的流程如下图所示:
C语言笔记-数列例题_第1张图片

*注: 黄 绿 色 数 字 是 \textcolor{YellowGreen}{黄绿色数字}是 绿 红 色 数 字 的 和 \textcolor{Red}{红色数字}的和


5.例题小结

  • 本题的关键在于,如何运用数组和循环来推算Fibonacci数列

  • 一般来说,在定义数组时,其方括号中只能使用常量常量表达式 (严格地说,应该是一个有限正整数有限正整数的表达式

  • 定义数组时,数组方括号中的数字m,表示长度,表明该数组有m个元素;而在调用数组的元素时,数组方括号中的数字n(即下标),表示第n+1个元素
    -例如:

    定义时,int arr[m]; (m = 10) 那么调用时,arr[n]; (0 ≤ n < m, n ∈ N)


二、例7-3:顺序查找法

1.题目描述

输入正整数**n和整数x,再输入n个整数并存入一个数组a中,然后在数组a中查找x**,如果找到,输出相应元素的最小下标,否则,输出"Not Found"。

输入样例1

5 9
2 9 8 1 9

输出样例1

1

输入样例2

4 101
9 8 -101 10

输出样例2

Not Found

注意:

对于这类无法确定数组长度的题目,在定义的数组时长度要足够大,避免出现数组越界的情况

只需要输出一次下标,且该下标是所有符合条件的下标中最小的一个

输出“Not Found”时的拼写和字母大小写是否正确


2.解题思路

定义一个整型变量**index**,并赋值一个负数,用于记录下标;

使用一次循环,将所输入n个整数全部储存到数组中;

再使用一次循环,检测数组中有无符合条件的元素,并通过变量**index**记录符合条件的元素的下标;

根据记录的变量分辨情况,输出对应的结果。


3.代码示例

#include

int main()
{
	int n, x;
   	int index = -1;//记录下标的变量,初始赋值一个负数
	int a[1000];//定义一个较大的长度,防止出现异常
	scanf("%d %d", &n, &x);
   	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
	}
	/*<遍历查找>*/
   	for (int i = 0; i < n; i++) {
		if (a[i] == x)//发现符合条件(等于x)的数
		{
   			index = i;//记录下标,使得index≥0
   			break;//跳出循环,不再往后检测
   		}
   	}
	if (index < 0) { printf("Not Found"); }//index<0,表示没能在数组范围内找到x
   	else { printf("%d", index); }//index≥0,表示在数组范围内找到了x
	return 0;
}

4.例题小结

  • 在不确定所输入数字数量的情况下,一定要考虑数组的长度,避免出现异常和报错
  • 养成良好的习惯,善用break关键字,减少不必要的循环,在一定程度上能提高程序的运行效率

三、例7-4:数组元素交换

1.题目描述

输入正整数**n**(1 < n ≤ 10),再输入n个整数,输出最小值和它对应的下标,并与第一个数交换,再输出交换后的n个数

输入样例1

5
5 9 8 2 15

输出样例1

2 3
2 9 8 5 15

输入样例2

6
-1 9 2 8 1 6

输出样例2

-1 0
-1 9 2 8 1 6

注意:

只有最小值需要与第一个数交换

先输出最小值,再输出最小值的下标,最后再输出经过交换处理的所有数


2.解题思路

定义两个整型变量**minindex,分别用于记录最小值**、最小值的下标

使用一次循环,将所输入n个整数全部储存到数组中;

再使用一次循环,查找数组中值最小的元素,并通过变量**minindex,分别记录该元素的下标**;

令最小值和第一个元素交换;

输出结果。


3.代码示例

#include

int main()
{
	int n;
	int min;//最小值
	int index;//最小值下标
	int a[10];
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
	}
	/*<遍历查找>*/
	for (int i = 0; i < n; i++) {
		if (i == 0 || a[i] < min) {//附加一个条件(i == 0),表示假设最小值min是第一个元素的值
			min = a[i];//记录最小值
			index = i;//记录最小值的下标
		}
	}
	/*<元素互换>*/
	//这里了一个类似的骚操作:用一个空杯子将一瓶可口可乐和一瓶百事可乐调包
	int temp = a[0];
	a[0] = a[index];
	a[index] = temp;
	printf("%d %d\n", min, index);
	for (int i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
	return 0;
}

输入样例2为准,对于<元素互换>部分,各变量的变化流程如下图所示:
在这里插入图片描述

在这里插入图片描述

*注: 绿 色 数 据 将 会 取 代 \textcolor{ForestGreen}{绿色数据}将会取代 绿 红 色 数 据 或 与 之 互 换 \textcolor{Red}{红色数据}或与之互换


4.例题小结

  • 在本题中,元素的交换是关键点,偶尔会有一些童鞋将交换的方法写成下面这样的形式:
a[0] = a[index];
a[index] = a[0];

​ 逻辑上好像没啥毛病,但是在程序中,代码会严格按照各自的顺序运行处理,因此这样的写法是不可取的


四、例7-5:选择法排序

1.题目描述

输入一个整数**n**(1 < n ≤ 10),再任意输入n个整数,用选择法将它们从小到大排序后输出。

输入样例1

5
3 5 2 8 1

输出样例1

1 2 3 5 8

输入样例2

8
5 -1 7 4 -6 0 4 1

输出样例2

-6 -1 0 1 4 4 5 7

注意:

按照从左到右的顺序,数字由小到大排列


2.关于选择排序

选择排序(Selection sort),是数组的基础排序法的一种,和冒泡排序(Bubble Sort)相比,它的优点在于每次循环只需执行一次元素交换,因此运行效率也会更高一些,但它的主要缺点是不稳定,感兴趣的童鞋可以问度娘,这里不做过多的解释。

工作原理:每次从未排序的元素中找出这些元素的最值,并将这个最值与每次查找的第一个元素的值交换,如此循环,直至范围内的元素不足2个时,即意味着排序结束 。


3.解题思路

定义一个变量**index,用于记录每次排序时查找到的最小值的下标;
先利用一层循环,对每个元素进行排列;
在上一层循环中,再嵌入一层循环,用于查找未排序范围内的最小值;
每次查找到最小值后,根据
index记录,使其下标与index相同的元素(arr[index])与未排序范围内的最左端的数(arr[i]**)交换;
嵌套循环结束后,表明排序完成,此时输出结果即可。


4.代码示例

#include
#define N 10
int main()
{
	int n;
	int index;//最小值的下标 
	int arr[N];
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &arr[i]);
	}
	/*<选择排序>*/
	for (int i = 0; i < n; i++) {//第一层循环
		index = i;//假设最小值是第i个元素
		for (int j = i; j < n; j++) {//第二层循环,每次查找的第一个元素是arr[i]
			if (arr[index] > arr[j]) {//检测到比当前最小值更小的数
				index = j;//原最小值的下标替换为当前最小值的下标
			}
		}
		int temp = arr[i];//元素互换
		arr[i] = arr[index];
		arr[index] = temp;
	}
	for (int i = 0; i < n; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

输入样例1为准,数组各个元素的值在n次排列中的变化如下表所示:

arr[0] arr[1] arr[2] arr[3] arr[4] 查找结束后
i = 0 (3) 5 2 8 (1) arr[0]与arr[4]的值互换
i = 1 1 (5) (2) 8 3 arr[1]与arr[2]的值互换
i = 2 1 2 (5) 8 (3) arr[2]与arr[4]的值互换
i = 3 1 2 3 (8) (5) arr[3]与arr[4]的值互换
i = 4 1 2 3 5 8 排序完成,循环结束

输入样例1为准,对于<选择排序>部分,查找最小值和交换的部分流程如下图所示:

C语言笔记-数列例题_第2张图片

*注: 两 个 不 同 的 绿 色 数 字 在 查 找 结 束 后 相 互 交 换 , 两个\textcolor{ForestGreen}{不同的绿色数字}在查找结束后相互交换, 绿 红 色 方 框 为 未 进 行 排 序 的 数 组 范 围 \textcolor{Red}{红色方框}为未进行排序的数组范围


5.例题小结

  • 该题的难点在于查找和交换最值的部分,如何用循环进行查找和交换位置的确定是解决该题的关键

  • 如何用循环进行查找和交换位置的确定是关键


五、例7-6:调查电视节目的热度

1.题目描述

某电视台要对该台的8个电视栏目的受欢迎程度进行一次调查,共调查了n位观众(1 ≤ n ≤ 1000),现要求编写程序,输入观众的投票,统计输出各个栏目的得票情况

输入样例

6
3 1 6 9 8 1

输出样例

1 2
3 1
6 1
8 1

注意:

可能会有观众投出无效票

只输出得票数不为0的栏目编号


2.解题思路

定义一个整型变量**n**,表示调查了几位观众;

使用一次循环,对每个观众的投票进行判断和统计;

筛选出得票数不为0的栏目,并输出对应的结果。


3.代码示例

#include
#include//memset函数的头文件
#define N 9//根据电视栏目数定义的宏定义常量,这里定义为9有一定的好处

int main()
{
	int n;
	int program[N];//各电视栏目票数
	//for (int i = 0; i < N; i++) program[i] = 0;//利用循环,数组元素全部赋值为0
	memset(program, 0, sizeof(program));//利用memset函数,将数组元素全部赋值为0
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int vote;//观众给对应栏目投的票
		scanf("%d", &vote);
		if (vote >= 1 && vote <= 8) {//有效票
			program[vote]++;//栏目票数加一
		}
	}
	for (int i = 1; i < N; i++) {//这里i初值为1,跳过program[0]
								 //因为在该数组中,program[8]是第8个栏目
								 //加上program[0],一共九个元素,所以才将N定义为9
		if (program[i] > 0) {
			printf("%d %d\n", i, program[i]);//输出栏目对应的编号和票数
		}
	}
	return 0;
}

4.例题小结

  • 遇到这类题目时,数组的长度不一定要根据题目给出的一些数来决定,可以依情况稍稍调整,使编写过程更简洁*(以本题为例,为电视栏目所定义的数组长度N是9而不是8,不然在编写时就需要为某些变量进行±1等操作,比如program[i - 1],vote -= 1等)*

  • 在编写时,多多考虑可能出现的特殊情况,最好作出相应的对策


六、例7-7:二分查找法

1.题目描述

输入正整数**n(1 < n ≤ 10)和整数x,再输入n个从小到大有序排列不重复的整数,并存入一个数组a中,然后在数组a中查找x**,如果找到,输出相应元素的下标,否则,输出"Not Found"。

输入样例1

10 7
1 2 3 4 5 6 7 8 9 10

输出样例1

6

输入样例2

6 1
-3 -1 0 2 4 6

输出样例2

Not Found

注意:

查找到目标值后,输出的是目标值的在数组中的下标

输出“Not Found”时的拼写和字母大小写是否正确


2.关于二分查找

二分查找,又称折半查找(Binary search),是一种比遍历要高效的查找方法,但只有查找目标有序排列无重复的目标时可用,因此这种查找方法主要适用于有序的顺序表。

工作原理:获取一个区间,然后取其中间值,将整个区间一分为二(每个区间是整个区间的½),再根据目标值可能出现的情况选择其中一个区间,再继续将这个区间一分为二(每个区间是整个区间的¼),如此循环,直至找到目标值区间不可再分


3.解题思路

二分查找的查找方式具有规律性,因此可以借助循环递归来查找目标值,这里我们使用递归的方法进行解答;
定义一个函数方法**binarySearch,对一个区间进行判断,取其中间值,将区间划分成两个半区间,确定目标值可能会在的哪一半区间;
确定目标值可能所在的那半区间后,立即再调用这个
binarySearch**,对这半个区间再进行一次判断,如此递归,直到找到目标值或区间不可再分;

根据查找结束后返回的下标,输出对应的结果。


4.代码示例

#include

int binarySearch(int l, int r, int x, int a[])
{
    int mid = (l + r) >> 1;//闭区间[l,r]的中间值
    int idx = -1;//记录下标的变量,初值为负数,表示还没找到x
    if(x == arr[mid]) idx = mid;//发现中间值a[mid]等于x,则记录其下标
    else if(arr[l] <= x && x < a[mid])//x在前半区间:a[l] ≤ x < a[mid]
        idx = binarySearch(l , mid - 1, x, arr);//在数组[l,mid-1]范围中继续二分查找
    else if(arr[r] >= x && x > arr[mid])//x在后半区间:a[mid] < x ≤ a[r]
        idx = binarySearch(mid + 1 , r, x, arr);//在数组[mid+1,r]范围中继续二分查找
    return idx;//返回下标变量idx的值
}

int main()
{
    int n, x, index;
    int a[10];
    scanf("%d %d", &n, &x);
    for(int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    index = binarySearch(0, n-1, x, arr);//第一次二分查找开始
    if(index < 0) { printf("Not Found"); }//没找到x,index是负数
    else { printf("%d", index); }//找到x,index是非负数
    return 0;
}

输入样例1为准,binarySearch函数查找流程如下图所示:

在这里插入图片描述

*注: 红 色 方 框 表 示 本 次 查 找 的 区 间 范 围 \textcolor{Red}{红色方框}表示本次查找的区间范围


5.例题小结

  • 本题的关键在于如何理解和实现二分查找的方法

  • 递归和循环都各有千秋。相比于循环,递归更难以理解,但对于某些复杂的问题,使用递归的方法能将程序的编写过程简单化

七、总结说明

1.数组的重要作用

所谓的数组(Array),是一种有序的元素序列,即是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式,它不仅是与**指针(Pointer)**相关的一部分内容,更是绝大多数编程语言的一种重要工具。

数组的出现带给我们方便,其中也包含了许多的算法(Algorithm),这些算法对我们处理数组非常有用,因此在掌握各种算法之前,了解数组的逻辑和结构是非常重要的。

数组主要用于统计(Statistic)大量的数据处理,当然,数组的用途并不仅局限于此,如果肯下功夫去学的话,甚至能用它玩出各种花样来。

2.一些关于数组的细节

  • 一定要弄清楚,定义数组和调用数组的方括号中的数字具有不同含义,定义时表示长度(Length) ,调用时表示下标,下标的值往往小于长度且不为负数;
  • 静态数组的长度尽可能地使用常量(Constant)常量表达式来定义;
  • 数组的长度不一定要直接定义,也可以通过根据初值的数目来自动定义,如:int a[] = { 0,1,2 };表示定义一个长度为3的整型数组;
  • 数组越界是未知而又危险的,因此在调用数组时,务必要避免这种情况发生;
  • 数组的长度可以很大,但往往是有限的,长度的上限因数据类型和所分配的空间和内存的大小而异;
  • 静态分配数组时,各个元素的地址通常都是连续的,这点对于理解指针板块的动态数组的很重要;
  • 字符串(String)本质上也是一种数组,它由多个char型数据组成,所以在c语言中通常用char 标识符[]char *标识符来表示定义一个字符串,需要注意的是,字符串最后一个元素都以**'\0'**作为字符串的结尾;

*注: 上 述 内 容 基 于 c 语 言 编 写 上述内容基于\textcolor{Red}{c语言}编写 c

主编:佚名

你可能感兴趣的:(《C语言》课程笔记,c语言,数组)