前言
继续跟着我导师写 java 代码,下面是第六天到第十天。
此处附上我导的链接:日撸 Java 三百行(01-10天,基本语法)
6. day 6:基本 for 语句
for 语句是一种循环结构,其形式一般如下:
/*
① 初始条件
② 循环条件(为 boolean 类型)
③ 循环体
④ 迭代条件
*/
for (①; ②; ④){
③;
}
在上述结构中,程序执行的过程如下:
① → ② → ③ → ④ → ② → ③ → ④ → … → ②
抄写老师的代码:
public class ForStatement {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
forStatementTest();
}// Of main
/**
*********************
* Method unit test.
*********************
*/
public static void forStatementTest() {
int tempN = 10;
System.out.println("1 add to " + tempN + " is: " + addToN(tempN));
tempN = 0; // 从1加到0,不满足for循环中的条件,故不会执行语句,直接跳出循环
System.out.println("1 add to " + tempN + " is: " + addToN(tempN));
int tempStepLength = 1;
tempN = 10;
System.out.println("1 add to " + tempN + " with step length " + tempStepLength + " is: " + addToNWithStepLength(tempN, tempStepLength));
tempStepLength = 2;//将步长设置为2,实现了将1-10中的奇数相加
System.out.println("1 add to " + tempN + " with step length " + tempStepLength + " is: " + addToNWithStepLength(tempN, tempStepLength));
}// Of forStatementTest
/**
*********************
* Add from 1 to N.
*
* @param paraN The given upper bound.
* @return The sum.
*********************
*/
public static int addToN(int paraN) {
int resultSum = 0;
for (int i = 1; i<= paraN; i++) {
resultSum += i;
}// Of for i
return resultSum;
}// Of addToN
/**
*********************
* Add from 1 to N with a step length.
*
* @param paraN The given upper bound.
* @param paraStepLength The given step length.
* @return The sum.
*********************
*/
public static int addToNWithStepLength(int paraN, int paraStepLength) {
int resultSum = 0;
for (int i=1; i <= paraN; i += paraStepLength) {
resultSum += i;
} // Of for i
return resultSum;
}// Of addToNWithStepLength
} // Of class ForStatement
上面的循环实际上是比较简单的循环,在处理复杂问题时,会面临嵌套循环。嵌套循环的使用虽然在第七天和第八天使用矩阵的时候自然而然就用到了,但此处还是想多写一点东西。
嵌套循环
可以看成是外层循环和内层循环,内层循环结构遍历一遍,只相当于外层循环循环体执行了一次。而提及循环,就涉及到了时间复杂度的问题。比如,使用2层循环时,若外层循环需要执行m次,内层循环需要执行n次,则内层一共执行了 n*m 次。在解决实际问题时(若使用2层循环),就相当于外层循环控制行数,内层循环控制列数,这在写矩阵部分的内容时就更加清楚了。
一般在初识嵌套循环时,最喜欢的就是让初学者输出各种各样的图形(但当然不是用print一行一行输出),比如输出一个菱形!
public class DiamondPrint {
public static void main(String[] args) {
/* *部分即是菱形的形状
####*
###* *
##* * *
#* * * *
* * * * *
#* * * *
##* * *
###* *
####*
*/
// 上部分
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 4 - i; j++) {//需要输出每行前面的空格[#的地方]
System.out.print(" ");
}// Of for j
for (int k = 0; k <= i; k++) {
System.out.print("* ");
}// Of for j
System.out.println();//一行输出后换行
}//Of for i
//下部分
for (int i = 0; i < 4; i++) {
for (int j = 0; j <= i; j++) {
System.out.print(" ");
}// Of for j
for (int k = 0; k < 4 - i; k++) {
System.out.print("* ");
}// Of for k
System.out.println();
}// Of for i
}// Of main
}// Of class DiamondPrint
7. day 7:矩阵元素相加
矩阵相加,就涉及了数组的使用以及双重循环。
数组(Array),由多个类型相同且按一定顺序排列(有序)的数据构成的集合。
一维数组
- 声明及初始化
int[] studentIds;//声明,int表明数组中元素的数据类型
//1.数组的静态初始化:初始化和赋值操作同时进行
studentIds = new int[]{2001, 2002, 2003, 2004}; //数组元素赋值
//2.数组的动态初始化:初始化和赋值操作不同时进行
String[] studentNames = new String[4];
studentNames[0] = "Tom";
studentNames[1] = "Jerry";
studentNames[2] = "Lucy";
studentNames[3] = "Lily";
不管是静态还是动态,需要明确的是初始化一旦完成,数组的长度就确定了。因为这样内存才知道要开辟多大的空间。
- 数组的元素
获取数组某个位置的元素是通过调用其角标(也叫下标、索引),且下标是从0开始的。 - 数组的长度
System.out.println(studentNames.length);// 输出数组的长度
- 遍历数组
for (int i = 0; i < studentIds.length; i++) {
System.out.println(studentIds[i]);
}// Of for i
二维数组
- 声明及初始化
//静态初始化
int[][] staticArray = new int[][] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
//动态初始化
String[][] dynamicArray = new String[2][3];
- 数组的长度
System.out.println(staticArray.length);
System.out.println(staticArray[0].length);
- 遍历数组
for (int i = 0; i < staticArray.length; i++) {
for (int j = 0; j < staticArray[i].length; j++) {
System.out.println(staticArray[i][j]);
}// Of for j
}// Of for i
抄写老师的代码:
这里的矩阵实际上就是二维数组的使用,可以看成矩阵的行和列. 以下代码主要实现矩阵的初始化、赋值、矩阵中所有元素的求和、两个矩阵相加。
import java.util.Arrays;
public class MatirxAddition {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
matrixElementSumTest();
matirxAdditionTest();
}// Of main
/**
*********************
* Sum the elements of a matrix.
*
* @param paraMatrix The given matrix.
* @return The sum of all its elements;
*********************
*/
public static int matrixElementSum(int[][] paraMatrix) {
int resultSum = 0;
//遍历整个数组,对矩阵中的所有元素求和
for (int i = 0; i < paraMatrix.length; i++) {//可以看作是遍历所有行,且不能超过其长度
for (int j = 0; j < paraMatrix[0].length; j++) {//可以看作是遍历所有列
resultSum += paraMatrix[i][j];
}// Of for j
}// Of for i
return resultSum;
}// Of matrixElementSum
/**
*********************
* Unit test for respective method.
*********************
*/
public static void matrixElementSumTest() {
int[][] tempMatrix = new int[3][4];//初始化,可看成是初始化了一个3行4列的二维数组
for (int i = 0; i < tempMatrix.length; i++) {
for (int j = 0; j < tempMatrix[0].length; j++) {
tempMatrix[i][j] = i * 10 + j;//赋值操作
}// Of for j
}// Of for i
System.out.println("The matrix is: \r\n" + Arrays.deepToString(tempMatrix));//打印矩阵本身
System.out.println("The matrix element sum is: " + matrixElementSum(tempMatrix) + "\r\n");//打印矩阵中所有元素求和的结果
}// Of matrixElementSumTest
/**
*********************
* Add two matrices. Attention: NO error check is provided at this moment.
*
* @param paraMatrix1 The first matrix.
* @param paraMatrix2 The second matrix. It should have the same size as the first one's.
* @return The addition of these matrices.
*********************
*/
public static int[][] matirxAddition(int[][] paraMatrix1, int[][] paraMatrix2) {
int[][] resultMatrix = new int[paraMatrix1.length][paraMatrix1[0].length];//保存两个矩阵相加的结果,该结果矩阵的size跟那两个矩阵的size是相同的
for (int i = 0; i < paraMatrix1.length; i++) {
for (int j = 0; j < paraMatrix1[0].length; j++) {
resultMatrix[i][j] = paraMatrix1[i][j] + paraMatrix2[i][j];//对应的行和列相加
}// Of for j
}// Of for i
return resultMatrix;
}// Of matirxAddition
/**
*********************
* Unit test for respective method.
*********************
*/
public static void matirxAdditionTest() {
int[][] tempMatrix = new int [3][4];//初始化
for (int i = 0; i < tempMatrix.length; i++) {
for (int j = 0; j < tempMatrix[0].length; j++) {
tempMatrix[i][j] = i * 10 + j;//赋值操作
}// Of for j
}// Of for i
System.out.println("The matrix is: \r\n" + Arrays.deepToString(tempMatrix));//打印初始矩阵
int[][] tempNewMatrix = matirxAddition(tempMatrix, tempMatrix);
System.out.println("The new matrix is: \r\n" + Arrays.deepToString(tempNewMatrix));//打印两个矩阵相加后的结果矩阵
}// Of matirxAdditionTest
}// Of class MatirxAddition
结果:
The matrix is:
[[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23]]
The matrix element sum is: 138
The matrix is:
[[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23]]
The new matrix is:
[[0, 2, 4, 6], [20, 22, 24, 26], [40, 42, 44, 46]]
关于 Arrays.deepToString
- 操作数组的工具类:
java.util.Arrays
deepToString()
方法:
官方解释:返回指定数组的“深层内容”的字符串表示形式。如果数组包含其他数组作为元素,则字符串表示包含它们的内容。 此方法旨在将多维数组转换为字符串。
通俗理解就是这种方法能够遍历数组,在打印数组的时候调用就很方便,不用自己写 for 循环。
8. day 8:矩阵相乘
两个矩阵相乘是有前提条件的,比如第一个矩阵是
m
×
n
m\times n
m×n,那么第二个矩阵必须是
n
×
p
n\times p
n×p,才能进行矩阵的乘法运算,且其结果矩阵是
m
×
p
m\times p
m×p.
因此,需要先判断给定的两个矩阵能否相乘,再进行后边的运算。
import java.util.Arrays;
public class MatrixMultiplication {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
matrixMultiplicationTest();
}// Of main
/**
*********************
* Matrix multiplication. The columns of the first matrix should be equal to the
* rows of the second one.
*
* @param paraFirstMatrix The first matrix.
* @param paraSecondMatrix The second matrix.
*********************
*/
public static int[][] multiplication(int[][] paraFirstMatrix, int[][] paraSecondMatrix) {
int m = paraFirstMatrix.length;//第一个矩阵的行数
int n = paraFirstMatrix[0].length;//第一个矩阵的列数
int p = paraSecondMatrix[0].length;//第二个矩阵的列数
// Step 1. Dimension check.
if (paraSecondMatrix.length != n) {//第一个矩阵的列与第二个矩阵的行不等时,就不能进行矩阵的乘法运算
System.out.println("The two matrices cannot be multiplied.");
return null;//不能乘返回null
}// Of if
// Step 2. The loop.
int[][] resultMatrix = new int[m][p];//结果矩阵是m行p列
for (int i = 0; i < m; i++) {
for (int j = 0; j < p; j++) {
for (int k = 0; k < n; k++) {
resultMatrix[i][j] += paraFirstMatrix[i][k] * paraSecondMatrix[k][j];
}// Of for k
}// Of for j
}// Of for i
return resultMatrix;
}// Of multiplication
/**
*********************
* Unit test for respective method.
*********************
*/
public static void matrixMultiplicationTest() {
int[][] tempFirstMatrix = new int[2][3];//初始化一个2×3的矩阵
for (int i = 0; i < tempFirstMatrix.length; i++) {
for (int j = 0; j < tempFirstMatrix[0].length; j++) {
tempFirstMatrix[i][j] = i + j;//赋值
}// Of for j
}// Of for i
System.out.println("The first matrix is: \r\n" + Arrays.deepToString(tempFirstMatrix));
int[][] tempSecondMatrix = new int[3][2];//初始化一个3×2的矩阵
for (int i = 0; i < tempSecondMatrix.length; i++) {
for (int j = 0; j < tempSecondMatrix[0].length; j++) {
tempSecondMatrix[i][j] = i * 10 + j;//赋值
}// Of for j
}// Of for i
System.out.println("The second matrix is: \r\n" + Arrays.deepToString(tempSecondMatrix));
int[][] tempThirdMatrix = multiplication(tempFirstMatrix, tempSecondMatrix);
System.out.println("The third matrix is: \r\n" + Arrays.deepToString(tempThirdMatrix));
System.out.println("Trying to multiply the first matrix with itself.\r\n");
tempThirdMatrix = multiplication(tempFirstMatrix, tempFirstMatrix);//不合法的相乘
System.out.println("The result matrix is: \r\n" + Arrays.deepToString(tempThirdMatrix));
}// Of matrixMultiplicationTest
}// Of class MatrixMultiplication
9. day 9:while 语句
while 语句是另一种循环结构,一般有如下形式:
/*
① 初始条件
② 循环条件(为 boolean 类型)
③ 循环体
④ 迭代条件
*/
①
while (②){
③;
④;
}
在上述结构中,程序执行的过程如下:
① → ② → ③ → ④ → ② → ③ → ④ → … → ②
跟写老师的代码:
package com.teachercode;
public class WhileStatement {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
whileStatementTest();
}// Of main
/**
*********************
* The sum not exceeding a given value.
*********************
*/
public static void whileStatementTest() {
int tempMax = 5;
int tempValue = 0;
int tempSum = 0;
// Approach 1
while (tempSum <= tempMax) {
tempValue++;
tempSum += tempValue;
System.out.println("tempValue = " + tempValue + ", tempSum = " + tempSum);//输出每次循环对应的tempValue和tempSum的值
}// Of while
tempSum -= tempValue;//tempSum的值一旦大于tempMax就退出循环,故此时tempSum减去最后那一个tempValue才是不超过tempMax
System.out.println("The sum not exceeding " + tempMax + " is: " + tempSum);
// Approach 2
System.out.println("\r\nAlternative approach.");
tempValue = 0;
tempSum = 0;
while (true) {// 无限循环的格式,不确定循环多少次时使用
tempValue++;
tempSum += tempValue;
System.out.println("tempValue = " + tempValue + ", tempSum = " + tempSum);
if (tempMax < tempSum) {
break;//一旦tempSum的值大于tempMax,执行到break语句,就跳出循环
}// Of if
}// Of while
tempSum -= tempValue;
System.out.println("The sum not exceeding " + tempMax + " is: " + tempSum);
}// Of whileStatementTest
}// Of class WhileStatement
注意:
- 在使用 while 循环的时候,循环的迭代条件漏写可能导致程序陷入死循环;
- for 和 while 这2种循环可以相互转换
- 关于
while (true)
:
可以看作“无限循环”的形式,在 for 循环中就是for(;;)
。这种形式在不明确循环次数的时候使用,一般通过循环内部的条件,来控制循环的结束。比如上面代码中,在不满足tempMax >= tempSum
这个条件时,就通过break
跳出循环; - 对于循环结构,结束循环的方式不唯一:
① 循环条件为 false 时;
② 在循环体中,执行到 break.
10. day 10:综合任务1
学生的成绩存放于一个矩阵,其中行表示学生,列表示科目。如:第 0 行表示第 0 个学生的数学、语文、英语成绩。要求:
进行学生成绩的随机生成, 区间为 [50, 100].
找出成绩最好、最差的同学。但有挂科的同学不参加评比.
10.1 实际代码中,for 和 if 是最常见的, switch 和 while 使用少得多.
10.2 使用了 continue, 它是指继续跳过本次循环后面的代码,直接进入下一次循环. 而 break 是跳出整个循环体.
10.3 为了随机数,迫不得已提前使用了 new 语句生成对象.
10.4 通过数据测试找出程序中的 bug.
import java.util.Arrays;
import java.util.Random;
public class Task1 {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
task1();
}// Of main
/**
*********************
* Method unit test.
*********************
*/
public static void task1() {
// Step 1. Generate the data with n students and m courses.
// Set these values.
int n = 10; // 学生人数
int m = 3; // 课程数
// 确定学生分数下限和上限分别为50、100
int lowerBound = 50;
int upperBound = 65; // Should be 100. I use this value for testing.
int threshold = 60; // 及格的标准
// Here we have to use an object to generate random numbers.
Random tempRandom = new Random();
int[][] data = new int[n][m]; // 初始化n×m的二维数组存储每个学生的各科成绩信息
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
data[i][j] = lowerBound + tempRandom.nextInt(upperBound - lowerBound);
}// Of for j
}// Of for i
System.out.println("The data is:\r\n" + Arrays.deepToString(data)); // 打印出随机生成的各个学生各科分数的矩阵
// Step 2. Compute the total score of each student.
int[] totalScores = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (data[i][j] < threshold) {
totalScores[i] = 0; // 如果某个人的分数小于60,即挂科,不参与评比,记为0
break; // 跳出循环(一旦有一门小于60,就直接跳出循环,挂科不参与评比)
}// Of if
totalScores[i] += data[i][j];
}// Of for j
}// Of for i
System.out.println("The total scores are:\r\n" + Arrays.toString(totalScores));
// Step 3. Find the best and worst student.
// Typical initialization for index: invalid value.
int tempBestIndex = -1;
int tempWorstIndex = -1;
int tempBestScore = 0;
int tempWorstScore = m * upperBound + 1;
for (int i = 0; i < n; i++) {
if (totalScores[i] == 0) {
continue; // 一旦等于0(表明有挂科,不参与评比),就跳出此次循环,进入下一次
}// Of if
if (tempBestScore < totalScores[i]) { // 找最好的
tempBestScore = totalScores[i];
tempBestIndex = i;
}// Of if
// Attention: This if statement cannot be combined with the last one
// using "else if", because a student can be both the best and the
// worst. I found this bug while setting upperBound = 65.
if (tempWorstScore > totalScores[i]) { // 找最差的
tempWorstScore = totalScores[i];
tempWorstIndex = i;
}// Of if
}// Of for i
// Step 4. Output the student number and score.
if (tempBestIndex == -1) {
System.out.println("Cannot find best student. All students have failed.");
}else {
System.out.println("The best student is No." + tempBestIndex + " with scores: "
+ Arrays.toString(data[tempBestIndex]));
}// Of if
if (tempWorstIndex == -1) {
System.out.println("Cannot find worst student. All students have failed.");
}else {
System.out.println("The worst student is No." + tempWorstIndex + " with scores: "
+ Arrays.toString(data[tempWorstIndex]));
}// Of if
}// Of task1
}// Of class Task1
运行结果:
The data is:
[[61, 51, 50], [59, 51, 55], [60, 50, 54], [61, 53, 53], [62, 57, 62], [50, 57, 58], [60, 57, 57], [60, 62, 60], [62, 64, 51], [52, 60, 58]]
The total scores are:
[0, 0, 0, 0, 0, 0, 0, 182, 0, 0]
The best student is No.7 with scores: [60, 62, 60]
The worst student is No.7 with scores: [60, 62, 60]
此任务具体实现如上,但在自己做的时候,对于随机数的生成发现了一个问题。题目的要求是:学生成绩的区间是 [50, 100],是一个完整的闭区间。但上面生成随机数的方式实际上取不到100,即是左闭右开的。对于这个情况,我自己通过代码也验证了一下。
此处是看生成的随机数是否包含区间的边界,所以数的范围设置得比较小,[0,3]。
import java.util.Arrays;
import java.util.Random;
public class RandomTest {
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String[] args) {
int n = 6; // 随机生成的数的个数
// 随机生成[0,3]中的数
int lowerBound = 0;
int upperBound = 3;
Random tempRandom = new Random();
int[] data = new int[n];
for (int i = 0; i < n; i++) {
data[i] = lowerBound + tempRandom.nextInt(upperBound - lowerBound);
}// Of for i
System.out.println("The data is:\r\n" + Arrays.toString(data)); // 打印出随机生成
}// Of main
}// Of class RandomTest
运行结果:
The data is:
[1, 2, 1, 0, 1, 2]
所以,应该将此句代码 data[i] = lowerBound + tempRandom.nextInt(upperBound - lowerBound)
更改一下:data[i] = lowerBound + tempRandom.nextInt(upperBound - lowerBound + 1)
更改之后,就能取到右边的边界值了!
The data is:
[0, 0, 0, 2, 1, 3]