zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    172. Factorial Trailing Zeroes
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
    91. Decode Ways
    LeetCode 328 奇偶链表
    LeetCode 72 编辑距离
    LeetCode 226 翻转二叉树
    LeetCode 79单词搜索
    LeetCode 198 打家劫舍
    LeetCode 504 七进制数
  • 原文地址:https://www.cnblogs.com/dust2017/p/12705845.html
Copyright © 2011-2022 走看看