自学C语言第2篇的核心技术:第8章 数组

第2篇 核心技术

本篇介绍了C语言中数组,函数和指针这三大部分内容,并将前面所学的基础内容融入其中,学习更高级的程序设计知识。读者学习完本篇知识,可编写一些简单的C语言应用程序。
在这里插入图片描述

第8章 数组

编写程序过程中,有时会用到大量相同类型的数据。如果每个变量都需要单独定义,编程过程将会变得极其烦琐。使用数组可以很好地解决这个问题。
本章致力于使初学者掌握一维数组和二维数组的应用,能利用所学知识解决一些实际问题;掌握字符数组的使用及其相关操作;并能通过数组掌握常见的排序算法。
本章的知识架构及重难点如下:
在这里插入图片描述

8.1 一维数组

8.1.1 一维数组的定义和引用

1.一维数组的定义

一维数组实际上是一组相同类型数据的线性集合。其定义形式如下:
类型说明符 数组标识符[常量表达式];
1.类型说明符:表示数组中元素的类型。
2.数组标识符:表示该数组型变量的名称,命名规则与变量名一致。
3.常量表达式:定义了数组中存放的数据元素个数,即数组长度。
例如,定义一个包含5个整型元素的数组,代码如下:

int iArray[5];

代码中的int为数组元素的类型,iArray为数组变量名,括号中的5表示数组中包含5个元素。

2.一维数组的引用

数组定义后,可以引用其中的数组元素,引用方式为“数组标识符[下标]"。
例如,引用数组iArray中的第3个变量,格式为iArray[2]。其中,iArray是数组变量名,2为数组下标。有的读者会问:为什么引用第3个数组元素使用的是下标2呢?这是因为数组下标是从0开始的,iArray[0]表示第一个元素,iArray[1]表示第2个数组元素,iArray[2]表示第3个数组元素。

注意:
数组下标可以是整型常量或整型表达式。在数组iArray[5]中,只能使用iArray[0]、iArray[1]、iArray[2]、iArray[3]、iArray[4],而不能使用iArray[5]。若使用iArray[5],则会出现下标越界错误。

【例8.1】使用数组保存手机号 在本实例中,使用数组保存用户输入的手机号,并输出显示。
在这里插入图片描述
(1)定义index作为循环控制变量,定义数组iArray[11]用来保存11位手机号码。
(2)通过一个for循环,依次输入11位手机号码(用空格隔开),存储到各数组元素对应的地址中。这里,iArray[index]就是对数组元素的引用,"&"为取地址符。
(3)循环输出各数组元素,得到完整的手机号码。
运行程序,显示效果如上图所示。

8.1.2 一维数组的初始化

一维数组的初始化,可以用以下3种方法实现。
(1)定义数组时直接对数组元素赋初值(数组元素值放在一对大括号中)。例如:

int i,iArray[6]={1,2,3,4,5,6};

定义和初始化之后,iArray[0]=1,iArray[1]=2,iArray[2]=3,iArray[3]=4,iArray[4]=5,iArray[5]=6。
(2)如果只给一部分数组元素赋值,则未赋值的元素默认为被赋值0。例如:

int iArray[6]={0,1,2};

数组iArray包含6个元素,但初始化时只给出了3个值,结果是数组前3个元素得到赋值,后3个元素被默认赋值为0。
(3)当对全部数组元素都赋初值时,可以不指定数组长度。例如:

int iArray[]={1,2,3,4};

上述代码的大括号中有4个元素,因此系统会默认为该数组变量的长度为4。
【例8.2】计算篮球平均成绩 记分员记录了某球员在10场篮球比赛中的成绩,求该球员的平均成绩。
在这里插入图片描述
程序中首先定义一个数组grade,初始化数值为10场球赛的成绩;定义整型变量total,用来保存总成绩;定义浮点型变量avg,用来保存平均成绩。接着通过一个for循环,控制循环变量使用10场球赛成绩累加,相加结果赋给toal。最后计算平均成绩,赋给avg,并用printf函数输出结果。运行程序,显示效果如上图所示。

8.1.3 一维数组的应用

一个班级中往往有很多学生,使用数组来保存这些学生的姓名、编号等,管理起来非常方便。
【例8.3】输出插队之后的编号 体育课上,老师按身体高矮给20名同学编号。刚编完号,一位男生姗姗来迟,老师比对后将他排在8号位置,并重新排列后面的同学。使用数组,输出男生插队后有变化的学生编号。代码如下:
在这里插入图片描述
在这里插入图片描述
可以看出,要想从第8名同学开始编号,就应该从下标7开始。运行程序,效果如上图所示。
编程训练
训练1:保存语、数、外成绩 编写程序,输入语文、数学、英语3门学科的成绩,使用数组保存并输出。运行效果如下:

请输入语文、数学、英语的成绩:
89  95  96
语文、数学、英语的成绩分别如下:
89  95  96

