数据结构-前置概念 - 指南
时间和空间复杂度
时间复杂度
概念
算法的时间复杂度是一个科学函数,定量描述了算法的运行时间。算法中基本操作的执行次数就是算法的时间复杂度
大O的渐进表示法
用常数1取代运行时间中所有的加法常数,仅保留最高阶项,且最高阶项的系数恒为1
有些算法的的时间复杂度存在最好,平均和最坏情况(默认关注情况)。
计算时间复杂度:
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; }
递归的时间复杂度=递归的次数*每次递归的执行次数
long factorial(int N) { return N < 2 ? N : factorial(N-1) * N; }O(N)->忽略-1
int fibonacci(int N) {
return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);
}
O(2^N)
总结:计算时间复杂度要结合思想,不能一味的观察代码
空间复杂度
对一个算法在运行过程中临时占用储存空间大小的量度,计算的是变量的个数(额外的内存),也是用大O渐进表示法
1.冒泡排序的空间复杂度是O(1)
2.
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; }O(N)
3.
long factorial(int N) { return N < 2 ? N : factorial(N-1)*N; }每一次递归都会给函数开辟内存
O(N)
常遇到复杂度有O(1),O(logN),O(N),O(N*logN),O(N^2) 未特殊声明,log都是以2为底
包装类
在Java中由于基本类型不是继承于Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型

除了Integer和Character,其余基本类型的包装类都是首字母大写
装箱/装包:把基本数据类型变为包装类型
int a=10;
Integer a1=Integer.valueOf(a);//显式装箱
Integer a2=new Integer(a);
Integer a3=a;//隐式装箱
Integer a4=(Integer)a;
拆箱/拆包:包装类型变为基本数据类型
int a=10;
Integer a1=Integer.valueOf(a);
int a2= a1.intValue();//显式拆箱
int a3=a1;//自动拆箱
int a4=(int)a1;
面试题:
Integer a=100; Integer b=100; System.out.println(a==b); Integer c=200; Integer d=200; System.out.println(c==d);
原因:
Integer装箱的源码:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }其中IntegerCache.low =-128 IntegerCache.higt =127
可以发现,凡是符合[-128,127]的256个数据都储存在了数组里,那么a和b的地址就是一样的,c和d的地址就不一样
想要比较值可以用.equals()
泛型
泛型是JDK1.5引入的新的语法,通俗讲就是:适用于许多许多类型。从代码上讲,就是对类型实现了参数化
引出泛型
泛型的主要目的是:指定当前容器,要持有什么类型的对象。让编译器去做检查
语法
声明:
class 泛型类名称<类型形参列表> {
// 这里可以使用类型参数
}
class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
使用:
泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象
MyArray list = new MyArray();
裸类型:
表示一个泛型类但是没有带着类型实参
MyArray list = new MyArray();这是为了兼容老版本的API所保留的机制,我们不要去主动的使用
class Myarray{
public Object[] array=new Object[10];
public void setArray(int pos,T value){
array[pos]=value;
}
public T getArray(int pos){
return (T)array[pos];
}
}
public static void main(String[] args){
Myarray myarray1 =new Myarray();
myarray1.setArray(0,22);
Myarray myarray2 =new Myarray();
myarray2.setArray(0,"Hello");
String str=(String)myarray2.getArray(0);
}
注意:
1.类名后<T>表示占位符,表示当前类是一个泛型类,类型必须是包装类
2.泛型是将数据类型参数化,进行传递
3.new 对象时后面<>可以不写类型
4.泛型目前为止的优点是:数据类型参数化,编译时自动进行类型检查和转换
了解:类型的形参一般使用一个大写字母表示
E:Element
K:Key
V:Value
N:Number
T:Type
泛型是如何编译的
擦除机制
在编译过程中将所有的T替换为Object这种机制称为擦除机制,以下是介绍https://zhuanlan.zhihu.com/p/51452375
https://zhuanlan.zhihu.com/p/51452375
此时尖括号当中的内容不参与类型的组成
Myarray myarray1 =new Myarray();
Myarray myarray2 =new Myarray();
myarray1和myarray2的类型就是Myarray类型
问题解答:
问:T[] ts =new T[5];编译T替换为Object,不是相当于:Object[] ts = new Object[5]吗?
答:1.Java在运行时会严格检查元素特性,而泛型编译后会发生“类型擦除”,泛型T的具体类型信息会被丢弃(如果没有指定边界,那被擦除为Object),这意味着创建数组时无法知道T是什么类型
问:类型擦除,一定是把T变成Object吗?
答:不一定。无界类型擦除为Object,有界类型擦除为对应的上界,多边界泛型擦除为第一个上界类型
泛型的上界
class 泛型类名称<类型形参 extends 类型边界> {
...
}
//
public class MyArray {
...
}
此时只接受Number或者Number的子类作为E的类型实参
MyArray l1; // 正常,因为 Integer 是 Number 的子类型
MyArray l2; // 编译错误,因为 String 不是 Number 的子类型
没有指定E的边界那么可以视为 E extends Object
复杂示例:
public class Person implements Comparable{
public String name;
public int age;
public int compareTo(Person o){
return this.name.compareTo(o.name);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Max>{
public T toMax(T[] arrays){
T tmp=arrays[0];
for(int i=1;i max=new Max<>();
Integer i=max.toMax(array);
Max max2=new Max<>();
System.out.println(max2.toMax(ps));
System.out.println(i);
}
}
泛型方法
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }
//静态的泛型方法 需要在static后用<>声明泛型类型参数
class Max{
public static> T toMax(T[] arrays){//静态方法
T tmp=arrays[0];
for(int i=1;itoMax(ps));//不使用类型推导
}
}
JDK17新增的特性
var关键字
从Java10开始,var被引用,用于局部变量
var num =10;
var str="Hello";
自动识别后面的类型,更加简洁
注意:var不能声明字段(属于类的变量),方法参数,返回值且必须初始化(不能初始化为null)
密封类
sealed修饰也表示密封类
sealed class Animal permits Dog, Dog1 {
}
//Dog1也是密封类
final class Dog1 extends Animal{
}
//Dog无限制,任何类都可继承
non-sealed class Dog extends Animal{
}
sealed修饰的类必须有子类,而且子类必须是final,sealed,non-sealed
没有写permits表示都允许
接口的私有方法
Java8,接口可以有默认方法,Java9之后,接口内可以实现私有方法,仅为接口提供服务
instanceof
允许在判断类型的同时,声明一个变量
Object obj="Hello";
if(obj instanceof String str){
str="Hello";
}





浙公网安备 33010602011771号