week 5 数组
5.1 数组
初试数组
用我们以前学过的知识来设想一个问题:如何写一个程序计算用户输入的数字的平均数?
在不需要记录输入的每一个数的前提下,我们很容易就可以得到下面的类似代码:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int x = 0;
double sum = 0.0;
int cnt = 0;
x = in.nextInt();
while (x != -1) {
sum += x;
cnt++;
x = in.nextInt();
}
if( cnt > 0) {
System.out.println(sum/cnt);
}
}
代码5-1
(cnt是count的缩写,常用来做计数器;-1也是我们常用的标志。)
但是这是在不需要记录输入的每一个的数的情况下,如果我们有更多的要求呢?
- 比如如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?
- 必须先记录每一个输入的数字,计算平均数以后,再检查记录下来的每一个数字,与平均数做比较,决定是否输出。
那么?如果记录很多数呢?
- 采用变量命名,如int num1,num 2,num3…当然是可以的,但是肯定有更高效率的做法,这就是我们接下来要说的数组。
对代码5-1稍作修改可以得到代码5-2(此代码存在安全隐患,具体情况会在后说明)
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int x = 0;
double sum = 0.0;
int cnt = 0;
int[] numbers = new int[100]; //定义数组
x = in.nextInt();
while(x != -1) {
numbers[cnt] = x;
sum += x;
cnt++;
x = in.nextInt();
}
if(cnt > 0) {
double average = sum/cnt;
for(int i = 0; i < cnt ; i++ ) { //遍历数组
if(numbers[i] > average) {
System.out.println(numbers[i]);
}
}
}
}
**代码 5-2**
创建数组
数组: 是一种 容器(放东西的东西),特点是:
- 其中所有的元素都具有相同的数据类型;;
- 一旦创建,不能改变大小。
数组中的每个数据叫做元素。
定义数组变量:
类型 [] 名字 = new 类型 [元素个数]
e.g.: int[] grades = new int[100];
double[] averages = new double [20];
几个要注意的点:
- 元素个数必须是整数;
- 元素个数必须给出;
- 元素个数可以是变量。
数组的元素
对于代码 int[] a = new int[10];
,创建了一个int型的数组,数组里有10个元素:a[0],a[1],a[2],a[3]…a[9],每个元素是一个int的变量,可以像使用普通变量一样对其进行基础操作。
对于数组的元素:
- 每个元素都是它定义的数据类型的变量
- 索引或者下标都是从0开始的
有效的下标:
- 最小的下标是0,最大的下标是 数组的元素个数-1;
- 可是编译器不会检查你是否用了有效的下标;
- 但是如果运行的时候出现了无效的下标,可能会导致程序终止。
代码5-2存在安全隐患,因为我们并不知道具体会输入多少数据,输入的数据可能超过100个。对于此种情况,又该如何解决呢?
联系到上面所说,元素的个数可以是变量,我们不妨用变量代替具体的数字100,先让用户输入一个数cnt来表示需要多少个数据,再输入具体的数据,可以得到如下代码:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
double sum = 0;
int cnt = in.nextInt();
if( cnt > 0) {
int[] numbers = new int [cnt];
for(int i =0; i<cnt ; i++ ) {
numbers[i] = in.nextInt();
sum += numbers[i];
}
double average = sum/cnt;
for(int i = 0; i < cnt; i++) {
if(numbers[i] > average) {
System.out.println(numbers[i]);
}
}
}
}
**length:**每个数组都有一个内部成员length,会告诉你它的元素的数量。
比如对于代码,for(i=0;i<100;++i)
这种形式,最好改写成for(i=0;i<grade.length;++i)
的形式,这有利于代码的可扩展性。
**代码练习:**投票统计:写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束。
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int x = in.nextInt();
int[] numbers = new int[10];
while(x != -1) {
if(x >= 0 && x <= 9) {
numbers[x]++;
}
x = in.nextInt();
}
for(int i = 0; i < numbers.length; i++) {
System.out.println(i+":"+numbers[i]);
}
}
TIPS:没有给数组赋值时,数组里面的元素自动赋0,number[x]++时是说number数据的第x个元素+1,x代表输入的数,并不是每个数字的的计数,number[x]才是值为x的计数。
5.2数组计算
数组变量:
直接初始化数组:
- new创建的数组会得到默认的0值
- 可以直接用大括号给i出数组的所有元素的初始值
- 不需要给出数组的大小,编译器替你数数
- 如何得知数组的大小? length。
数组变量:
- 数组变量时数组的管理者,而非数组本身;
- 数组必须创建出来然后交给数组变量来管理;
- 数组变量之间的赋值是管理权限的赋予;
- 数组变量之间的比较是判断是否管理同一个数组。
复制数组:
- 必须遍历源数组,将每个元素逐一拷贝给目的数组。
遍历数组:
在一组给定的数据中,如何找出某个数据是否存在?
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] data = {2,3,5,7,4,9,11,34,12,28};
int x = in.nextInt();
int loc = -1;
for(int i = 0; i < data.length; i++) {
if(x == data[i]) {
loc = i;
break;
}
}
if( loc > -1) {
System.out.println(x+"是第"+(loc+1)+"个");
}
else {
System.out.println(x+"不在其中");
}
}
tips:此种搜索方法被称作线性搜索。
代码 5-3
注意代码5-3中的第7~11行。
- 遍历数组时,通常都是使用for循环,让循环变量i从0到<数组的length,这样循环体内最大的i正好是数组最大的有效下标。
- 常见错误是:
- 循环结束的条件是 <= 数组长度;
- 离开循环后,继续用i的值来做数组元素的下标。
接下来另一种语句,for-each语句。
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] data = {2,3,5,7,4,9,11,34,12,28};
int x = in.nextInt();
boolean found = false;
for(int k:data) {
if(x == k) {
found = true;
break;
}
}
if(found) {
System.out.println(x+"在其中");
}
else {
System.out.println(x+"不在其中");
}
}
for-each:
for(类型 变量:数组){
…
}
for-each适合做数组的遍历。写法是对于数组中的每一个元素取出来作为这个类型的一个变量,所以中间用的是冒号,读的时候我们要倒过来,对于数组中的每一个元素,取出来作为那一个元素的变量。
数组的例子:素数
**从2到x-1测试是否可以整除:**参考代码4-1。
对于n,要循环n-遍;当n很大的是偶,就可以被看作是n遍。
去掉偶数之后,从3到x-1,每次加2:
核心代码:
if(x ==1 || x%2 ==0 && x!=2){
isPrime = false;
}
else{
for(int i = 3; i < x; i+=2){
if(x %i ==0){
isPrime == false;
break;
}
}
}
如果x是偶数,立刻判断为不是素数。否则要循环(n-3)/2+1遍,当n非常大的就是n/2遍。
无须到1,到sqrt(x)就够了:
for(int i = 3; i<Math.sqtr(x);i+=2){
if(x % i == 0){
isPrime = false;
break;
}
}
具体原因参见数学证明。只需要循环sqtr(x)遍。
以上,从 n→ n/2 → sqrt(n),这是人类思维较容易理解思考到的方法。
以下提供另一种思维模式:
构造素数表:
欲构造n以内的素数表:
- 令x为2;
- 将2x、3x、4x直到ax<n的数标记为非素数
- 令x为下一个没有被标记为非素数的数,重复2;直到所有的数已经尝试完毕
欲构造n以内(不含)的素数表:
- 创建prime为boolean[n],初始化其所有元素为true,prime[x]为true表示x是素数。
- 令x=2
- 如果x是素数,则对于(i=2;x*i<n;i++)令prime[i * x]=false。
- 令x++,如果x<n,重复3,否则结束。
核心代码:
boolean[] isPrime = new boolean[100];
for(int i =2; i <isPrime.length; i++){
isPrime[i] = true;
}
for(int i =2; i<isPrime.length; i++){
if(isPrime[i]){
for(int k=2;i*k<isPrime.length; k++){
isPrime[i*k] = false;
}
}
}
for(int i =0; i < isPrime.length;i++){
if(isPrime[i]){
System.out.println(i+" ");
}
}
System.out.println();
算法不一定和人的思考方式相同。
5.3 二维数组
如 int[][]a =new int[3][5]
;,通常理解为a是一个3行5列的矩阵。如图所示:
二维数组的遍历:
for(i=0;i<3;i++){
for(j=0;j<5;j++){
a[i][j] = i*j;
}
}
-
a[i] [j]是一个int
-
表示第i行第j列上的单元
-
a[i,j]这种并不存在
二维数组的初始化:
int[][] a={
{1,2,3,4};
{1,2,3};
}
- 编译器来数数
- 每行一个{},用逗号分隔,最后的都好可以存在(有古老的传统)
- 如果省略,表示补0。
tic-tac-toe游戏
读入一个3x3的矩阵,矩阵中的数字1表示该位置上有一个x,为0表示为o。程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符x或o,或输出无人获胜。
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//int[] number = new int[10];
//int a;
int i,j;
final int SIZE = 3;//the size of SIZE is three
//tic-tac-toe
int[][] board = new int[SIZE][SIZE];
System.out.println("Gameplayer_1 input 1 and Gameplayer_2 input 0");
for(i=0;i<board.length;i++)//Input the element,1 representative x,0 representative o;
{
for(j=0;j<board[i].length;j++)
{
board[i][j] = in.nextInt();
}
}
boolean GotResult = false;
//Jude row column Diagonal diagonal line
int cnt = 0;
int num1 = 0;
int num2 = 0;
//Judge row and column
for(i=0;i<board.length;i++)
{
num1 = 0;
num2 = 0;
for(j=0;j<board[i].length;j++)//row
{
if(board[i][j]==1) {
num1++;
}
}
for(j=0;j<board[i].length;j++)//column
{
if(board[j][i]==1) {
num2++;
}
}
if(num1 == SIZE || num2 == SIZE)
{
System.out.println("Gameplayer_1 win!");
GotResult = true;
break;
}
if(num2 == 0 || num1 == 0)
{
System.out.println("Gameplayer_2 win!");
GotResult = true;
break;
}
}
if(GotResult == false){
num1 = 0;
num2 = 0;
//Judge diagonal diagonal line
for(i=0;i<board.length;i++)
{
if(board[i][i] == 1) {
num1++;
}
if(board[i][2-i] == 1) {
num2++;
}
}
if(num1==3 || num2 ==3) {
System.out.println("Gameplayer_1 win!");
// GotResult = true;
}
else if(num2 == 0 || num1 == 0) {
System.out.println("Gameplayer_2 win!");
// GotResult = true;
}
else {
System.out.println("Draw!");
}
}
}