数据结构前置知识
集合框架
类和接口总览
概念
Java 集合框架 Java Collection Framework
,又被称为容器container
,是定义在 java.util
包下的一组接口 interfaces
和其实现类classes
。
主要表现
其主要表现为将多个元素 element
置于一个单元中,用于对这些元素进行快速、便捷的存储 store
、检索 retrieve
、 管理 manipulate
,即平时我们俗称的增删查改 CRUD
。
eg.一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)、一个通讯录(一组姓名和电话的映射关系)等等。
时间和空间复杂度
时间效率
概念
被称为时间复杂度,是一个数学函数。算法中的基本操作的执行次数,为算法的时间复杂度。
【实例1】
// 请计算一下func1基本操作执行了多少次?
void func1(int N){
int count = 0;//一次,不计
for (int i = 0; i < N ; i++) {
for (int j = 0; j < N ; j++) {
count++;//(N*N)次
}
}
for (int k = 0; k < 2 * N ; k++) {
count++;//(2*N)次
}
int M = 10;
while ((M--) > 0) {
count++;//M次
}
System.out.println(count);
}
Func1 执行的基本操作次数 :
实际中计算时间复杂度时,其实并不一定要计算精确的执行次数,而只需要大概执行次数,使用大O的渐进表示法。
大O阶方法
-
用常数1取代运行时间中的所有加法常数。
-
在修改后的运行次数函数中,只保留最高阶项
-
如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法后,Func1的时间复杂度为:
【实例2】
//计算func2的时间复杂度?
void func2(int N) {
int count = 0;
for (int k = 0; k < 100; k++) {
count++;
}
System.out.println(count);
}
实例3基本操作执行了100次,通过推导大O阶方法,时间复杂度为 O(1)
【实例3】
// 计算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;
}
}
}
时间复杂度的计算一定要激活代码的思想,而不能单纯只看代码。
实例3基本操作执行最好N次,最坏执行了(N*(N-1))/2次* [等差数列:n-1 + n-2 + n-3 + … + 1],通过推导大O阶方法+时间复杂度一般看最坏,时间 复杂度为 O(N^2)
【实例4】
// 计算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;
}
二分查找最坏的情况:剩下最后一个才被找到
实例4基本操作执行最好1次,最坏O(log2N)次,时间复杂度为 O( log2N)
在算法分析中表示是底数为2,对数为N,有些地方会写成lgN。
建议通过折纸查找的方式理解logN是怎么计算出来的,因为二分查找每次排除掉一半的不适合值,一次二分剩下:n/2 ,两次二分剩下:n/2/2 = n/4)
【实例5】
// 计算阶乘递归Factorial的空间复杂度?
long factorial(int N) {//递归的次数:N-1
return N < 2 ? N : factorial(N-1)*N;
}
递归的时间复杂度 = 递归的次数 * 每次递归后执行的次数
实例5时间复杂度为O(N)
【实例6】
// 计算阶乘递归Factorial的空间复杂度?
long factorial(int N) {
return N < 2 ? N : factorial(N-1)*factorial(N-2);
}
递归次数(等比数列求和):1 + 2 + 4 + … = 2^0 + 2^1 + 2^2 + … + 2^(n-1) = 2^n
实例6时间复杂度为O(2^N)
空间效率
概念
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。
空间复杂度不是程序占用了多少bytes的空 间,没太大意义,所以空间复杂度算的是变量的个数。
【实例7】
// 计算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;
}
}
}
实例7使用了常数个额外空间,所以空间复杂度为 O(1)
【实例8】
// 计算阶乘递归Factorial的空间复杂度?
long factorial(int N) {
return N < 2 ? N : factorial(N-1)*N;
}
实例8递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)
以后见到的比较多的复杂度:O(logN) O(N) O(NlogN) O(N^2)
包装类
在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了 一个包装类型。
基本数据类型和对应的包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Characater |
boolean | Boolean |
除了 Integer 和 Character, 其余基本类型的包装类都是首字母大写。
装箱和拆箱(装包和拆包)
装箱
Integer a = 10;//装包 自动装箱
int i = 99;
Integer b = i;//基本类型给转变为包装类型
System.out.println(a);//输出10
System.out.println(b);//输出99
Integer aa = Integer.valueOf(10);//显示装箱
拆箱
Integer a = 10;
int i = a;
System.out.println(i);//输出10
int aa = a.intValue();//显示拆箱
double d = a.doubleValue();//拆成double类型
System.out.println(d);//输出10.0
【面试题】
Integer a = 100;
Integer b = 100;
System.out.println(a == b);//输出true
Integer a1 = 200;
Integer b1 = 200;
System.out.println(a1 == b1);//输出false
看源码
low = -128 high = 127
i = [-128,127]