数据结构(Java)——Day1(时空复杂度和泛型)

一、复习部分

 

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

2. 算法:在特定的数据集上,如何利用数据完成特定的功能。本质上是一系列运算的集合。

二、时间复杂度和空间复杂度

1. 作用:评价一个算法的好坏的两种维度

2. 时间复杂度:衡量一个算法的运行速度(理论值)

A. 利用大O表示法描述一个算法的基本操作的执行次数,就是时间复杂度

B. 大O表示法规则:当问题规模N趋于正无穷的时候,哪部分影响最大就保留

C. 时间复杂度求解:

a. 顺序执行的代码忽略

b. 计算循环或者递归的执行次数和n的关系

c. 如果有多层嵌套只需要关注最深层嵌套即可

D. 大O实际计算规则:

a. 用常数1来代替函数中所有的加法常数(只要是常数,常量,都是1,无论常量有多大,用1替代,哪怕常量1w,10w,统统都是1)因为常数和传入数值N变量毫无关系,此算法是一个稳定的时间效率算法,和变量无关

b. .只保留函数的最高阶项,所有的低阶省略不计

c. 若最高阶项存在,且系数不为1,统一都将最高阶的系数看做1。例如10*N^2 => N ^2

E. 例子:时间复杂度:O(N^2)

F. 一个算法的时间复杂度存在最高好,最坏,平均时间复杂度。平常衡量一个算法的时间复杂度一般看最坏时间复杂度(算法的上界)

a. 概念:

最坏(算法的上界):在最坏的情况下(执行次数最大),算法的时间复杂度

最好:在最好的情况下(执行次数最少),算法的时间复杂度

平均:所有可能输入等概率出现的时候,算法的平均时间复杂度

b. 例子: 顺序查找一个长度为N的数组中的一个数组x

最坏情况:数组查完还没找到或者x是数组最后一个元素。最好时间复杂度:O(N)

最好情况:第一次比对就找到了。最好时间复杂度:O(1)

平均情况:假设每一种输入的概率等可能为1/N. 则平均次数=1/N(1+2+3+...+N) = (N+1)/2 =O(N)

G. 几个算法的最坏时间复杂度分析

a. 例题1:时间复杂度为O(N+M)

解析:大O阶看的是问题规模变量的最高阶,若存在多个问题规模变量,都需要保留各个变量的最高阶。此时N和M都是问题规模变量,因此都需要保留最高阶,即为O(N+M)

一个小补充:图的邻接表的遍历的时间复杂度就是O(N+M),其中N是边长,M是顶点个数

b. 例题2:时间复杂度为O(1)

解析:这里的循环的执行次数为常量100和N无关,因此换算为大O阶为O(1)

c. 例题3:冒泡排序:时间复杂度 = 比较次数 + 交换次数

 d. 例题4:时间复杂度为O(N),O(1000*N) = O(N)

解析:

 e. 例题5:二分查找时间复杂度

 

注意1:小技巧1: 

 注意2:小技巧2:

增速快慢: 1 < log2(n) < n < n log2(n) < n^2 < n^3 < 2^n < n! < n^n

f. 例题六: 递归调用的时间复杂度: 将递归展开,看函数执行的次数

斐波那契数列为例

时间复杂度为 O(log2(N))。斐波那契数列的调用可以看作大致是一个完全二叉树,完全二叉树的高度如下,因此完全二叉树的节点个数为O(2^N), 因此时间复杂度为O(2^N)

 g. 例题七:时间复杂度:O(N)

3. 空间复杂度:衡量一个算法的正常执行所需要的额外空间(不是需要解决的问题的数据集占用的空间,而是为了解决该问题新开辟的空间)大小

A. 空间复杂度 = 递归的调用的深度(递归执行次数) + 存储参数的空间(临时变量,形参等等)。同样使用大O表示法

补充:为什么递归要分析调用的深度

补充2:一个做题技巧

B. 空间复杂度规则(重中之重):

C. 例子

a. 例子1:O(1),因为临时变量只有i , j , flag。操作的数组是待排序的数据集

b. 例题2:空间复杂度:O(N)

解析:开辟了一个长度为N+1的数组,和几个变量,用大O表示法即为O(N)数量级

 c. 例题3:递归函数的空间复杂度:O(N) + O(1) = O(N).前面是递归函数调用次数,后者是临时变量的开辟

 四、泛型

1. 泛型的引出(java是一个强类型语言: 定义一个变量的时候,必须定义其类型)

A. 场景:要定义一个坐标类Point{

        x;

        y;

}

坐标的x和y两个属性可能有一下三种数据类型

x = 10, y = 20; //int类型

x = "东经108°",y = "北纬39°";//String类型

x = 10.3,y = 30.6;//double类型

B. 解决方案:Object是java中的最高参数统一化,能接收所有的引用类型;有了包装类的自动拆装箱后,Object还能接收基本数据类型(自动拆装箱)。即为Object可以接收所有数据类型。如下图

C. 但是用Object做接收存在一些问题,如下示例:由于用户的不规范输入,导致类型转换异常,出现异常之后,程序就退出了,其他用户无法正常使用坐标类,且这个问题发生在运行阶段,编译阶段正确执行

 D. 结论:由于Object虽然可以接收所有类型,但是Object转为其他类型都需要进行强制类型转换,调用get方法的时候,需要根据具体类型,将Object强转为子类(隐藏的风险:类型转换异常,发生在运行阶段)

2. 上述结论的场景下,需要有一种新的机制,在定义类的时候,成员变量可以接收多种类型,但是在具体产生对象时明确类型,当有不同类型设置时,编译阶段就能发现错误,因此泛型应运而生

3. 泛型定义:JDK1.5后引入的泛型机制。所谓泛型,即在定义类或方法时,没有明确参数的类型,而是在使用该类的时候,明确类型。不需要进行类型强转,编译阶段就会在语法阶段检查类型是否匹配的机制。类型的编译截断守门员,不会让类型不匹配的问题进入到运行阶段

4. 泛型类的使用

(1)语法:泛型类的定义语法如下

注意:类型参数一般用大写的英文字母,实际上写什么都行

类名称<类型参数1,类型参数2...>{

        类型参数1 成员变量名称//

        类型参数2 成员变量名称//

        ...

}

(2)示例:一个类型参数的泛型类

A. 泛型类的定义

B. 泛型类的使用

(3)示例2:多个类型参数的泛型类(下面的例子中由于x和y的类型参数不同,因此在生成NewPoint对象的时候可以使得x和y的类型相同,也可以使得x和y的类型不相同)

 p1是不同类型

 p2是相同类型

 5. 关于类型参数命名的说法

(1)T:一般指代任何类

(2)E:一般指代元素Element,也有用来指代异常Exception

(3)K和V一般搭配使用,描述一个键值对的对象,K表示key(不重复),V表示value(可以重复)

6. 泛型方法

(1)语法:

权限修饰符 <类型参数> 方法返回值类型 方法名称(){

}

(2)注意1:这两个根据上述语法规定都不是泛型方法

技巧(重中之重):看是不是泛型方法就看有没有尖括号

 (3)示例

注意:泛型方法以自己的类型参数为准。也就是泛型方法中的T和泛型类中的T指代的不是一个。一般不会这么写,都是用不同的字母表示

 

 

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值