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 }
  • 相关阅读:
    SpringMVC上传文件的三种方式(转载)
    几种流行Webservice框架性能对比(转载)
    @QueryParam和@PathParam使用方法比较
    MyEclipse开发Rest服务入门
    WebService SOAP、Restful和HTTP(post/get)请求区别
    Java WebService入门实例
    WebService 与 Socket 区别
    Double 数据保留两位小数二:直接截取小数后面两位,不进行四舍五入
    SVN 常识
    Android 友盟分享躺过的几个坑,大坑,坑爹啊
  • 原文地址:https://www.cnblogs.com/dust2017/p/12705845.html
Copyright © 2011-2022 走看看