day07--二维数组 & 递归

今天我们主要学习以下内容
1. 二维数组的概念,内存映象,二维数组中数据元素的访问
2. 二维数组的3中初始化方式
3. 方法的参数传递问题(基本数据类型和引用数据类型)
4 递归方法的定义,递归的算法核心思想

二维数组

引例:  

  我们已经学习了基本的一维数组,可以持有大量数据。

   思考如下场景: 假设我现在要统计,学校中某个年级中,各班的数学成绩的平均分。 思考一下,用一维数组可以吗?会遇到什么问题? 分析一下出现问题的原因是什么呢? 不同的维度

 

 

 

 图:二维数组的引例

  二维数组初始化三种格式

 

 

 图:二维数组的初始化格式1

 

图:二维数组的初始化格式2 

 

  1 package com.cskaoyan.two;
  2 
  3 /**
  4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
  5  * @version 1.0
  6  * 二维数组的实质:一维 (数组的) 数组
  7 
  8      二维数组的初始化格式1:
  9       数据类型[][] 变量名 = new 数据类型[m][n];
 10       m代表二维数组中一维数组的个数
 11       n代表二维数组中包含的每个一维数组,所能包含的元素个数
 12       特征:二维数组中每一个一维数组包含的元素个数相同
 13 
 14     二维数组定义的格式2
 15     数据类型[][] 变量名 = new 数据类型[m][];
 16     m表示这个二维数组有多少个一维数组
 17     这一次没有直接给出一维数组的元素个数,可以动态的给出。
 18 
 19     int[][] arr2 = new int[2][];
 20     arr[0] = new int[1];
 21     arr[1] = new int[2];
 22     特征: 初始化二维数组比较麻烦,二维数组中的一维数组,都需要我们自己初始化,
 23           但可以让二维数组的每个一维数组包含不同的元素个数
 24 
 25 
 26     二维数组定义的格式3
 27     数据类型[][] 变量名 = new 数据类型[][]{{元素…},{元素…},{元素…}};
 28 
 29     简化版格式:
 30     数据类型[][] 变量名 = {{元素…},{元素…},{元素…}};
 31 
 32     注意:
 33     简化版只能在定义数组的引用变量时使用!
 34 
 35 
 36 
 37  *
 38  *
 39  *
 40  */
 41 public class Demo1 {
 42 
 43   public static void main(String[] args) {
 44 
 45     // 二维数组的初始化格式1:
 46     // 数据类型[][] 变量名 = new 数据类型[m][n];
 47     firstInitialization();
 48 
 49     // 二维数组的初始化格式2:
 50     // 数据类型[][] 变量名 = new 数据类型[m][];
 51     secondInitialiization();
 52 
 53     //二维数组定义的格式3
 54     //数据类型[][] 变量名 = new 数据类型[][]{{元素…},{元素…},{元素…}};
 55     // 3个一维数组,每个一维数组的初值分别通过{}来指定
 56     int[][] arr3 = new int[][] {{1}, {2, 3}, {4, 5, 6}};
 57 
 58     traverseTwoDimensionArray(arr3);
 59 
 60   }
 61 
 62 
 63   public static void traverseTwoDimensionArray(int[][] arr) {
 64     //遍历二维数组,输出二维数组中的每一个值
 65     // arr 指向的是代表二维数组的那个一维数组  arr.length 表示二维数组中的一维数组个数
 66     // arr[i] 代表二维数组中的第i个一维数组  arr[i].length 表示的就是二维数组中第i个一维数组的元素个数
 67 
 68     for (int i = 0; i < arr.length; i++) {
 69 
 70       //完成,对二维数组中的一个一维数组的遍历
 71       for (int j = 0; j < arr[i].length; j++) {
 72         System.out.print(arr[i][j] + " ");
 73       }
 74       System.out.println();
 75     }
 76 
 77 
 78   }
 79 
 80   private static void secondInitialiization() {
 81     // 数据类型[][] 变量名 = new 数据类型[m][];
 82     int[][] arr2 = new int[2][];
 83     //初始化二维数组中的第一个一维数组
 84     arr2[0] = new int[1];
 85     //初始化二维数组中的第二个一维数组
 86     arr2[1] = new int[2];
 87   }
 88 
 89   private static void firstInitialization() {
 90 
 91 
 92     // 一个包含2个一维数组,且每个一维数组中包含2个元素的 一个二维数组
 93     int[][] arr = new int[2][2];
 94 
 95     // 访问一下二维数组
 96 
 97     //1. 输出二维数组的引用变量的值
 98     System.out.println(arr); //[[(表示一个二维数组) I@ 4554617c
 99     // 2. 输出执行二维数组中,第一个一维数组的,引用的值
100     System.out.println(arr[0]); // [I@74a14482
101 
102     //3. 访问二维数组中,一维数组的元素值
103     System.out.println(arr[0][0]);
104 
105     arr[1][1] = 100;
106     System.out.println(arr[1][1]);
107   }
108 
109 }

 

  练习:

  • 二维数组遍历
  • 打印杨辉三角形(行数可以键盘录入)
  1 package com.cskaoyan.two;
  2 
  3 import java.util.Scanner;
  4 
  5 /**
  6  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
  7  * @version 1.0
  8  *
  9  * 1.二维数组遍历
 10     公司年销售额求和
 11     某公司按照季度和月份统计的数据如下:单位(万元)
 12     第一季度:22,66,44
 13     第二季度:77,33,88
 14     第三季度:25,45,65
 15     第四季度:11,66,99
 16 
 17    2. 打印杨辉三角形(行数可以键盘录入)
 18  1    1
 19  2    1 1
 20  3    1 2 1
 21  4    1 3 3 1
 22  5    1 4 6 4 1
 23 
 24    1. 对于杨辉三角而言,第一行和第二行数据是固定的
 25    2. 每一行的元素个数和行数相同(从第1行开始)
 26    3. 从第3行开始:
 27       a. 每一行的第一个和最后一个元素值固定,都是1
 28       b. 每一行出首尾位置之外,其他位置的元素值都有规律:
 29          第i行第j列 = 第i - 1行第j列的值 + 第i-1行j - 1列
 30 
 31 
 32  */
 33 public class Exercise {
 34 
 35   public static void main(String[] args) {
 36 
 37     // 第三种初始化二维数组格式的简化
 38     int[][] sales = {{22,66,44}, {77,33,88}, {25,45,65}, {11,66,99}};
 39     //简化版只能在定义数组的引用变量时使用!
 40     //sales = {{22,66,44}, {77,33,88}, {25,45,65}, {11,66,99}};
 41     //sales = new int[][]{{22,66,44}, {77,33,88}, {25,45,65}, {11,66,99}};
 42 
 43     //System.out.println(exercise01(sales));
 44 
 45 
 46 
 47     //练习2
 48     Scanner scanner = new Scanner(System.in);
 49     int n = scanner.nextInt();
 50     int[][] arr = exercise02(n);
 51     traverseTwoDimensionArray(arr);
 52 
 53   }
 54 
 55 
 56   public static int exercise01(int[][] salse) {
 57     int sum = 0;
 58 
 59     for (int i = 0; i < salse.length; i++) {
 60 
 61       for (int j = 0; j < salse[i].length; j++) {
 62          sum += salse[i][j];
 63       }
 64     }
 65 
 66     return sum;
 67   }
 68 
 69 
 70   /**
 71    *
 72    * @param n  表示杨辉三角的行数
 73    * @return 表示n行杨辉三角数据的二维数组
 74    */
 75   public static int[][] exercise02 (int n) {
 76 
 77     //针对第n值为1和n值为2,做特殊处理
 78     if (n == 1) {
 79       return new int[][]{{1}};
 80     }
 81 
 82     if (n == 2) {
 83       return new int[][]{{1}, {1, 1}};
 84     }
 85 
 86 
 87     // 表示,最终返回的二维数组的结果
 88     int[][] result;
 89     // 用格式2初始化,结果二维数组
 90     result = new int[n][];
 91     // 初始化杨辉三角的第一行和第二行数据
 92     result[0] = new int[] {1};
 93     result[1] = new int[] {1, 1};
 94 
 95     // 从第3行开始,计算并初始化,每一行杨辉三角数据
 96     for (int i = 2; i < n; i++) {
 97       // 初始化杨辉三角第i + 1行 对应的第i个一维数组
 98       result[i] = new int[i + 1];
 99       //对于杨辉三角的第i行数据, 初始化首尾元素的值
100       result[i][0] = 1;
101       result[i][i] = 1;
102 
103 
104       for (int j = 1; j < i; j++) {
105         //第i行第j列 = 第i - 1行第j列的值 + 第i-1行j - 1列
106         result[i][j] = result[i - 1][j] + result[i - 1][j - 1];
107       }
108     }
109     return result;
110   }
111 
112   public static void traverseTwoDimensionArray(int[][] arr) {
113     //遍历二维数组,输出二维数组中的每一个值
114     // arr 指向的是代表二维数组的那个一维数组  arr.length 表示二维数组中的一维数组个数
115     // arr[i] 代表二维数组中的第i个一维数组  arr[i].length 表示的就是二维数组中第i个一维数组的元素个数
116 
117     for (int i = 0; i < arr.length; i++) {
118 
119       //完成,对二维数组中的一个一维数组的遍历
120       for (int j = 0; j < arr[i].length; j++) {
121         System.out.print(arr[i][j] + " ");
122       }
123       System.out.println();
124     }
125 
126 
127   }
128 
129 }

 

  看程序写结果,并总结基本类型和引用类型参数的传递问题(题目在备注部分)

 1 package com.cskaoyan.method;
 2 
 3 /**
 4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
 5  * @version 1.0
 6  *
 7  * 总结一下:
 8  *
 9  *   在java语言中,不管参数的类型,是引用类型还是,基本数据类型,在实际参数和形式参数进行值传递的方式只有一种:
10  *   实际参数的值   复制一份 赋值给形式参数
11  *
12  *   所以,实参的值,其实就有两份,调用方法中一份,被调用方法中一份
13  *
14  * 1. 当方法的参数是基本数据类型的参数的时候,
15  *     参数有两份,同时参数对应的数据的值,也有两份
16  *
17  * 2. 当方法的参数是引用数据类型的时候
18  *     参数值有两份,但是两个数组类型引用变量,对应的值(数组),只有一个
19  *
20  *
21  */
22 public class Demo1 {
23 
24   public static void main(String[] args) {
25 
26     //int a = 10;
27     //int b = 20;
28     //System.out.println("main a:" + a + ",b:" + b);
29     //change(a, b);
30     //System.out.println("main a:" + a + ",b:" + b);
31 
32 
33 
34     int[] arr = {1, 2, 3, 4, 5};
35     change(arr);
36     System.out.println(arr[1]);
37   }
38 
39   public static void change(int a, int b) {
40     System.out.println("change a:" + a + ",b:" + b);
41     a = b;
42     b = a + b; // 2 * b
43     System.out.println("change a:" + a + ",b:" + b);
44   }
45 
46 
47   public static void change(int[] arr) {
48     for (int x = 0; x < arr.length; x++) {
49       if (arr[x] % 2 == 0) {
50         arr[x] *= 2;
51       }
52     }
53   }
54 }

 

 

 图:方法的参数传递

 

