一.时间复杂度
定义:
算法的时间复杂度是一个数学函数,算法中的基本操作的执行次数,为算法的时间复杂度。
O渐进表示方法:
原因:
计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要
大概执行次数,那么这里我们使用大O
的渐进表示法。
举例练习:
// 计算func2的时间复杂度?
void func2(int N) {
int count = 0;
for (int k = 0; k < 2 * N ; k++) {
count++;
}
int M = 10;
while ((M--) > 0) {
count++;
}
System.out.println(count);
}
计算func3的时间复杂度?
void func3(int N, int M) {
int count = 0;
for (int k = 0; k < M; k++) {
count++;
}
for (int k = 0; k < N ; k++) {
count++;
}
System.out.println(count);
}
// 计算func4的时间复杂度?
void func4(int N) {
int count = 0;
for (int k = 0; k < 100; k++) {
count++;
}
System.out.println(count);
}
// 计算bubbleSort的时间复杂度?
void bubbleSort(int[] array) {
for (int end = array.length; end > 0; end--) {
boolean sorted = true;
for (int i = 1; i < end; i++) {
if (array[i - 1] > array[i]) {
Swap(array, i - 1, i);
sorted = false;
}
}
if (sorted == true) {
break;
}
}
}
// 计算binarySearch的时间复杂度?
int binarySearch(int[] array, int value) {
int begin = 0;
int end = array.length - 1;
while (begin <= end) {
int mid = begin + ((end-begin) / 2);
if (array[mid] < value)
begin = mid + 1;
else if (array[mid] > value)
end = mid - 1;
else
return mid;
}
return -1;
}
// 计算阶乘递归factorial的时间复杂度?
long factorial(int N) {
return N < 2 ? N : factorial(N-1) * N;
}
// 计算斐波那契递归fibonacci的时间复杂度?
int fibonacci(int N) {
return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);
}
分析:
1.
实例
1
基本操作执行了
2N+10
次,通过推导大
O
阶方法知道,时间复杂度为
O(N)
2.
实例
2
基本操作执行了
M+N
次,有两个未知数
M
和
N
,时间复杂度为
O(N+M)
3.
实例
3
基本操作执行了
100
次,通过推导大
O
阶方法,时间复杂度为
O(1)
4.
实例
4
基本操作执行最好
N
次,最坏执行了
(N*(N-1))/2
次,通过推导大
O
阶方法
+
时间复杂度一般看最坏,时间复杂度为 O(N^2)
5.
实例
5
基本操作执行最好
1次,最坏 log(以2为低)n次,时间复杂度为 O(log(以2为低)n ) ps:
在算法分析中表示是底数
为
2
,对数为
N
,有些地方会写成
lgN。
6.
实例
6
通过计算分析发现基本操作递归了
N
次,时间复杂度为
O(N)
。
7.
实例
7通过计算分析发现基本操作递归了2的n次方次,时间复杂度为O( 2的n次方)。
二.空间复杂度
定义:
空间复杂度是对一个算法在运行过程中
临时占用存储空间大小的量度
。空间复杂度不是程序占用了多少
bytes
的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也使用大
O
渐进表示法
。
举例练习:
// 计算bubbleSort的空间复杂度?
void bubbleSort(int[] array) {
for (int end = array.length; end > 0; end--) {
boolean sorted = true;
for (int i = 1; i < end; i++) {
if (array[i - 1] > array[i]) {
Swap(array, i - 1, i);
sorted = false;
}
}
if (sorted == true) {
break;
}}}
// 计算fibonacci的空间复杂度?
int[] fibonacci(int n) {
long[] fibArray = new long[n + 1];
fibArray[0] = 0;
fibArray[1] = 1;
for (int i = 2; i <= n ; i++) {
fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
}
return fibArray;
}
// 计算阶乘递归Factorial的空间复杂度?
long factorial(int N) {
return N < 2 ? N : factorial(N-1)*N;
}
分析:
1.
实例
1
使用了常数个额外空间,所以空间复杂度为
O(1)
2.
实例
2
动态开辟了
N
个空间,空间复杂度为
O(N)
3.
实例
3
递归调用了
N
次,开辟了
N
个栈帧,每个栈帧使用了常数个空间。空间复杂度为
O(N)
三.包装类
定义:
在
Java
中,由于基本类型不是继承自
Object
,为了在泛型代码中可以支持基本类型,
Java
给每个基本类型都对应了一个包装类型。(为了让基本类型也面向对象)
基本类型=>包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
(
除了
Integer
和
Character
, 其余基本类型的包装类都是首字母大写。)
装箱与拆箱:
int
i
=
10
;
//
装箱操作,新建一个
Integer
类型对象,将
i
的值放入对象的某个属性中
Integer
ii
=
Integer
.
valueOf
(
i
);
Integer
ij
=
new
Integer
(
i
);
//
拆箱操作,将
Integer
对象中的值取出,放到一个基本数据类型中
int
j
=
ii
.
intValue
();
注意:自动装箱:
Integer ii = i; // 自动装箱
Integer ij = (Integer)i; // 自动装箱
int j = ii; // 自动拆箱
int k = (int)ii; // 自动拆箱
注:
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
}
//true false
原因:在变量位于-128到125时是直接赋值,其他情况上就不是了
四.认识泛型
泛型的主要目的:
就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传入什么类型。
语法:
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> {
}
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分类型参数
}
代码练习:
//实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数
//组中某个下标的值?
class MyArray<T> {
public T[] array = (T[])new Object[10];//1
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
}
public class TestDemo {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();//2
myArray.setVal(0,10);
myArray.setVal(1,12);
int ret = myArray.getPos(1);//3
System.out.println(ret);
myArray.setVal(2,"bit");//4
}
}
代码解释:
1.
类名后的
<T>
代表占位符,表示当前类是一个泛型类
了解:
【规范】类型形参一般使用一个大写字母表示,常用的名称有:
E
表示
Element
K
表示
Key
V
表示
Value
N
表示
Number
T
表示
Type
S, U, V
等等
-
第二、第三、第四个类型
2.注释
1
处,不能
new泛型类型的数组
T
[]
ts
=
new
T
[
5
];
//
是不对的
3.
注释
2
处,类型后加入
<Integer>
指定当前类型
4.
注释
3
处,不需要进行强制类型转换
5.
注释
4
处,代码编译报错,此时因为在注释
2
处指定类当前的类型,此时在注释
4
处,编译器会在存放元素的时
候帮助我们进行类型检查。