在这里插入图片描述

训练2:清空购物车 2018年亚运会上,中国女排3:0战胜泰国女排,夺得冠军,当时阿里巴巴承诺要帮女排姑娘们清空购物车。模拟输出某女排姑娘的购物车清单。运行效果如下:

淘宝购物车清单:
========生活用品类:============
纸抽,纸巾,收纳箱,水杯,垃圾袋,剪刀,挂钩,拖鞋,小闹钟
==========化妆品类:============
保湿套装,气垫cc,隔离霜,防晒霜,眉粉,眼影色盘,睫毛䯧
=============运动类商品:==============
运行服,球鞋,护腕,护膝,护掌,排球,瑜伽垫,瑜伽球
===============保健类商品:==================
蛋白粉,口服液,眼部按摩仪,血压计,脚底按摩器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2 二维数组

8.2.1 二维数组的定义和引用

1.二维数组的定义

二维数组可以看作是特殊的一维数组,其各个元素仍然是一个数组。其定义形式如下:

数据类型	数组名[常量表达式1][常量表达式2];

其中,“常量表达式!”定义了二维数组的行数,“常量表达式2”定义了二维数组的列数。不管是行下标还是列下标,其索引都是从0开始的。因此,一个m行n列的二维数组array[m][n],其行下标数值范围为0~m-1,列下标取值范围为0-n-1,最大下标元素是array[m-1][n-1]。
例如,定义一个3行4列的整型数组,代码如下:

int array[3][4];

二维数组array[3][4]共包含12个数组元素(3x4),分别为array[0][0]、array[0][1]、array[0][2],array[0][3]、array[1][0]、array[1][1]、array[1][2]、array[1][3]、array[2][0]、array[2][1]、array[2][2]、array[2][3]。可见,C语言中数组是按行排列的,数组array[3][4]在内存中先按行顺序存放,依次为array[0]行、array[1]行和array[2]行,每行有4个元素,也依次存放。

2. 二维数组的引用

二维数组元素的引用形式为“数组名[下标][下标]”。例如,array[1][2]表示对array数组的第2行第3个元素进行引用。和一维数组一样,二维数组也要注意下标越界的问题。例如:
int array[2][4];							/*对数组元素进行赋值*/
array[2][4]=9;								/*错误,下标越界!*/

8.2.2 二维数组的初始化

二维数组赋初值,有以下4种情况。
(1)将所有数据写在一个大括号内,按照数组元素排列顺序对元素赋值。例如:

int array[2][2]={1,2,3,4};

如果大括号内的数据少于数组元素的个数,则系统会默认后面未被赋值的元素值为0。
(2)为所有元素赋初值时,可以省略行下标,但不能省略列下标。例如:

int array[][3]={1,2,3,4,5,6};

系统会根据数据的个数进行分配,一共有6个数据,而数组分为3列,因此数组有2行。
(3)分行给数组元素赋值。例如:

int a[2][3]={{1,2,3},{4,5,6}};

在分行赋值时,可以只对部分元素赋值。例如,下面的代码中,a[0][0]的值是1;a[0][1]的值是2;a[0][2]的值是0; a[1][0]的值是4;a[1][1]的值是5;a[1][2]的值是0。

int a[2][3]={{1,2},{4,5}};

(4)直接对数组元素赋值。例如:

int a[2][3];
a[0][0]=1;
a[0][1]=2;

【例8.4】魔方阵数据 一个3x3的网格,将1-9的数字放入方格中(矩阵中心元素为5),使得每行、每列以及每条对角线上的3个数相加都相同。通过键盘为二维数组元素赋值,并显示二维数组。
在这里插入图片描述
(1)程序先依次输入相应的数组元素值,然后将这个3行3列的数组输出。
(2)给数组赋值时,使用了for循环嵌套,第一层循环行,第二层循环列,用printf函数输出元素名,用scanf函数输入数据,赋给对应的数组元素。
(3)输出数组时,也使用了for循环嵌套,第一层循环行,第二层循环列,用printf函数逐行输出数组元素。为了使输出的数据更整齐,使用制表符“\t"来控制间距。运行程序,显示效果如上图所示。

8.2.3 二维数组的应用

【例8.5】将矩阵行列对换 本实例中,把二维数组中各行的元素换成列元素,各列的元素换成行元素,生成一个新的二维数组。代码如下:
在这里插入图片描述
在这里插入图片描述
(1)利用双重for循环为2行3列的数组赋值,然后分行显示其元素。
(2)通过一个双重for循环,第二维数组a中的元素赋值到转置后的二维数组b中。
(3)通过循环控制,将转置后的二维数组b中的元素输出。
运行程序,显示效果如上图所示。
编程训练:
训练3:打印数组对角线上的字符 有如下二维数组:

