第 5 章 数 组 在第4章编写了一个10个数中找最大数的程序,因为每个数据只需要使用 一次,因此只使用一个变量来存储输入数据,从而利用循环完成了任务。现在 稍微调整一下需求,要求输出所有超过平均数的数据,这时每个数据将不止使 用一次(一次用于计算平均数,一次与平均数进行比较),此时将使用数组这一 数据存储形式存储输入的数据,然后再利用循环结构解决这一新的问题。 .. 5.1 一维数组 数组是存放在连续内存单元中的一组数据类型相同的元素的有限集合。 数组中存放的所有数据的数据类型相同,数组中的每个数据被称为一个数组元 素。通过数组名和数组下标来使用数组中的数据,数组下标代表数据在数组中 的位置,数组下标从0开始。 1.一维数组的声明 一维数组有两种声明方式,格式如下: 类型标识符 数组名[ ]; 或 类型标识符[ ] 数组名; 数组元素的数据类型可以是Java语言中的任何数据类型,如前面学过的基 本数据类型(int、float、double、char、boolean等),或者将来学习的类(class)或接 口(interface)类型等。数组名是符合Java标识符定义规则的用户自定义标 识符。例 如“int a[];”或“int[] a;”都表示声明一个一维数组a,该数组存放 int类型的数据;而“double[]array1,array2;”表示声明两个一维数组array1 和array2,这两个数组存放double类型的数据。 数组定义后,系统将给数组名分配一个内存单元,其值为数组在内存中的 实际存放地址。由于在数组变量定义时,数组元素本身在内存中的实际存放地 76 Java语言程序设计基础(微课版) 址还没有分配,所以,此时该数组名的值为空(null)。 2.一维数组的初始化 因为在数组声明中并未明确指出数组元素的个数,系统无法知道需要给这个数组分 配多大的内存空间。在使用数组元素之前,必须要对其进行初始化操作,使系统知道数组 元素的个数,并为其分配连续的存储空间。初始化数组后,数组就可以使用了,在Java语 言中用new关键字来完成数组的初始化操作,也可以在声明数组时直接指定数组元素的 初值。 1)用new关键字初始化数组 用new关键字初始化数组,只为数组分配存储空间而不对数组元素赋初值。用new 关键字来初始化数组有两种方式。 (1)先声明数组,再初始化数组。这实际上由两条语句构成,格式如下: 类型标识符 数组名[ ]; 数组名=new 类型标识符[数组长度]; 其中,第一条语句是数组的声明语句,第二条语句是初始化语句。应该注意的是,两条语 句中的数组名、类型标识符必须一致。数组长度是整型常量或整型变量,用以指明数组元 素的个数。例如: int a[ ]; a=new int[10]; 第一句是声明一个整型数组变量a,第二句是初始化该数组为存放10个数组元素的 整型数组。这种初始化方式最为常用。 (2)在声明数组的同时使用new关键字初始化数组。这种初始化实际上是将上面的 两条语句合并为一条语句。 例如: int[ ] a=new int[10]; Java语言规定,在数组分配内存单元后,系统将自动给每个数组元素赋值,其中数值 类型数组元素的初值为0,逻辑类型数组元素的初值为false。 2)直接指定数组元素初值的方式 用直接指定数组元素初值的方式对数组初始化,是指在声明一个数组的同时将数组 元素的初值依次写入赋值运算符后的一对花括号内,给这个数组的所有数组元素赋上初 始值。这样,Java编译器可通过初值的个数确定数组元素的个数,为其分配存储空间并 将这些值写入相应的存储单元中。例如: int[ ] a={19,23,29,31,37}; 这条语句声明类型为int的数组a,数组元素共有5个,初始值分别为a[0]=19, a[1]=23,a[2]=29,a[3]=31,a[4]=37。Java中的数组下标从0开始。 第5章 数组 77 3.一维数组的使用 当数组初始化后就可通过数组名与数组下标来访问数组中的每个元素。一维数组元 素的引用格式如下: 数组名[数组下标] 其中,数组名是经过声明和初始化的标识符,数组下标是指元素在数组中的位置,数组下 标的取值范围是0~(数组长度-1)。下标值可以是整数型常量或整数型变量表达式。 例如,在有了“int[]a=newint[10];”语句后,下面的两条赋值语句是合法的: a[3]=25; a[3+6]=50; 但赋值语句“a[10]=8;”是错误的,这是因为数组a在初始化时确定其长度为10,第10个 元素的下标是数字9,不存在下标为10的数组元素a[10]。 例5.1 输入10个学生的某科成绩,要求输出高于平均分的成绩。 解题思路:显然程序需要两次使用学生的成绩,第一次计算平均成绩,第二次查看所 有学生成绩,然后输出高于平均分的成绩。程序代码如下: import java.util.*; public class Array_01 { public static void main(String[] args) { int[] a; //平均成绩含有一位小数,用实数类型 double ave=0; a=new int[10]; Scanner reader=new Scanner(System.in); //利用循环输入10 个学生的成绩,注意下标为0~9 for(int i=0;i<10;i++) a[i]=reader.nextInt(); //先求成绩总和,再计算平均成绩,可减少除法次数 for(int i=0;i<10;i++) ave=ave+a[i]; ave=ave/10; System.out.println("平均值是"+ave); //检查数组中每个元素,输出符合要求的元素 for(int i=0;i<10;i++) if(a[i]> ave) System.out.println(a[i]); } } 因为本程序数组中的数据是10个,所以在3个与数组操作有关的循环中(输入数据、 计算和、输出数据)的循环判断表达式都是i<10。在Java语言中,数组经初始化后就确 78 Java语言程序设计基础(微课版) 定了它的长度,对于每个已分配了存储空间的数组,Java语言用一个数据成员length来 存储这个数组的长度值,调用方式为“数组名.length”。可以将表达式i<10改写为i< a.length,这样不仅提高了程序的可读性,在修改程序时(例如,学生人数变为30)也更加 方便了。 .. 5.2 一维数组应用 数组用于存放同种数据类型的一批数据,在对这些数据进行处理时,对于每个数据的 处理方式往往相同,正好使用循环结构对数组元素进行操作。 例5.2 数组中存放若干数据,输入一个数据,判断此数据是否在数组中,如果在输出 其所在位置,否则输出不在的提示信息。 解题思路:利用循环将数组中存放的每个元素都与要查找的数据进行比较,如果相 同则输出当前数组元素的下标,如果数组中所有的元素都与要查找的数据不同,则输出没 找到的提示信息。 import java.util.*; public class ArraySearch { public static void main(String[] args) { int[] a={1,3,5,7,9,2,4,6,8,10}; int i,x; boolean find=false; //是否找到的标记,false 表示没找到 Scanner reader=new Scanner(System.in); System.out.println("请输入要查找的数:"); x=reader.nextInt(); for(i=0;i a[i][j]){ min=a[i][j];minI=i; } } //交换最大值和最小值所在行的元素 for(j=0;j<4;j++){ t=a[maxI][j];a[maxI][j]=a[minI][j];a[minI][j]=t; } //按行输出数组数据 for(i=0;i<3;i++){ 84 Java语言程序设计基础(微课版) for(j=0;j<4;j++) System.out.print(a[i][j]+"\t"); System.out.println(); } } } 在程序中,在查找最大值和最小值的双重循环之前有语句“max=min=a[0][0];”, 如果不写这一条语句,且a[0][0]恰好是最大值或最小值之一,那么程序逻辑上有错误。 .. 5.5 查找和排序 查找和排序是在程序设计中经常使用的两种技术,下面通过学习一种快速的查找算 法———二分查找,以及两种简单直观的排序算法———选择排序和冒泡排序,来了解简单的 查找和排序程序的设计。 1.二分查找算法 本书之前编写的在数据序列中查找数据的程序,采用的是从头到尾的顺序查找方式, 顺序查找的效率比较低,适合数据量比较小的情况。如果有n个数据,使用顺序查找最多 需要判断n次,才能给出数据在或不在数据序列中。如果数据量比较大,如像图书馆中的 书籍信息,这时管理人员通常会采取一定方式保证数据是有序的,如按从小到大的顺序进 行排序,这样就可以使用一些效率更高的方法进行数据的查找了。二分查找算法就是最 常用的一种快速查找算法。 如图5.1(a)所示,要查找的数据是由小到大有序的,现要查找数据x是否在这批数据 中;已知待查找数据的起始位置s(start)、结束位置e(end),可计算得到二分位置m(mid, 中间位置,m=(s+e)/2),如图5.1(b)所示;将x与m 位置上的数据进行比较,不妨假定 x比m 位置上的数大,则如果数据x在该序列中,一定在m 位置之后的部分中(因为数据 是有序的,m 位置之前的数都比m 位置上的数小),如图5.1(c)所示;现在e不变,新的s =m+1,根据新的s可计算新的m 位置,如图5.1(d)所示;这次假定x比m 位置上的数 小,则如果数据x在该序列中,一定在m 位置之前的部分中,如图5.1(e)所示;此时s不 变,新的e=m-1,根据新的e可计算新的m 位置,如图中f所示;这样一直查找下去,直 到找到数据或确定x不在数据序列中。 二分查找的结束条件: (1)找到了,此时m 位置上的数就是x; (2)没找到,新的待查找序列中已经没有数据了,判定条件为s>e,因为事先约定s 是开始位置、e是结束位置,开始位置不应在结束位置之后。 例5.6 利用二分查找算法在升序数组中(为输入数据方便,假定为10个元素)查找 数据。根 据上述思想编写的二分查找程序代码如下: