第5章 函数
5.1 函数的概念
什么是函数
函数的定义就是指一段具有独立功能的代码,减少代码冗余,提高程序的利用率和效率。
我们应该如何去定义函数呢?
- 需要一个封闭的空间,将这段独立性的代码进行封装,用一对大括号
- 需要对每一个封闭的空间进行命名,函数名
- 函数所需要的一些原始数据
- 函数所产生的一些结果数据
函数的语法格式
修饰符 函数类型 返回值类型 函数名(数据类型 数据1,数据类型 数据2,...) {
独立功能的代码片段;
return 函数的计算结果;
}
- 修饰符:指的是函数的访问权限,public private 默认 protected
- 函数类型:函数的分类,本地函数native,静态函数static,同步函数 synchronized
- 返回值类型:指的就是函数计算结果的数据类型 如果函数没有返回值 则为void
- 函数名:就是函数的名称
- 参数列表:指的是外界向函数传入的数据(实际参数),由这些参数变量进行接收(形式参数)
- 函数体:具有独立功能的代码片段;
- return:仅仅表示函数结束!如果函数有返回值,则return后跟返回值;如果没有返回值,则return可以不写,但是是存在的(隐藏的 在最后一行)
根据形参和返回值来看,函数有如下几个分类
有参数有返回值
例:求三个数字当中的最大值
public class
Sample
{
public static
void
main
(
String
[]
args
) {
int
max
=
getMax
(
1
,
2
,
3
);
System
.
out
.
println
(
max
);
}
public static
int
getMax
(
int
a
,
int
b
,
int
c
) {
/*
if (a >= b && a >= c) {
return a;
}else if (b >= a && b >= c) {
return b;
}else {
return c;
}
*/
if
(
a
>=
b
&&
a
>=
c
) {
return
a
;
}
if
(
b
>=
a
&&
b
>=
c
) {
return
b
;
}
if
(
c
>=
a
&&
c
>=
b
) {
return
c
;
}
return
-
10000
;
}
}
有参数没返回值
例:将三个字符串的反转,并拼接打印
public class
Sample
{
public static
void
main
(
String
[]
args
) {
print
(
"123"
,
"456"
,
"789"
);
}
//123 456 789 =? 321654987
public static
void
print
(
String
s1
,
String
s2
,
String
s3
) {
System
.
out
.
println
(
reverse
(
s1
)
+
reverse
(
s2
)
+
reverse
(
s3
));
}
public static
String
reverse
(
String
s
) {
String
res
=
""
;
for
(
int
i
=
s
.
length
()
-
1
;
i
>=
0
;
i
--
) {
res
+=
s
.
charAt
(
i
);
}
return
res
;
}
}
没参数有返回值
例:
获取当前时间的字符串
public class
Sample
{
public static
void
main
(
String
[]
args
) {
String
currentTime
=
getTime
();
System
.
out
.
println
(
currentTime
);
}
public static
String
getTime
() {
//1.
获取总毫秒数
long
millis
=
System
.
currentTimeMillis
();
//2.
计算总秒数
long
seconds
=
millis
/
1000
;
//3.
计算总分钟数
long
minutes
=
seconds
/
60
;
//4.
计算总小时数
long
hours
=
minutes
/
60
;
//5.
计算当前小时数
long
currentHours
=
hours
%
24
;
//6.
计算当前分钟数
long
currenrMinutes
=
minutes
%
60
;
没参数没返回值
//7.
计算当前秒数
long
currentSeconds
=
seconds
%
60
;
return
currentHours
+
":"
+
currenrMinutes
+
":"
+
currentSeconds
;
}
}
没参数有返回值
例:
public class
Sample
{
public static
void
main
(
String
[]
args
) {
getTime
();
}
public static
void
getTime
() {
//1.
获取总毫秒数
long
millis
=
System
.
currentTimeMillis
();
//2.
计算总秒数
long
seconds
=
millis
/
1000
;
//3.
计算总分钟数
long
minutes
=
seconds
/
60
;
//4.
计算总小时数
long
hours
=
minutes
/
60
;
//5.
计算当前小时数
long
currentHours
=
hours
%
24
;
//6.
计算当前分钟数
long
currenrMinutes
=
minutes
%
60
;
//7.
计算当前秒数
long
currentSeconds
=
seconds
%
60
;
System
.
out
.
println
(
currentHours
+
":"
+
currenrMinutes
+
":"
+
currentSeconds
);
}
}
总结定义函数时需要考虑的有哪些?
- 函数的运行有哪些未知的参数?
- 函数的运行结果有是什么?
- 明确参数和结果
- 明确内容和返回
- 函数到底要干什么?尽量将独立功能且重复性较高的代码片段提取出来
5.2 函数的运行原理
函数的运行是基于栈运行的
栈:是一种先进后出的容器,我们这里面所说的栈是指
JVM
中的栈内存空间
每一个函数,叫做栈帧,栈帧中所包含的内容有函数的定义,参数列表,函数的执行内容代码
每一个函数要运行,就相当于这个栈帧进入到栈内存中
-
入栈
如果一个函数即将结束,将这个栈帧从栈顶移出
-
出栈
如果栈内存中有多个栈帧,运行的是最上面的栈帧,底下的栈帧暂停运行,直到该栈帧为栈顶元素
比如:主函数先进栈,开始逐行运行,如果执行到第
n
行,调用另外一个函数
A
,则主函数在第
n
行
暂停运行,将另一个函数
A
的栈帧入栈,再继续逐行运行,直到函数
A
的内容执行完毕,函数
A
出栈,主
函数接着从第
n
行继续向下执行。以此类推。
5.3 函数重载
同一个类中可以出现多个同名函数,这个现象就叫做函数的重载(
overload
)
如何来区分同名函数是否是重载关系呢?前提必须是同名,和返回值类型无关(返回值类型只和函
数的计算功能相关),和权限也没有关系,和形式参数的名称也无关!只和形式参数的数据类型有关
(数量,排列组合)
下列哪些是该函数的重载:
- int show(int x, float y, char z) :不算重载 数据类型都是int float char
- void show(float b,int a,char c):算重载,顺序不一样float int char
- void show(int a,int b,int c):算重载,顺序不一样int int int
- double show():算重载,参数不一样
寻找重载函数的流程:
1.
看是否有确切的参数定义匹配,
int int
找
int int
2.
看是否有可兼容的参数定义匹配,
int int
找
double double
或
int double
或
double int
3.
如果可兼容的参数定义匹配较多,会报引用确定报错 引用不明确
5.4 函数的递归
函数的递归就是指函数自身调用自身。
递归的特点
- 但凡迭代能够解决的问题,递归都可以解决;递归能够解决的问题,迭代就不一定了
- 相对而言,从内存的角度而言,函数如果过多的自我调用,势必会对内存不友好,占用过多
- 通常来讲,同样的问题用递归写要比用迭代写代码量较少
用递归实现斐波那契数列
public class
Sample
{
public static
void
main
(
String
[]
args
) {
/*
1 1 2 3 5 8 13 21 34 55 ...
1 x=1,x=2
f(x) =
f(x-1) + f(x-2) x>2
f(5)
f(4) f(3)
f(3) f(2) f(2) f(1)
f(2) f(1)
*/
//
递归
O(2^n)
System
.
out
.
println
(
f
(
35
));
//
迭代
O(n)
System
.
out
.
println
(
fibo_it
(
35
));
}
public static
int
fibo_it
(
int
x
) {
if
(
x
==
1
||
x
==
2
) {
return
1
;
}
/*
1 1 2 3
c
a b
*/
int
a
=
1
;
int
b
=
1
;
int
c
=
0
;
for
(
int
i
=
3
;
i
<=
x
;
i
++
) {
c
=
a
+
b
;
a
=
b
;
b
=
c
;
}
return
c
;
}
public static
int
f
(
int
x
) {
if
(
x
==
1
||
x
==
2
) {
return
1
;
}
else
{
return
f
(
x
-
1
)
+
f
(
x
-
2
);
}
}
}
用递归实现汉诺塔
public class
Demo81
{
/*
前
4
个
x->z
前
3
个
x->y
前
2
个
x->z
前
1
个
x->y
第
2
个
x->z
前
1
个
y->z
第
3
个
x->y
前
2
个
z->y
前
1
个
z->x
第
2
个
z->y
前
1
个
x->y
第
4
个
x->z
前
3
个
y->z
前
2
个
y->x
前
1
个
y->z
第
2
个
y->x
前
1
个
z->x
第
3
个
y->z
前
2
个
x->z
前
1
个
x->y
第
2
个
x->z
前
1
个
y->z
*/
public static
void
main
(
String
[]
args
) {
String
x
=
"x"
;
String
y
=
"y"
;
String
z
=
"z"
;
hano
(
3
,
x
,
y
,
z
);
//
前
3
层从
x->z
//
前
2
层从
x->y
}
public static
void
hano
(
int
level
,
String
begin
,
String
mid
,
String
end
) {
if
(
level
==
1
) {
System
.
out
.
println
(
begin
+
"->"
+
end
);
}
else
{
//
前
level-1
层
hano
(
level
-
1
,
begin
,
end
,
mid
);
System
.
out
.
println
(
begin
+
"->"
+
end
);
//
前
leve1-1
层
hano
(
level
-
1
,
mid
,
begin
,
end
);
}
}
}
第6章 数组
6.1 数组的概念及定义
数组主要用于解决大量数据计算与存储的问题
比如:输入
100
个数字,统计其中的最大值和最小值并计算平均值,创建
100
个变量,会有一堆
if-
else
语句,比较麻烦。
数组是
Java
提供的一种最简单的数据结构,可以用来存储一个元素
个数固定
且
类型相同
的有序
集。
数组在内存中的情况
栈:主要用于运行函数的内存
堆:主要用于存储数据对象的内存
每一个数组而言,都是存在堆内存当中,每一个数组都是一个对象
数组本质上就是在堆内存中一系列地址连续且空间大小相等的存储空间(变量),每一个存
储空间用来存储数据(基本,引用)
数组是在堆内存中存储,称之为是一个对数对象,并且在堆内存中存储的数据都有
默认初始
化
的流程。所以数组创建之初,每一个存储空间里面都会被
JVM
初始化该数据类型对应的零值。
数组的地址是连续的,所以通过公式:
An=A1+(n-1)*d
可以快速访问到其他的元素,所以对于
数组而言查找元素比较快的。将元素的真实物理地址转换成对应的角标获取元素。
如何来调用数组呢?通过一个变量存储该数组在堆内存当中的首元素的地址。
当数组一旦定义出来,其长度不可变,存储空间的内容是可变的
所以我们在定义数组的时候,要么把长度固定,要么直接输入相关的元素。
数组的定义方式
//
创建一个指定长度且指定数据类型的一维数组,名称为数组名,虽然没有指定元素,但是会有默认值
数据类型
[]
数组名
=
new
数据类型
[
长度
];
//
创建一个指定元素且指定数据类型的一维数组,名称为数组名,虽然有指定元素,还是有默认初始化这个步骤的!
数据类型
[]
数组名
=
new
数据类型
[]{
数据
1
,
数据
2
,...,
数据
n
};
数据类型
[]
数组名
=
{
数据
1
,
数据
2
,...,
数据
n
};
public class
Sample
{
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
new
int
[
5
];
System
.
out
.
println
(
arr
[
0
]);
//System.out.println(arr[5]);
//ArrayIndexOutOfBoundsException
arr
[
2
]
=
10
;
int
[]
arr2
=
arr
;
System
.
out
.
println
(
arr2
[
2
]);
arr2
=
null
;
//System.out.println(arr2[2]);
//NullPointerException
/*
String s = null;
s.length();
*/
arr
=
null
;
}
}
6.2 常用数组操作
数组遍历问题
public class
Sample
{
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
new
int
[]{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
};
//String str str.length()-
函数
//int[] arr arr.length-
属性
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
arr
[
i
]
=
arr
[
i
]
*
10
;
System
.
out
.
println
(
arr
[
i
]);
}
//
通过角标遍历 可以在遍历的过程中对指定的元素进行修改
//foreach
遍历 主要针对的是一些可迭代对象
Iterable
/*
for (
数据类型 变量名
:
可迭代容器
) {
}
*/
for
(
int
num
:
arr
) {
//num -> arr[i]
num
=
num
/
10
;
System
.
out
.
println
(
num
);
}
//
这种遍历方式 只能获取元素,不能修改元素
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
println
(
arr
[
i
]);
}
}
}
数组最值问题
public class
Sample
{
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
new
int
[]{
3
,
6
,
8
,
2
,
9
,
4
,
5
,
1
,
7
};
int
min
=
arr
[
0
];
int
max
=
arr
[
0
];
for
(
int
i
=
1
;
i
<
arr
.
length
;
i
++
) {
if
(
arr
[
i
]
<
min
) {
min
=
arr
[
i
];
}
if
(
arr
[
i
]
>
max
) {
max
=
arr
[
i
];
}
}
System
.
out
.
println
(
max
);
System
.
out
.
println
(
min
);
}
}
数组扩容问题
public class
Sample
{
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
new
int
[]{
1
,
2
,
3
,
4
,
5
};
arr
=
add
(
arr
,
6
);
arr
=
add
(
arr
,
6
);
arr
=
add
(
arr
,
6
);
arr
=
add
(
arr
,
6
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
println
(
arr
[
i
]);
}
}
//
在指定的数组
arr
中添加元素
element
public static
int
[]
add
(
int
[]
arr
,
int
element
) {
int
[]
newArr
=
new
int
[
arr
.
length
+
1
];
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
newArr
[
i
]
=
arr
[
i
];
}
newArr
[
newArr
.
length
-
1
]
=
element
;
return
newArr
;
}
}
选择排序算法
public class
Sample
{
//
选择排序
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
8
,
9
,
2
,
6
,
7
,
1
,
4
,
5
,
3
};
for
(
int
i
=
0
;
i
<
arr
.
length
-
1
;
i
++
) {
//-1 n
个数字没有第
n
轮
for
(
int
j
=
i
+
1
;
j
<
arr
.
length
;
j
++
) {
if
(
arr
[
i
]
>
arr
[
j
]) {
swap
(
arr
,
i
,
j
);
}
}
}
print
(
arr
);
}
//[1, 2, 3, 4, 5]
public static
void
print
(
int
[]
arr
) {
System
.
out
.
print
(
"["
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
print
(
arr
[
i
]);
if
(
i
==
arr
.
length
-
1
) {
System
.
out
.
println
(
"]"
);
}
else
{
System
.
out
.
print
(
", "
);
}
}
}
public static
void
swap
(
int
[]
arr
,
int
i
,
int
j
) {
int
temp
=
arr
[
i
];
arr
[
i
]
=
arr
[
j
];
arr
[
j
]
=
temp
;
}
}
冒泡排序算法
public class
Sample
{
//
冒泡排序
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
8
,
9
,
2
,
6
,
7
,
1
,
4
,
5
,
3
};
for
(
int
i
=
0
;
i
<
arr
.
length
-
1
;
i
++
) {
//-1
表示
n
个数字只有
n-1
轮
for
(
int
j
=
0
;
j
<
arr
.
length
-
1
-
i
;
j
++
) {
//-1
避免重复比较
(
当前最大和上一
轮最大
)
if
(
arr
[
j
]
>
arr
[
j
+
1
]) {
swap
(
arr
,
j
,
j
+
1
);
}
}
}
print
(
arr
);
}
public static
void
print
(
int
[]
arr
) {
System
.
out
.
print
(
"["
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
print
(
arr
[
i
]);
if
(
i
==
arr
.
length
-
1
) {
System
.
out
.
println
(
"]"
);
}
else
{
System
.
out
.
print
(
", "
);
}
}
}
public static
void
swap
(
int
[]
arr
,
int
i
,
int
j
) {
int
temp
=
arr
[
i
];
arr
[
i
]
=
arr
[
j
];
arr
[
j
]
=
temp
;
}
}
插入排序算法
public class
Sample
{
//
插入排序
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
8
,
9
,
2
,
6
,
7
,
1
,
4
,
5
,
3
};
for
(
int
i
=
1
;
i
<
arr
.
length
;
i
++
) {
int
e
=
arr
[
i
];
int
j
=
0
;
for
(
j
=
i
;
j
>
0
&&
arr
[
j
-
1
]
>
e
;
j
--
) {
arr
[
j
]
=
arr
[
j
-
1
];
}
arr
[
j
]
=
e
;
}
print
(
arr
);
}
public static
void
print
(
int
[]
arr
) {
System
.
out
.
print
(
"["
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
print
(
arr
[
i
]);
if
(
i
==
arr
.
length
-
1
) {
System
.
out
.
println
(
"]"
);
}
else
{
System
.
out
.
print
(
", "
);
}
}
}
}
计数排序算法
public class
Sample
{
//
计数排序
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
-
2
,
9
,
-
1
,
12
,
8
,
-
3
,
6
,
7
,
4
,
5
,
2
,
1
,
0
,
8
,
6
,
7
,
4
,
-
3
,
-
2
,
-
1
,
-
1
,
7
};
int
min
=
arr
[
0
];
int
max
=
arr
[
0
];
//O(n)
for
(
int
i
=
1
;
i
<
arr
.
length
;
i
++
) {
if
(
arr
[
i
]
<
min
) {
min
=
arr
[
i
];
}
if
(
arr
[
i
]
>
max
) {
max
=
arr
[
i
];
}
}
int
[]
temp
=
new
int
[
max
-
min
+
1
];
//
对应关系
index = number - min number = index + min
//O(n)
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
temp
[
arr
[
i
]
-
min
]
++
;
}
//temp[index]
表示
index
对应的数字
number
出现的次数
int
k
=
0
;
//O(n)
for
(
int
index
=
0
;
index
<
temp
.
length
;
index
++
) {
while
(
temp
[
index
]
!=
0
) {
arr
[
k
]
=
index
+
min
;
k
++
;
temp
[
index
]
--
;
}
}
print
(
arr
);
}
public static
void
print
(
int
[]
arr
) {
System
.
out
.
print
(
"["
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
print
(
arr
[
i
]);
if
(
i
==
arr
.
length
-
1
) {
System
.
out
.
println
(
"]"
);
}
else
{
System
.
out
.
print
(
", "
);
}
}
}
}
基数排序算法
import
java
.
util
.
LinkedList
;
public class
Sample
{
//
基数排序
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
102
,
203
,
321
,
13
,
12
,
78
,
96
,
34
,
37
,
28
,
6
,
8
,
5
,
6
};
//1.
先找到最大值 决定轮数
int
max
=
arr
[
0
];
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
if
(
arr
[
i
]
>
max
) {
max
=
arr
[
i
];
}
}
int
radex
=
(
max
+
""
).
length
();
//2.
创建十个桶 每一个桶是
LinkedList
LinkedList
<
Integer
>
[]
queues
=
new
LinkedList
[
10
];
for
(
int
i
=
0
;
i
<
queues
.
length
;
i
++
) {
queues
[
i
]
=
new
LinkedList
<
Integer
>
();
}
//3.
进行数字分类和规整
//r=0
个位
r=1
十位
r=2
百位
...
for
(
int
r
=
0
;
r
<
radex
;
r
++
) {
//
先按照
r
进行分类
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
int
index
=
getIndex
(
arr
[
i
],
r
);
//
获取数字的
r
位 返回该数字要去的桶的角标
0~9
queues
[
index
].
offer
(
arr
[
i
]);
}
//
然后在重新规整到
arr
里
int
k
=
0
;
for
(
int
index
=
0
;
index
<
queues
.
length
;
index
++
) {
while
(
!
queues
[
index
].
isEmpty
()) {
arr
[
k
++
]
=
queues
[
index
].
poll
();
}
}
}
print
(
arr
);
}
public static
int
getIndex
(
int
number
,
int
r
) {
//123 r=0
//123 r=1
//123 r=2
int
index
=
0
;
for
(
int
i
=
0
;
i
<=
r
;
i
++
) {
index
=
number
%
10
;
number
/=
10
;
}
return
index
;
}
public static
void
print
(
int
[]
arr
) {
System
.
out
.
print
(
"["
);
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++
) {
System
.
out
.
print
(
arr
[
i
]);
if
(
i
==
arr
.
length
-
1
) {
System
.
out
.
println
(
"]"
);
}
else
{
System
.
out
.
print
(
", "
);
}
}
}
}
二分查找算法(注:所查找的数据必须是有序的)
public class
Sample
{
//
二分查找
public static
void
main
(
String
[]
args
) {
int
[]
arr
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
};
int
min
=
0
;
int
max
=
arr
.
length
-
1
;
int
mid
=
(
min
+
max
)
/
2
;
int
key
=
10
;
while
(
arr
[
mid
]
!=
key
) {
if
(
key
<
arr
[
mid
]) {
max
=
mid
-
1
;
}
if
(
arr
[
mid
]
<
key
) {
min
=
mid
+
1
;
}
if
(
min
>
max
) {
mid
= -
1
;
break
;
}
mid
=
(
min
+
max
)
/
2
;
}
System
.
out
.
println
(
mid
);
}
}
可变长参数列表
public class
Sample
{
public static
void
main
(
String
[]
args
) {
show
(
1
);
show
(
1
,
2
,
3
);
show
(
"hehe"
,
"lala"
,
"haha"
,
"xixi"
,
"heihei"
);
}
public static
void
show
(
int
...
nums
) {
for
(
int
i
=
0
;
i
<
nums
.
length
;
i
++
) {
System
.
out
.
print
(
nums
[
i
]
+
" "
);
}
System
.
out
.
println
();
}
public static
void
show
(
String
...
strs
) {
for
(
int
i
=
0
;
i
<
strs
.
length
;
i
++
) {
System
.
out
.
print
(
strs
[
i
]
+
" "
);
}
System
.
out
.
println
();
}
}