递归

递归定义:方法定义中调用方法本身的现象

实现递归需要注意的问题:

  • 递归一定要有出口!!
  • 次数不能太多,否则就出现 stack overflow

 

 图:递归方法错误 & 栈内存管理

 

 

 1 package com.cskaoyan.recursion;
 2 
 3 /**
 4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
 5  * @version 1.0
 6  *
 7  * 仅仅从语法角度:
 8  * 递归方法定义:方法 定义中 调用 方法本身的方法
 9  *
10  * 实现递归需要注意的问题:
11       1. 递归一定要有出口!!(递归一定要有终止条件,在一定条件下,终止自己调用自己)
12       2. 次数不能太多,否则就出现 stack overflow
13  *
14  */
15 public class Demo1 {
16 
17   public static void main(String[] args) {
18 
19     //调用递归方法
20     //recursion1();
21 
22     //调用带递归出口的递归方法
23     recursion2(4);
24 
25     //次数不能太多,否则就出现 stack overflow
26     recursion2(Integer.MAX_VALUE);
27 
28   }
29 
30 
31   // 按照递归方法的定义来书写  java.lang.StackOverflowError 栈溢出错误
32   public static void recursion1() {
33     // 方法体中,自己调用自己
34     recursion1();
35   }
36 
37 
38   /*
39       带递归出口的递归方法
40    */
41   public static void recursion2(int n) {
42 
43     //每一次调用自己之前,判断一下 n>0才自己调用自己,否则(n <= 0),终止自己调用自己
44     if (n <= 0) {
45       System.out.println("达到出口条件,不在自己调用自己 " + n);
46       return;
47     }
48 
49     n--;
50     //否则,如果还没有达到递归的出口条件,继续自己调用自己
51     recursion2(n);
52 
53   }
60 }

 

 图:带出口条件的递归方法

 

 

