【数据结构Day2】时间复杂度、空间复杂度和泛型

数据结构:计算机如何存储数据,ds关心的是如何高效进行数据的读写。

算法:在特定的数据集上(不关心怎么进行具体的数据读写),如何利用数据完成特定功能。算法本质上就是一系列运算的先后集合。

如何评价一个算法的好坏?

评价一个算法好坏的两种维度:时间效率与空间效率。

时间复杂度

时间复杂度主要衡量一个算法的运行速度(不是计算机运行的实际速度,是一种理论值,是在所有计算机上的通用描述)。

时间复杂度不是具体的时间,在计算机领域实际上是一个数学函数,算法中的基本操作的执行次数,为算法的时间复杂度。

因为不同计算机的硬件不同,相同的算法在不同的CPU、内存这些硬件上具体执行的时间都会有差异,而且差异可能会很大。

实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,使用大O的渐进表示法。 大O符号(Big O notation):是用于描述函数渐进行为的数学符号。

推导大O阶方法

  1. 用常数1取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,只保留最高阶项。
  3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

先看最高阶在哪,低阶全部省略,若没有最高阶变量参与,统一都是常数1。

一个算法的时间复杂度存在最好、最坏、平均时间复杂度。在实际中一般情况关注的是算法的最坏运行情况。

最坏:任意输入的数据,最大的执行次数(算法的上界)

最好:任意输入的数据,最小的执行次数(算法的下界)

平均:任意输入的数据,平均的执行次数

例:在一个长度为N的数组中搜索一个数据x

最坏:若x恰好在数组末尾或没有该数据,需要将整个数组遍历结束,执行N次才有结果

最好:若x恰好在数组首元素,执行1次就可以得到结果

平均:(1 + 2 +3 +...  +N)/ N= (1 + N ) * N / 2N = 1/2 * N = O(N)

空间复杂度

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。

所谓的额外空间。

空间复杂度描述一个算法额外开辟的临时变量的个数,同样用大O渐进法表示空间复杂度。

泛型

什么是泛型

一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的 代码,这种刻板的限制对代码的束缚就会很大。----- 来源《Java编程思想》对泛型的介绍。

泛型是在JDK1.5引入的新的语法,通俗讲,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。

泛型的主要目的:就是指定当前的容器,要持有什么类型的对象就让编译器去做检查。此时,就需要把类型作为参数传递。

泛型在定义阶段不明确具体类型,产生对象时确定具体类型。

泛型和Object类型最大的区别在于,Object转为其他类型都需要强转,只要强转都有出错的风险。泛型的出现就是为了解决该问题,有泛型不需要进行类型转换,编译阶段就可以校验设置的类型是否是产生对象时的类型——泛型是一种更加安全的机制。

泛型的语法

泛型类的定义

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.类名后的 代表占位符,表示当前类是一个泛型类

了解: 【规范】类型形参一般使用一个大写字母表示,常用的名称有:

  • E 表示 Element
  • K 表示 Key
  • V 表示 Value
  • N 表示 Number
  • T 表示 Type
  • S, U, V 等等 - 第二、第三、第四个类型

2.注释1处,不能new泛型类型的数组 

3. 注释2处,类型后加入 指定当前类型

4. 注释3处,不需要进行强制类型转换

5. 注释4处,代码编译报错,此时因为在注释2处指定类当前的类型,此时在注释4处,编译器会在存放元素的时候帮助我们进行类型检查。

 泛型方法的定义

权限修饰符 <l类型参数> 方法返回值 方法名称(形参列表) { ... }

public <T> T FUN(T t){

}

小结: 

  1. 泛型是将数据类型参数化,进行传递
  2. 使用<T>表示当前类是一个泛型类。
  3. 泛型目前为止的优点:数据类型参数化,编译时自动进行类型检查和转换  
  4. 当泛型类和泛型方法同时存在时,泛型方法始终以自己的类型参数为准

泛型的注意点

  1. 泛型只能用在成员域,不能用在静态域
  2. 产生泛型对象时,具体的类型不能使用基本数据类型,要用基本类型的话统一使用包装类
  3. 不能直接创建和实例化泛型数组,要使用泛型数组,统一使用Object数组

这三个问题的本质在于泛型只存在于编译阶段,运行阶段没有泛型——类型擦除

类型擦除:泛型的信息只存在于编译阶段,进入JVM前,有关泛型的的所有信息会被编译器擦除。

一般的泛型会擦除为Object类型,若存在泛型上限,则擦除为对应的泛型上限。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值