char ccArray[5][5]={
{'a','b','c','d','e'},
{'b','a','8','d','d'},
{'c','d','a','e','c'},
{'d','j','f','a','b'},
{'e','d','a','f','a'},
};

在这里插入图片描述
在这里插入图片描述

编写程序,打印数组对角线(左上至右下)上的字符。输出结果如下:

对角线上的字符是:a
对角线上的字符是:a
对角线上的字符是:a
对角线上的字符是:a
对角线上的字符是:a

在这里插入图片描述

训练4:求平均成绩 用二维数组求表8.1中各科的平均成绩。
在这里插入图片描述
运行如果如下:

请输入成绩:
array[0][0]=93
array[0][1]=87
array[0][2]=90
array[0][3]=76
array[0][4]=70
array[1][0]=90
array[1][1]=76
array[1][2]=60
array[1][3]=80
array[1][4]=81
array[2][0]=70
array[2][1]=88
array[2][2]=72
array[2][3]=77
array[2][4]=96
数学的平均成绩是(取整数):83
语文的平均成绩是(取整数):77
英语的平均成绩是(取整数):80

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.3 字符数组

数组中的元素类型为字符型时,称为字符数组。字符数组中的每个元素可以存放一个字符。字符数组的定义和引用方法与其他数据类型相似。

8.3.1 字符数组的定义和引用

1.字符数组的定义

字符数组的定义形式如下:

char 数组标识符[常量表达式]

例如,下述代码定义了一个字符数组cArray,该数组中包含5个字符型的变量元素。

char cArray[5];

2.字符数组的引用
字符数组的引用也采用下标的形式。例如,引用数组cArray中的元素,并依次赋值,代码如下:

cArray[0]='H';
cArray[1]='e';
cArray[2]='l';
cArray[3]='l';
cArray[4]='o';

8.3.2 字符数组的初始化

在对字符数组进行初始化操作时,有以下几种方法。
(1)逐个字符赋给数组中的元素。
这是最容易理解的初始化字符数组的方式。例如:

char cArray[5]={'H','e','l','l','o'};

定义了一个包含5个元素的字符数组,在初始化的大括号中,每个字符赋值给一个数组元素。
(2)定义字符数组的同时进行初始化,此时可以省略数组长度。
如果初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数来确定数组长度。例如,上面初始化字符数组的代码可以写成:

char cArray[]={'H','e','l','l','o'};

代码中定义的cArray[]没有给出数组大小,但系统会自动根据初值的个数确定数组长度为5。
(3)利用字符串给字符数组赋初值。
字符数组可用来存放字符串。因此,也可以用字符串的方式对字符数组进行初始化赋值。例如:

char cArray[]={"Hello"};

或者将“{}”去掉,写成:

char cArray[]="Hello";

【例8.6】打印停车场标志 本实例将利用字符数组输出“Park"。定义一个字符数组,通过初始化操作保存一串字符,然后通过循环引用将每一个数组元素输出。

#include<stdio.h>
int main()
{
	char cArray[5]={'P','a','r','k'};					/*定义字符数组并初始化*/
	int i;												/*循环控制变量*/
	for(i=0;i<5;i++)									/*循环输出字符数组元素*/
	{
		printf("%c",cArray[i]);
	}
	printf("\n");									/*输出换行*/
	return 0;										/*程序结束*/
}

在这里插入图片描述

初始化字符数组时要注意,每个元素都要使用一对单引号(‘ ’)括起来。在for循环中,因为输出的类型是字符型,所以printf函数中使用的是"%c"格式符。通过循环变量i,cArray[i]实现了对数组中不同元素的引用。运行程序,显示效果如上图所示。

8.3.3 字符数组的结束标志

在c语言中,使用字符数组保存字符串时,系统会自动为其添加“\0"作为结束符。也就是说,用字符串方式赋值比用字符逐个赋值要多占一个字节,多占的这个字节用于存放字符串结束标志"\0"。
例如,使用下述代码可以初始化一个字符数组:

char cArray[]="Hello";

上面的字符数组cArray在内存中的实际存放情况如图8.7所示。
在这里插入图片描述
“\0"是由C编译系统自动加上的。因此上面的赋值语句等价于:

char cArray[]={'H','e','l','l','o','\0'};

字符数组并不要求最后一个字符为”\0“,甚至可以不包含"\0"。因此下面的写法也是合法的:

char cArray[5]={'H','e','l','l','o'};

是否加“\0",应根据需要来决定。由于系统对字符串常量会自动加一个”\0",因此,为了使处理方法一致,且便于测定字符串的实际长度,在字符数组中也常常人为地加上一个“\0"。例如:

char cArray[6]={'H','e','l','l','o','\0'};
注意
如果在一个字符数组中先后存放多个不同长度的字符串,则应使数组长度大于最长的那个字符串的长度。

8.3.4 字符数组的输入和输出