例子:

1.汉诺塔问题:

  有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。

要求按下列规则将所有圆盘移至 C 杆:

  • 每次只能移动一个圆盘;
  • 大盘不能叠在小盘上面。

提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。

问:最少要移动多少次?如何移?

  2 
  3 /**
  4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
  5  * @version 1.0
  6  *
  7  * 1.有三根杆子A,B,C。A杆上有 N(64) 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
  8     a. 每次只能移动一个圆盘;
  9     b. 大盘不能叠在小盘上面。
 10     提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
 11     问:最少要移动多少次?如何移?
 12 
 13 
 14     解决思路以N个圆盘为例:
 15 
 16          1. 当我们要解决N个圆盘的搬运问题,对于N个圆盘,我们可能无法一次性得到结果,
 17             于是,我们把N个圆盘的搬运问题, -》 最大的云盘的搬运 & 最大圆盘上面的 n - 1
 18 
 19          2. 对于规模为1那个待搬运的最大的圆盘,直接就知道如何搬运(一步搞定)
 20 
 21          3. 在2的基础上,只要,解决 N - 1个圆盘搬运的问题
 22 
 23          总结一下,汉诺塔问题,解决思路(递归算法的核心思想): 分而治之
 24                                      把一个复杂的大规模的问题,分解成若干相似的小规模的子问题,
 25                                     当问题规模,足够小的是时候,我们就可以直接得到小规模问题的解,
 26                                     再把小规模问题的解,组合起来,——> 大规模问题提的解
 27 
 28 
 29     1.用递归方法 -> 实现按照递归的方式,解决汉诺塔问题的代码
 30 
 31     n 为3时的输出序列:
 32       A——>C
 33       A——>B
 34       C——>B
 35       A——>C
 36       B——>A
 37       B——>C
 38       A——>C
 39 
 40     2. n个圆盘,总共要搬运多少次,才能完成n个圆盘的搬运?
 41        f(n) 表示n个圆盘搬运的次数
 42 
 43        f(n) = f(n - 1) +  1 +  f(n - 1) = 2 * f(n - 1) + 1
 44        f(n) + 1 = 2(f(n - 1) + 1)
 45        ...
 46        f(1) + 1 = 2
 47 
 48       {f(n) + 1} = 2^n
 49        f(n) = 2^n - 1
 50 
 51 
 52  */
 53 public class Demo2 {
 54 
 55   public static void main(String[] args) {
 56 
 57     //hanoi(3, 'A', 'C', 'B');
 58 
 59     System.out.println(count(3));
 60 
 61   }
 62 
 63   //  f(n) = f(n - 1) +  1 +  f(n - 1)
 64   public static int count(int n) {
 65 
 66     // 出口条件
 67     if (n == 1) {
 68       return 1;
 69     }
 70 
 71     // 递归计算,n个圆盘搬运的的次数
 72 
 73     return count(n - 1) + 1 + count(n - 1);
 74   }
 75 
 76 
 77   /**
 78    *
 79    * @param n      表示当前待搬运的圆盘的数量(代表当前问题的规模)
 80    * @param start  当前待搬运的圆盘所在的杆的名字
 81    * @param end    待搬运的n个盘盘,搬运到的目标杆的名字
 82    * @param middle  此次把n个圆盘从start杆 -》 end杆时,所使用的辅助杆的名字
 83    */
 84   public static void hanoi(int n, char start, char end, char middle) {
 85 
 86     //递归方法的出口条件
 87     if (n == 1) {
 88       System.out.println(start + "——>" + end);
 89       return;
 90     }
 91 
 92     //分解规模为n汉汉塔问题
 93 
 94     // 将待搬运那上面的n-1个圆盘,搬运到辅助杆上
 95     hanoi(n - 1, start, middle, end);
 96 
 97     // 将起始杆上剩下的那个,待搬运的最大的圆盘,一次搬运到目标杆,它的最终位置
 98     System.out.println(start + "——>" + end);
 99 
100     //接下来,对于剩余的在middle上的那n-1个圆盘,从middle杆开始,以start为辅助,移动到end杆上
101     hanoi(n - 1, middle, end, start);
102   }
103 
104 }

 

 2. 周末晚上你和女朋友去看电影,月黑风高,女朋友悄悄地问你:我们在第几排?电影院太黑,没办法数?怎么办?

 1 package com.cskaoyan.recursion;
 2 
 3 /**
 4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
 5  * @version 1.0
 6  *
 7  * 2. 周末晚上你和女朋友去看电影,月黑风高,女朋友悄悄地问你:我们在第几排?电影院太黑,没办法数?怎么办?
 8  *    a. 无法直接看到,自己在第几排,但是我可以,问前排的同学,他在第几排
 9  *    b. 当依次向前询问,当问到第一排同学的时候,他可以用触觉来判断,比如,摸了一下发现前面没有椅子
10  *
11  *    f(n) = f(n - 1) + 1
12 
13  */
14 public class Demo3 {
15 
16 
17   public static void main(String[] args) {
18 
19     int row = f(10);
20     System.out.println(row);
21   }
22 
23 
24   /*
25        模拟,依次向前询问,自己在第几排,最终得到自己所在的排数
26        f(n) = f(n - 1) + 1
27    */
28   public static int f(int n) {
29 
30     if(n == 1) {
31       return 1;
32     }
33 
34     //如果当前不是第一排的同学,就继续问他前排的同学
35     return f(n - 1) + 1;
36   }
37 
38 }

 

