JAVA基础学习——Day7(函数递归解决汉诺塔和斐波那契数列,数组初识)

一、Day6部分复习(重中之重)

1. 方法递归(程序结构):函数自身调用自身。在递归函数的内部,有调用了函数本身的操作,称之为递归函数

2. 使用递归的条件

(1)原问题可以拆分为多个子问题的解

(2)拆分后的子问题与原问题之间除了数据规模不同外,其他解决思路完全相同

(3)存在递归的终止条件(不可无限拆分问题。)

注意:终止条件:无须借助其他方法或语句,当前场景下就能立即得出问题的解,该条件就是递归终止条件。例如下列例子

3.  写出正确递归代码的技巧:抓住函数的语义(语义:这个函数是干什么用的),假设这个函数已经实现完成,在当前场景下只需要利用好这个函数辅助解决问题即可

 二、斐波那契数列和汉诺塔(递归实现)

1. 斐波那契数列

(1)思路:

(2)代码实现

 

2. 上述代码运行时间会随着求解项数的增加而极具上升原因如下

原理:上图中,每一个节点代表着函数的一次执行,而运行时间和函数的执行次数成正比,因此这个问题转化成了上述二叉树节点个数的求解。标号为N的二叉树中最多有2^N-1个节点,因此时间复杂度为(2^N) 。因此当N变化很小的时候,运行时间会指数级增长

3. 解决方法

(1)解决方法一:记忆化搜索(将递归过程中,每个计算后的数字保存起来)

A. 原理:如上图所示,F(2)/F(3)被重复计算拖慢了整个函数的运行速度

B. 记忆化搜索解决问题:当发现F(num)已经被计算过了,不再递归,直接返回计算后的结果,减少了很多次无用的递归展开

C. 代码实现

 (2)解决方法二:动态规划解决(利用循环解决,不使用递归)

 注意:动态规划的速度又得到了进一步提升

三、汉诺塔问题(递归解决)

1. 汉诺塔问题(最少步数):

 (1)规则

 (2)规则图示(以三个盘子为例子)

 (3)移动过程(以三个盘子为例子,从上到下依次给盘子编号1,2,3)

 (4)思路:

要素1:可拆分如上图所示可以分为三个部分:

//A. 将A上的编号为N-1的盘子从A->B ,C作为辅助,暂存比较小的盘子
//B. 将A上的编号最大(N)的盘子从A -> C
//C. 将B上的编号N-1的盘子移动到C,A作为辅助,暂存比较小的盘子

要素2://递归的终止条件:如果只有一个盘子,直接从A->C(只有一个盘子的时候可以移动,且从起始柱移动到目标柱)

理解:要想移动3个盘子,就得移动上2个盘子,要想移动2个盘子就得移动上1个盘子,直到1个盘子的时候才可以移动,且从起始柱移动到目标柱

(5)实现代码

 结果

 四、数组的定义与使用

 1. 数组定义:(1)相同数据类型的集合(2)数组在内存空间中连续存储(3)java中数组是引用数据类型,在堆中存储

2. 数组特点:

(1)数组中存放的元素类型相同

(2)数组空间连续,一个元素紧挨另一个元素

(3)每个数组元素都有下标,第一个数组元素的下标从0开始

3. 数组的创建与初始化(注意:java中数组的长度一旦定义之后就是固定的,也就是定长数组)

(1)静态初始化:定义数组时,随机将每个元素的值也初始化好

A. 语法:

数据类型[] 数组名 = {元素值1,元素值2....,元素值N};

B. 代码实例:

 

C. 注意:数组长度编译器会根据元素的个数来自动计算并定长

(2)动态初始化:定义数组时,不给元素具体赋值,定义之后再赋值,数组中每个元素都是其数据类型的默认值

A. 语法:

数据类型[] 数组名 = new 数据类型[数组长度];

注意:前面的数据类型和后面的一致

B. 代码实例:

(3)注意:静态初始化实际上是一个语法糖,从源代码上看会加上new int[]

 

 4. 数组的基本使用

(1) 数组中元素的访问

A. 原理:

a. 由于数组在内存中是一块连续的内存空间,且每个数组元素都有下标,第一个元素的下标为0,程序只需要记录第一个元素的地址(数组的首地址)即可。

b. 下标为 i 的数组元素(第i+1个元素)的地址 = 首地址 + 数据类型的大小 * i

c. 数组的下标称为数组的偏移量:该数组元素相对于第一个元素的偏移量(即相对于第一个元素的位置(首地址)差了几个单位的数据类型大小)

 因此数组元素的下标为[0,N-1],N为数组的长度。(第一个元素距离自己的偏移量为0,因此最后一个元素的偏移量为N-1)

d.基于上述的原理,得到了数组的随机访问特性:可以立即取得数组中的任何一个元素

B. 语法:

数组名称[数组下标];

注意1:元素个数 =  下标 + 1

注意2: 下标范围是[0, N-1],越界访问会报错

C. 实例:

(2)得到数组长度的方法

A. 语法

数组名称.length

B. 实例

(3) 遍历数组的每一个元素

A. 通过普通的for循环,数组长度来遍历数组的每一个元素(可打印,可修改) 

 

 

 B. 只需要取得数组元素,并不修改,可以用for-each循环来遍历

 a. 语法:

for(int i : 数组名){

        //循环体

}

b. 原理

 

c. 代码实例

 五、什么是引用

1. 引用数据类型:数组,类,接口都属于引用数据类型

所有引用数据类型的变量,保存的都是一块堆内存的地址(对象的地址),不是具体数值,所谓的引用只不过是给对象起了个别名而已

2. java的栈区和堆区

(1)栈区:保存的是函数的栈帧,每个函数的调用和结束,就对应一个栈帧的入栈和出栈过程。方法中的所有局部变量都保存在栈帧中

(2)堆区:java中的new是为了产生对象的,new产生的对象都保存在堆区中。

3. 引用类型的变量就是保存对象的地址(相对的,基本数据类型都保存的是具体是数值)

举例:new int [5]相当于在内存中开辟了一块新的空间,数组是连续空间。将数组的首地址赋值给这个引用类型的变量 int[] num

 

 

 4. swap函数数组实现

(1)原理:

 

(2)代码实现

 

5. NPE异常

(1)原理:当引用数据类型保存的值为null,表示该引用不指向任何堆内存空间,无法通过该引用进行任何具体的操作(读写操作),一旦操作则发出NPE异常

(2)例子:

 6. 数组作为函数的返回值

例子:定义一个方法,传入一个整数N,返回从第一项到第N项的所有斐波那契数(此处应该返回一个整型数组)

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值