字符数组的输入和输出可以使用两种格式字符:“%c"和”%s"。使用格式字符“%c"可实现数组中字符的逐个输入与输出。例如,逐个输出字符数组中的元素:

for(i=0;i<5;i++)						/*进行循环*/
{
	printf("%c",cArray[i]);				/*逐个字符输出数组元素*/
}

使用格式字符”%s"可将整个字符串输入或输出。例如,输出一个字符串:

char cArray[]="GoodDay!";							/*初始化字符数组*/
printf("%s",cArray);								/*输出字符串*/

使用格式字符"%s"输出字符串时,需要注意以下几种情况。
1.输出字符中不包括结束符“\0"。
2.printf函数中的输出项是字符数组名cArray,而不是数组中的元素名cArray[0]等。
3.即使数组长度大于字符串实际长度,也只会输出到”\0"为止。
4.如果一个字符数组中包含多个“\0"结束字符,则在遇到第一个”\0“时输出就结束。
【例8.7】输出一条心灵鸡汤 输出名言“Education is the door to freedom"(教育是通向自由之门)。对定义的字符数组初始化,然后采用两种方式输出字符数组中保存的数据,先逐个字符输出数组元素,再直接将字符串整体输出。
在这里插入图片描述
在代码中,对数组元素逐个字符输出时,使用的是循环方式。直接输出字符串时,使用的是printf函数中的格式字符"%s"。要注意,直接输出字符串时,不能使用格式字符"%c"。运行程序,显示效果如上图所示。

注意:
C语言中只存在字符类型,不存在字符串类型。字符类型只能存放单个字符,如果要存放字符串,就需要使用字符数组。当然,也可以为使用字符型指针存放字符串。

8.3.5 字符数组的应用

【例8.8】计算字符串长度 随机输入一个字符串,计算该字符串的长度。
在这里插入图片描述
使用gets函数将输入的字符串保存在cArray字符数组中。使用for循环判断当前数组元素是否为结束符“\0",如果不是,则字符串长度加1;如果是,则退出循环。运行程序,显示效果如上图所示。
编程训练
训练5:统计单词个数 用户输入一行字符,然后统计其中有多少个单词。要求每个单词之间用空格分隔开,且最后的字符不能为空格。输出结果如下:

在这里插入图片描述

训练6:确定男女主角 利用字符数组保存男女主角的名字,使用for循环遍历字符数组,通过两种形式分别输出男女主角的名字。输出结果如下:

这部电影的男主角分别是:
雨石,玉轩,团子
这部电影的女主角分别是:
小点,紫轩,若美

在这里插入图片描述

8.4 多维数组

多维数组的声明和二维数组相同,只是下标更多。其一般形式如下:

数值类型  数组名[常量表达式1][常量表达式2]...[常量表达式n];

例如,下面的代码中定义了一个三维数组iArray1和一个四维数组iArray2。

int iArray1[3][4][5];
int iArray2[4][5][7][8];

由于数组元素的位置可以通过偏移量计算出来,因此对于三维数组a[m][n][p]来说,元素a[i][j][k]所在的地址是从a[0][0][0]算起到inp+j*p+k个单位的位置。

8.5 数组的排序算法

数组是一组有序数据的集合。这里,”有序”指的是数组元素在内存中的存放方式是有序的,其引用方式也有规律可循,而不是说数组元素有数组中是按照数值大小有序排列的。那么,有没有可能让数组元素按照数值大小有序排列呢?当然可以,下面就一起来学习下数组的各种常用排序算法。

8.5.1 选择法排序

选择法排序的原理如下:每次在待排序数组中查找最大或最小的数组元素,将其值与最前面没有进行过排序的数组元素的值互换。这里,由大到小排序应查找最大值,由小到大排序则应查找最小值。下面以数字9、6、15、4、2为例,利用选择法使其从小到大排序,每次交换后的数字顺序如表8.2所示。
在这里插入图片描述
可以发现,第一次排序过程中,将第一个数字和最小的数字进行了位置互换;第二次排序过程中,将第二个数字和剩下数字最小的数字进行了位置互换;依次类推,每次都将下一个数字和剩余数字中最小的数字进行位置互换,直到将一组数字按从小到大排序。
【例8.9】选择法从小到大排序 本实例中,声明了一个整型数组和两个整型变量。其中,整型数组用于存储用户输入的10个数值,而整型变量用于存储数量最小的数组元素和该元素的位置。通过双层循环进行选择法排序,最后将排好序的数组元素输出。
在这里插入图片描述
在这里插入图片描述
(1)声明一个整型数组,并通过键盘输入为数组元素赋值。
(2)设置一个嵌套循环,第一层循环为前9个数组元素,并在每次循环时将对应当前次数的数组元素设置为最小值(如果当前是第3次循环,那么将数组中第3个元素,也就是下标为2的元素设置为当前的最小值);在第二层循环中,循环比较该元素之后的各个数组元素,并将每次比较结果中较小的数设置为最小值,在第二层循环结束时,将最小值与开始时设置为最小值的数组元素进行互换。当所有循环都完成以后,就将数组元素按照从小到大的顺序重新排列了。
(3)循环输出数组中的元素,并在输出5个元素以后换行,在下一行输出后面的5个元素。运行程序,显示效果如上图所示。