3. 求n的阶乘

 

 1 package com.cskaoyan.recursion;
 2 
 3 /**
 4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
 5  * @version 1.0
 6  * 3. 求n的阶乘
 7      f(n) = n * ((n - 1) * (n - 2) ... * 2 * 1)
 8      f(n) = n * f(n - 1)
 9  */
10 public class Demo4 {
11 
12 
13   public static void main(String[] args) {
14     System.out.println(multiple(5));
15   }
16 
17   /**
18    *
19    * @param n  所求的问题规模 n的阶乘
20    * @return
21    *
22    *  f(n) = n * f(n - 1)
23    *  f(1) = 1
24    */
25   public static int multiple(int n) {
26 
27     if(n == 1) {
28       return 1;
29     }
30 
31     return n * multiple(n - 1);
32 
33   }
34 
35 }

 

4. 求n的阶乘 4.有一对兔子,从出生后第三个月开始每月生一对兔子,小兔子从第三个月开始每月也生一对兔子,假如是不死神兔,那么第20个月一共生多少对兔子?

 1 package com.cskaoyan.recursion;
 2 
 3 /**
 4  * @author zhangshuai@Cskaoyan.onaliyun.com on 2020/4/13.
 5  * @version 1.0
 6  *
 7  * 4.有一对兔子,从出生后第三个月开始每月生一对兔子,小兔子从第三个月开始每月也生一对兔子,
 8  *   假如是不死神兔,那么第20个月一共生多少对兔子?
 9  *
10  *  月份         1   2   3   4  ...
11     兔子的数量    1   1   2   3  ...
12 
13 
14     i                      i + 1                   i + 2
15     N(i)     +              N(i)              =     N(i) * 2
16     N(i - 1) +              N(i - 1) * 2      =     N(i - 1) * 2 + N(i - 1)
17     N(i - 2) +              N(i - 2) * 2      =     N(i - 2) * 2 + N(i - 2)
18 
19       N(i) + N(i + 1) = N(i + 2)
20 
21      count(n) = count(n - 1) + count(n - 2)
22 
23      count(1) = 1
24      count(2) = 1
25  */
26 public class Demo5 {
27 
28   public static void main(String[] args) {
29     System.out.println(count(20));
30   }
31 
32   /*
33       计算不死神兔的数量
34        count(n) = count(n - 1) + count(n - 2)
35    */
36   public static int count(int n) {
37 
38     if(n == 1) {
39       return 1;
40     }
41 
42     if (n == 2) {
43       return 1;
44     }
45 
46     return count(n - 1) + count(n - 2);
47 
48   }
49 
50 
51 
52 
53 
54   /*
55      分析:
56 
57    首先,我们设有n条直线时的答案为f(n)
58    1.那么当有n - 1条直线时,平面最多被分成了f(n - 1)个区域。
59    2.则第n条直线,如果要切成的区域数最多,那么第n条直线就必须与每条直线相交,且不能有同一交点。这样就会得到n - 1个交点。
60    3.而这些交点将这条直线(第n条直线)分为2条射线和n - 2条线段。而每条射线和线断将以有的区域一分为二。
61      这样就多出了2 + (n - 2),也就是n个区域。
62 
63     f(1) = 2;  一条直线将一个平面分成2份
64     f(n) = f(n-1) + n;
65 
66    */
67   public static int f(int n) {
68     if (n == 1) {
69       return 2;
70     }
71     return f(n - 1) + n;
72   }
73 
74 }

 

posted @ 2020-04-15 15:51  dust--  阅读(620)  评论(0编辑  收藏  举报