8.5.2 冒泡法排序

冒泡法排序的原理如下:每次比较数组中相邻的两个数组元素的值,将较小的数排在较大的数前面,可实现数组元素从小到大排序;每次将较大的数排在较小的数前面,可实现数组元素从大到小排序。
仍以数字9、6、15、4、2为例,对这几个数字进行冒泡法排序,使其从小到大排列。每次排序后的顺序如表8.3所示。
在这里插入图片描述
可以发现,在第一次排序过程中,将最小的数字移动到第一的位置,并将其他数字依次向后移动;在第二次排序过程中,从第二个数字开始的剩余数字中选择最小的数字,并将其移动到第二的位置,剩余数字依次向后移动;依次类推,每次都将剩余数字中最小的数字移动到当前剩余数字的最前方,直到将一组数字按从小到大排序为止。
【例8.10】冒泡法从小到大排序 声明一个整型数组和一个整型变量,整型数组用于存储用户输入的数字,整型变量作为两个元素交换时的中间变量,通过双层循环进行冒泡法排序,最后将排好序的数组输出。
在这里插入图片描述
在这里插入图片描述
(1)声明一个整型数组,并通过键盘输入为数组元素赋值。
(2)设置一个嵌套循环,第一层循环为后9个数组元素。
在第二层循环中,从最后一个数组元素开始向前循环,直到前面第一个没有进行排序的数组元素。循环比较这些数组元素,如果在比较中后一个数组元素的值小于前一个数组元素的值,则将两个数组元素的值进行互换。当所有循环都完成以后,就将数组元素按照从小到大的顺序重新排列了。
(3)循环输出数组中的元素,并在输出5个元素以后进行换行,在下一行输出后面的5个元素。运行程序,显示效果如上图所示。

8.5.3 交换法排序

交换法排序是将每一位数与其后的所有数一一比较,如果发现符合条件的数据,则交换数据。首先,用第一个数依次与其后的所有数进行比较,如果存在比其值大(小)的数,则交换这两个数,继续向后比较其他数直至最后一个数。然后再使用第二个数与其后面的数进行比较,如果存在比其值大(小)的数,则交换这两个数。继续向后比较其他数,直至最后一个数比较完成。
下面以数字9、6、15、4、2为例,进行交换法排序。每次排序后的顺序如表8.4所示。
在这里插入图片描述
可以发现,在第一次排序过程中将第一个数与后边的数依次进行比较。首先比较9和6,9大于6,交换两个数的位置,然后数字6成为第一个数字;用6和第3个数字15进行比较,6小于15,保持原来的位置;然后用6和4进行比较,6大于4,交换两个数字位置;再用当前数字4与最后的数字2进行比较,4大于2,则交换两个数字的位置,从而得到表8.3中第一次的排序结果。然后使用相同的方法,从当前第二个数字9开始,继续和后面的数字进行比较,如果遇到比当前数字小的数字则交换位置,依次类推,直到将一组数字按从小到大排序为止。
【例8.11】交换法从小到大排序 声明一个整型数组和一个整型变量,其中整型数组用于存储用户输入的数字,整型变量则作为两个元素交换时的中间变量,然后通过双层循环进行交换法排序,最后将排好序的数组输出。
在这里插入图片描述在这里插入图片描述
(1)声明一个整型数组,并通过键盘输入为数组元素赋值。
(2)设置一个嵌套循环,第一层循环为前9个数组元素,然后在第二层循环中,将第一个数组元素后面的数组元素依次进行比较,如果后面的数组元素值小于当前数组元素值,则交换两个元素值,然后使用交换后的第一个数组元素继续与后面的数组元素进行比较,直到本次循环结束。将最小的数组元素值交换到第一个数组元素的位置,然后从第二个数组元素开始,继续与后面的数组元素进行比较。依次类推,直到循环结束,就将数组元素按照从小到大的顺序重新排列了。
(3)循环输出数组中的元素,并在输出5个元素以后进行换行,在下一行输出后面的5个元素。运行程序,显示效果如上图。

8.5.4 插入法排序

插入法排序较为复杂,其基本原理是:抽出一个数据,在前面的数据中寻找相应的位置插入,然后继续下一个数据,直到完成排序。
下面以数字9、6、15、4、2为例,使用插入法使其从小到大排序。每次排序后的顺序如表8.5所示。
在这里插入图片描述
可以发现,在第一次排序过程中将第一个数取出来,并放置在第一个位置;然后取出第二个数,交将第二个数与第一个数进行比较,如果第二个数小于第一个数,则将第二个数排在第一个数之前,否则排在第一个数之后;然后取出下一个数,先与排在最后面的数字进行比较,如果当前数字比较大,则继续排在最后,如果当前数字比较小,还要与之前的数字进行比较,如果当前数字比前面的数字小,则将其排在比它小的数字和比它大的数字之间,如果没有比当前数字小的数字,则将当前数字排在最前方:依此类推,不断取出未进行排序的数字,与排好序的数字进行比较,并插入相应的位置,直到将一组数字全部按从小到大实现排序为止。
【例8.12】插入法从小到大排序 声明一个整型数组和两个整型变量,其中整型数组用于存储用户输入的数字,一个整型变量作为两个元素交换时的中间变量,一个用于记录数组元素的位置,然后通过双层循环进行交换法排序,最后将排好序的数组输出。
在这里插入图片描述
在这里插入图片描述
(1)声明一个整型数组,并通过键盘输入为数组元素赋值。
(2)设置一个嵌套循环,第一层循环为后9个数组元素,将第二个元素赋值给中间变量,并记录前一个数组元素的下标位置。在第二层循环中,首先要判断是否符合循环条件,允许循环的条件是记录的下标位置必须大于等于第一个数组元素的下标位置,并且中间变量的值小于之前设置下标位置的数组元素,如果满足循环条件,则将设置下标位置的数组元素赋值给当前的数组元素,然后将记录的数组元素下标位置向前移动一位,继续进行循环判断。内层循环结束以后,将中间变量中保存的数值赋值给当前记录的下标位置之后的数组元素,继续进行外层循环,将数组中下一个数组元素赋值给中间变量,再通过内层循环进行排序,依此类推,直到循环结束,就将数组元素按照从小到大的顺序重新排列了。
(3)循环输出数组中的元素,并在输出5个元素以后进行换行,在下一行输出后面的5个元素。运行程序,显示效果如上图所示。

8.5.5 折半法排序

折半法排序又称为快速排序,其基本原理为:选择一个中间值(在程序中使用数组中间元素的值),然后把比中间值小的元素放在左边,比中间值大的元素放在右边(具体的实现是从两边查找,找到一对后进行交换),然后再对左右两边分别递归使用折半法排序过程。
下面以数字9、6、16、4、2为例,对这几个数字使用折半法从小到大排序。每次排序后的顺序如表8.6所示。
在这里插入图片描述
可以发现,在第一次排序过程中,首先获取数组中间元素的值15,从左右两侧分别取出数组元素与中间值进行比较。如果左侧取出的值比中间值小,则取下一个数组元素与中间值进行比较;如果左侧取出的值比中间值大,则交换两个互相比较的数组元素值。右侧的比较正好与左侧相反,当右侧取出的值比中间值大时,取前一个数组元素的值与中间值进行比较;如果右侧取出的值比中间值小,则交换两个互相比较的数组元素值。当中间值两侧的数据都比较一遍以后,左侧以第一个元素为起点,以中间值的无素为终点,用上面的方法继续进行比较;右侧则以中间值的元素为起点,以数组最后一个元素为终点,用上述的方法继续进行比较。当比较完成以后,继续以折半的方式进行比较,直到将一组数字全部按从小到大的顺序排序为止。

注意:
折半法排序中会用到函数递归调用(即函数调用自身),这部分内容将在第9章中介绍,这里了解即可。请者也可以参考后面的内容提前进行学习。

【例8.13】折半法从小到大排序 在本实例中声明了一个整型数组,用于存储用户输入的数字,再定义一个函数,用于对数组元素进行排序,最后将排序好的数组输出。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
(1)声明一个整型数组,并通过键盘输入为数组元素赋值。
(2)定义折半排序函数CelerityRun,用于对数组元素进行排序,3个参数分别表示递归调用时数组最开始元素的下标、最后元素的下标以及要排序的数组。声明两个整型变量,作为控制排序算法循环的条件,分别将两个参数赋值给变量i和j,i表示左侧下标,j表示右侧下标。首先使用do…whiley语句设计外层循环,条件为i<=j,表示如果两边的下标交错,就停止循环。内层的两个循环分别用来比较中间值两侧的数组元素,当左侧的数值小于中间值时,取下一个元素与中间值进行比较,否则退出第二个内层循环。然后判断i的值是否小于等于j,如果是,则交换以i和j为下标的两个元素值,继续进行外层循环。当外层循环结束以后,以数组第一个元素到以j为下标的元素为参数递归调用该函数,同时,以i为下标的数组元素到数组最后一个参数也作为参数递归调用该函数。依此类推,直到将数组元素按照从小到大的顺序重新排列为止。
(3)循环输出数组中的元素,并在输出5个元素以后进行换行,在下一行输出后面的5个元素。运行程序,显示效果如上图所示。

8.5.6 排序算法的比较

前面介绍了5种排序算法,在具体应用时应该怎么选择呢?下面对5种排序算法的擅长方向进行总结。
1.选择法排序:简单、容易实现,适用于数量较小的排序。排序过程中需要进行n(n-1)/2次比较,互相交换n-1次。
2.冒泡法排序:相对稳定的排序方法,当待排序列有序时,效果比较好。最好的情况是正序,只要比较一次即可;最坏的情况是逆序,需要比较n的2次方。
3.交换法排序:和冒泡法排序类似,正序时最快,逆序时最慢,排列有序数据时效果最好。
4.插入法排序:要经过n-1次插入过程,如果数据恰好位于插入序列的最后端,则不需要移动数据,可节省时间。因此,若原始数据基本有序,具有较快的运算速度。
5.折半法排序:n越大,速度越快。n很小时,比其他排序算法都慢。折半法排序是不稳定的,对应有相同关键字的记录,排序后的结果可能会颠倒次序。
总之,插入法、冒泡法、交换法排序的速度较慢,但当参加排序的序列局部或整体有序时,这种排序能达到较快的速度;在这种情况下,折半法排序反而会显得速度慢了。当n较小,对稳定性不作要求时,宜选用选择法排序;对稳定性有要求时,宜选用插入法或冒泡法排序。
编程训练:
训练7:电视剧收视率排名 利用交换排序法将以下电视剧收视率从高到低排序。
《Give up,hold on to me》 收视率:1.4%
《The private dishes of the husbands》 收视率:1.343%
《My father-in-law will do martiaiarts》 收视率:0.92%
《North Canton still believe in love》 收视率:0.862%
《Impossible task》 收视率:0.553%
《Sparrow》 收视率:0.411%
《East of dream Avenue》 收视率:0.164%
《The prodigal son of the new frontier town》 收视率:0.259%
《Distant distance》 收视率:0.394%
《Music legend》 收视率:0.562%
程序运行结果如下:

1.4000     1.3930    0.9200   0.8620    0.5620
0.5530     0.4110    0.3940   0.2590    0.1640

训练8:公司股票排名 分析10家公司的股票数据,判断哪家更值得投资。在本实例中,声明了一个整型数组和一个整型变量,其中整型数组用于存储用户输入的数字,而整型变量则作为两个元素交换时的中间变量,然后通过双层循环进行冒泡法排序,最后将排序好的数组进行输出。输出结果如下:

各公司股票数据如下:
a[0]=546789
a[1]=543879
a[2]=234780
a[3]=357698
a[4]=345275
a[5]=875422
a[6]=875606
a[7]=567548
a[8]=459078
a[9]=438997
234780     345275        357698      438997      459078
543879     546789		 567548		 875422      875606

8.6 数组应用

本节将运用数组知识,通过3个实例来体验一下实际开发中常会遇到的一些问题。

8.6.1 反转输出字符串

【例8.14】反转输出字符串 以字符串mrsoft为例,其反转的结果为tfosrm。其算法实现过程如图8.15所示。定义两个字符数组,一个表示源字符串,另一个表示反转后的目标字符串。在源字符串中从第一个字符开始遍历,将第一个字符赋给目标字符串的最后一个字符,将第二个字符赋给目标字符串的倒数第二个字符,依次类推,这样就实现了字符串的反转。
在这里插入图片描述
运行程序,字符串反转输出的效果如上图所示。
在这里插入图片描述

8.6.2 输出系统日期和时间

【例8.15】输出系统日期和时间 设计一个程序,当用户输入0时显示帮助信息,输入1时显示系统日期,输入2时显示系统时间,输入3时退出系统。
要实现上述功能,需要解决两个问题:一是要不断地保持程序运行,等待用户输入命令,防止main函数结束;二是要获取系统日期和时间。第一个问题可使用一个无限循环语句来实现,在循环语句中等待用户输入,如果用户输入的是3,则终止循环,结束应用程序。第二个问题需要使用时间函数time和localtime来实现。
在main函数中将各个控制命令保存在数组中,然后使用while语句设计一个无限循环。在循环中让用户输入命令,并判断用户输入的命令是否和数组中存储的命令相同。如果相同,执行相应的语句。
在这里插入图片描述
在这里插入图片描述

8.6.3 字符串的加密和解密

设计应用程序时,为了防止一些敏感信息泄漏,通常需要对这些信息进行加密。以用户登录密码为例,如果密码以明文形式存储在数据表中,很容易被人发现;如果密码以密文形式存储,即使别人从数据表中发现了密码,这也是加密之后的密码,根本不能够使用,因此能提高系统的安全性。
【例8.16】字符串的加密和解密 设计一个加密和解密算法,对一个指定的字符串加密后,再利用解密函数对密文解密,显示明文信息。加密的方式是将字符串中每个字符加上它在字符串中的位置和一个偏移值5。以字符串“mrsoft”为例,第一个字符m在字符串中的位置为0,那么它对应的密文是‘m’+0+5,即r。
在main函数中使用while语句设计一个无限循环,并声明两个字符数组,用来保存明文和密文字符串。在首次循环中,要求用户输入字符串,将明文加密成密文,之后的操作则是根据用户输入的命令字符进行判断:输入1加密新的明文,输入2对之前加密的密文进行解密,输入3退出系统。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行程序,字符串的加密和解密效果如上图所示。
编程训练:
训练9:升序排列字符串 将已按升序排好的字符串a和字符串b按升序归并到字符串c中,并输出。运行结果如下:

请输入排序好的a:
abcegiklnt
请输入排序好的b:
dfmopsyz
输出排序好的c:
abcdefgiklmnopstyz

训练10:竞选班长 班级竞选班长,共有3个候选人。输入参加选举的人数及每个人推举的候选人编号,输出3个候选人最终的得票数及无效选票数。输出结果如下:

请输入参加投票人数:
15
输入每个人推举的候选人(输入123即可)
1 2 3 2 2 3 3 5 6 1 1 2 2 3 1
结果如下:
候选人1:4
候选人2:5
候选人3:4
无效票数:2

8.7 实践与练习

综合练习1:输出游戏角色 “王者荣耀”游戏中有很多英雄,包括法师、战士、坦克、刺客、射手和辅助。用数组输出不同类别的英雄,输出结果如下:

“王者荣耀”游戏角色:
======坦克:============
苏烈,刘邦,钟馗,张飞,牛魔,程咬金,白起,刘禅,庄周,项羽,廉颇,巨灵神,安禄山,猪八戒
=======战士:===========
狂铁,裴擒虎,铠,孙悟空,哪吒,杨戬,橘右京,亚瑟,雅典娜,夏侯惇,关羽,吕布,韩信,老夫子,达摩,典韦,曹操,钟无艳,墨子,赵云,刑天,龙且
=======刺客:===========
百里玄策,庞统,花木兰,阿轲(荆轲),不知火舞,李白,娜可露露,兰陵王,露娜,韩信,宫本武藏,盖聂,红拂
========法师:===========
杨玉环,弈星,女娲,周瑜,鬼谷子,芈月,干将莫邪,东皇太一,大乔,诸葛亮,貂蝉,张良,安琪拉,不知为舞,姜子牙,武则天,王昭君,甄姬,扁鹊,高渐离,赢政,妲已,小乔
=========射手:===========
公孙离,百里守约,后羿,刘备,黄忠,马可波罗,成吉思汗,虞姬,李元芳,艾琳,狄仁杰,鲁班七号,孙尚香
==========辅助:==========
明世隐,梦奇,孙膑,太乙真人,蔡文姬

综合练习2:十二星座速配 巨蟹座和十二星座的爱情匹配分值(星座名/速配值):白羊座/50,;金牛座/90; 双子座/70;巨蟹座/80;狮子座/75; 处女座/89; 天枰座/55; 天蝎座/100;射手座/40;摩羯座/60;水瓶座/45; 双鱼座/99。利用插入排序法,将12个速配值从小到大排序,运行结果如下:

巨蟹座与哪个星座匹配,匹配分数由低到高如下:
40		45		50	    55		60
70		75		80      89      90

综合练习3:统计数字出现的次数 用户输入0-9范围内任意10个数字,统计各数字出现的次数。运行结果如下:

输入成绩:
a[0]=95
a[1]=85
a[2]=90
a[3]=77
a[4]=88
a[5]=100
a[6]=96
a[7]=93
a[8]=80
a[9]=79
a[10]=89
a[11]=92
成绩排名如下:
100		96		95	    93        92
90		89		88		85		  80	 79  	77

综合练习5:平安夜卖苹果 平安夜,父亲推出一车苹果(共2520个),分给6个儿子卖。父亲先按事先写在纸上的数字把苹果分完,每个人拿到的苹果数量都不同。然后他说:“老大,把你的苹果分1/8给老二。老二连同原来的苹果,分1/7给老三。老三连同原来的苹果,分1/6给老四。依此类推,最后老六拿到后,连同原来的苹果分1/3给老大。这样,你们每个人手中的苹果就一样多了”。那么,兄弟6个人原来各有多少苹果呢?编写程序计算一下。输出结果如下:

x[1]=240
x[2]=460
x[3]=434
x[4]=441
x[5]=455
x[6]=490

综合练习6:谁被@了 QQ群或微信群里,要是找某个人有急事,就会@他。编写程序,输出被@的人员列表。输出结果如下:

被@的列表:
明日科技		你被@了
扎克伯格		你被@了
比尔盖茨		你被@了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

manyoftenvictory

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值