赞
踩
真题1:在面向对象方法中,两个及以上的类作为一个类的超类时,称为(多重继承),使用它可能造成子类中存在(二义性)的成员。
真题2:在面向对象方法中,多态指的是(客户类无需知道所调用方法的特定子类的实现)
真题3:多态有不同的形式,(过载)的多态是指同一个名字在不同上下文中所代表的含义不同。
类可以分为三种:实体类、接口类(边界类)和控制类。
实体类的对象表示现实世界中真实的实体,如人、物等。
接口类(边界类)的对象为用户提供一种与系统合作交互的方式,分为人和系统两大类,其中人的接口可以是显示屏窗口、Web 窗体、对话框、菜单、列表框、其他显示控制、条形码、二维码或者用户与系统交互的其他方法。系统接口涉及到把数据发送到其他系统,或者从其他系统接收数据。
控制类的对象用来控制活动流,充当协调者。
(3)抽象:通过**特定的实例抽取共同特征以后形成概念的过程。它强调主要特征,忽略次要特征。**一个对象是现实世界中一个实体的抽象,一个类是一组对象的抽象,抽象是一种单一化的描述,它强调给出与应用相关的特性,抛弃不相关的特性。
(4)封装:是一种信息隐蔽技术,将相关的概念组成一个单元模块,并通过一个名称来引用。面向对象封装是将数据和基于数据的操作封装成一个整体对象,对数据的访问或修改只能通过对象对外提供的接口进行。
(5)继承:表示类之间的层次关系(父类与子类),这种关系使得某类对象可以继承另外一类对象的特征,又可分为单继承和多继承。
(6)多态:**不同的对象收到同一个消息时产生完全不同的结果。包括参数多态(不同类型参数多种结构类型)、包含多态(父子类型关系)、过载多态(类似于重载,一个名字不同含义)、强制多态(强制类型转换)四种类型。**多态由继承机制支持,将通用消息放在抽象层,具体不同的功能实现放在低层。
(7)覆盖:子类在原有父类接口的基础上,用**适合于自己要求的实现去置换父类中的相应实现。**即在子类中重定义一个与父类同名同参的方法。
(8)函数重载:与覆盖要区分开,函数重载与子类父类无关,且函数是同名不同参数。
(8)绑定是一个把过程调用和响应调用所需要执行的代码加以结合的过程。在一般的程序设计语言中,绑定是在编译时进行的,叫作静态绑定。动态绑定则是在运行时进行的,因此,一一个给定的过程调用和代码的结合直到调用发生时才进行。
真题1:采用面向对象方法进行软件开发,在分析阶段,架构师主要关注系统的(行为)。
面向对象技术进行软件开发时,需要进行面向对象分析(OOA)、面向对象设计(OOD)、面向对象实现和面向对象测试几个阶段。
◆面向对象的分析:是为了确定问题域,理解问题。包含五个活动:认定对象组织对象、描述对象间的相互作用、确定对象的操作、定义对象的内部信息。主要关注系统的行为,明确系统需要提供什么服务。
◆面向对象需求建模:
◆面向对象的设计:是将采用面向对象技术将 OOA (面向对象分析)所创建的分析模型转化为设计模型,设计问题域的解决方案,与技术相关。OOD同样应遵循抽象、信息隐蔽、功能独立、模块化等设计准则。
◆面向对象的分析模型主要由顶层架构图、用例与用例图、领域概念模型构成; 设计模型则包含以包图表示的软件体系结构图、以交互图表示的用例实现图、完整精确的类图、针对复杂对象的状态图和用以描述流程化处理过程的活动图等。
体系结构 = 架构
面向对象实现(面向对象程序设计),是系统实现人员选用面向对象程序设计语言,采用对象、类及其他相关概念进行程序设计,实现系统。
◆面向对象的设计原则:
(1)**单一责任原则。**就一个类而言,应该仅有一个引起它变化的原因。即,当需要修改某个类的时候原因有且只有一个,让一个类只做一种类型责任
(2)开放-封闭原则。软件实体(类、模块、函数等)应该是可以扩展的,即开放的;但是不可修改的,即封闭的。
(3)**里氏替换原则。子类型必须能够替换掉他们的基类型。**即,在任何父类可以出现的地方,都可以用子类的实例来赋值给父类型的引用。
(4)**依赖倒置原则。抽象不应该依赖于细节,细节应该依赖于抽象。**即,高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
(5)**接口分离原则。**不应该强迫客户依赖于它们不用的方法。接口属于客户不属于它所在的类层次结构。即:依赖于抽象,不要依赖于具体,同时在抽象级别不应该有对于细节的依赖。这样做的好处就在于可以最大限度地应对可能的变化。(内部实现与接口是分离的)
(6)**共同重用。**在面向对象设计时,如果重用了包中的一个类,就要重用包中的所有类。
(7)**共同封闭。**进行面向对象系统设计时,针对包中的所有类对于同一类性质的变化。一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他包不造成任何影响。
真题1:
答案:C、B、D
真题2:UML类图通常不用于对(对象快照)进行建模。
UML 类图对系统的词汇、简单的协作、逻辑数据库模式进行建模。
对象快照采用对象图进行建模。
真题3:当UML状态用于对系统、类或用例的动态方面建模时,通常是对(反应型对象)建模。
真题4:有关状态图的叙述中不正确的是(状态由事件触发),正确的是(动作可以在状态内执行,也可以在状态转换时执行)。
◆UML(统一建模语言):是一种可视化的建模语言,而非程序设计语言,支持从需求分析开始的软件开发的全过程。
◆依赖: 一个事物的语义依赖于另一个事物的语义的变化而变化
◆关联: 是一种结构关系,描述了一组链,链是对象之间的连接。分为组合和聚合,都是部分和整体的关系,其中组合事物之间关系更强。两个类之间的关联,实际上是两个类所扮演角色的关联,因此,两个类之间可以有多个由不同角色标识的关联。
组合关系:部分和整体具有共同的生命周期,人和大脑,人死了,大脑也就不存在了。大脑不能单独独立于人而存在的。
聚合关系:一只大雁和雁群,一只大雁是可以离开雁群的。
◆泛化: 一般/特殊的关系,子类和父类之间的关系
◆实现:一个类元指定了另一个类元保证执行的契约。
以上六个图形、符号都要记住。
UML 图:
◆类图:静态图,为系统的静态设计视图,展现一组**对象、接口、协作和它们之间的关系。**UML 类图如下:
上图中的 聚集 画错了,不应该是实心。
◆对象图:静态图,展现**某一时刻一组对象及它们之间的关系,为类图的某一快照。**在没有类图的前提下,对象图就是静态设计视图,如下:
◆用例图:静态图,展现了一组**用例、参与者以及它们之间的关系。用例图中的参与者是人、硬件或其他系统可以扮演的角色;用例是参与者完成的一系列操作,用例之间的关系有扩展、包含、泛化。**如下:
扩展、包含、泛化是用例图特有的。
扩展: 当执行A操作之后,B操作可能做也可能不做,B不是必要的。图像的箭头是 B 指向 A比如查询到书籍信息后,如果查询到不一致,则可能需要去修改书籍信息;如果查询到一致,则不用去修改书籍信息。再比如支付付钱,如果支付宝余额里的钱够,就直接支付了;如果不够,则会从银行卡里先转钱,再支付。这里的银行卡转钱的操作就是可能做也可能不做的。
包含: 当执行A操作之前,必须先执行B操作。图像的箭头是 A 指向 B 比如要登记、查询外借信息,必须先用户登录,不登录就没有权限登记、查询。
**泛化:**表示父子关系。一般与特殊的关系。比如动物与小鸟;班级与二班;学生与小明等
◆序列图: 即顺序图,动态图,是场景的图形化表示,描述了以时间顺序组织的对象之间的交互活动。 有同步消息(进行阻塞调用,调用者中止执行,等待控制权返回,需要等待返回消息,用实心三角箭头表示)、异步消息(发出消息后继续执行,不引起调用者阻塞,也不等待返回消息,由空心箭头表示)、返回消息(由从右到左的虚线箭头表示)三种,如下:
◆通信图:动态图,**又叫协作图,强调参加交互的对象的组织。**如下:
◆状态图:动态图,展现了一个状态机,描述单个对象在多个用例中的行为,,包括简单状态和组合状态。转换可以通过事件触发器触发,事件触发后相应的监护条件会进行检查。状态图中转换和状态是两个独立的概念,如下:图中方框代表状态,箭头上的代表触发事件,实心圆点为起点和终点。
比如收音机 按下了开机,就处于开机状态;按下暂停,就处于暂停状态等。
按下开机 就是 事件触发器;
按下开机 就不一定处于开机状态,还需要满足一定的监护条件,比如按下开机 但是并没有插电,所以状态不会转换;
迁移就是一个状态到另一个状态。
真题1:
真题2:UML构件图专注于系统的静态(实现)图。
◆活动图:动态图,是一种特殊的状态图,展现了在系统内从一个活动到另一个活动的流程。活动的分岔和汇合线是一条水平粗线。牢记下图中并发分岔、并发汇合、监护表达式、分支、流等名词及含义。每个分岔的分支数代表了可同时运行的线程数。活动图中能够并行执行的是在一个分岔粗线下的分支上的活动
◆构件图(组件图): 静态图,为系统静态实现视图,展现了一组构件之间的组织和依赖。 如下:
供接口:半圆
需接口:完整的圆
◆部署图: 静态图,为系统静态部署视图,部署图物理模块的节点分布。它与构件图相关,通常一个结点包含一个或多个构件。其依赖关系类似于包依赖,因此部署组件之间的依赖是单向的类似于包含关系。如下:
涉及物理、硬件的就是部署图。
UML 类图
依赖、泛化(继承)、实现、关联、聚合与组合
强到弱依次为:组合>聚合>关联>依赖
。
泛化:表示父子关系。一般与特殊的关系。比如动物与小鸟;班级与二班;学生与小明等
泛化关系其实就是继承关系:指的是一个类(称为子类、子接口)继承(extends)另外的一个类(称为父类、父接口)的功能,并可以增加自己额外的一些功能,继承是类与类或者接口与接口之间最常见的关系;
在Java中此类关系通过关键字 extends明确标识。
在UML类图中,继承通常使用 空心三角+实线 表示
代码层面:
// 普通用户
public class User {
private Long id;
private String name;
}
// 员工
public class Employee extends User {
private BigDecimal salary;
}
// 客户
public class Customer ectends User {
private String address;
}
implements
明确标识。空心三角+虚线
表示代码层面
// 普通用户
public interface Vehicle {
// 抽象方法:行驶
void drive();
}
public class Car implements Vehicle {
private Engine engine;
public Car(Engine engien) {
this.engine = engine;
}
// 实现接口中的drive 方法
@Override
public void derive() {
entine.start();
}
}
代码层面
public class BClass{
}
public class AClass{
private BClass b1; // 依赖关系情况1:成员变量. 这也是关联关系
public void doWork(BClass b2){ // 依赖关系情况2: 方法参数
}
public void doWork(){
BClass b3; // 依赖关系情况3: 方法内的局部变量
}
}
public class EmployeeServiceImpl implements IEmployeeService{
private EmployeeMapper employeeMapper;
public PageResult query(QueryObject qo){
// TODO
return null;
}
}
不确定的有限自动机:NFA
上图可以这么理解:S0 输入 a 就变成了 S1,S0 输入 b 就变成了 S2
两个环(圆圈)就是结束状态。
确定的有限自动机就是:S0 输入 a 就一定变成 S1。
不确定的有限自动机就是:S0 输入 a 可能还是 S0,也可能输入 a 变成 S1.
NFA 和 DFA 等价转换方法:
ε 表示空串(即不需要任何输入),比如 上图:S0 输入 0 就会变为 S1,但 S1 可以通过空串 ε 不输入也能变为 S2。
上图中的 S2 和 S3 的变换是一个循环,体现了两个点:1、S2通过 1 变为 S3, 2、S3 通过空串可以转为 S2.
所以这个循环里必须包含 1 和 空串。
所以答案是 C。上图中DFA 中必须体现所有 NFA 中的输入:输入0可以变为其他、输入1也可以变为其他、通过1也有循环、起点终点输入为0
加了排它锁(也叫写锁,也叫X锁),则不能再加任何锁;
加了共享锁(也叫读锁,也叫S锁),则可以继续加读锁。
答案:A
第一范式:不可分
第二范式:非主属性完全依赖于任何一个候选码(而不是部分依赖于,即候选码完全决定非主属性)
第三范式:没有传递依赖(非主属性对码)
BCNF范式:主属性对于码没有部分函数依赖和传递依赖,即依赖关系的左边都包含候选键。
候选关键字的求法:
根据依赖集,找出从未在右边出现过的属性,必然是候选键之一(候选键可以唯一标识),以该属性为基础,根据依赖集依次扩展,看能否遍历所有属性,将无法遍历的加入候选键中。
1、找出从未在右边出现过的属性:
AB->C,CD->B,右侧中 A、D 都未在右侧出现过,所以 A、D必是候选键之一。
2、看1得出的候选键能否推出其他所有属性:
得知 AB 才能推出 C,CD 才能推出 B,所以A、D 不能推出其他。所以还需要加关键字。
3、添加关键字:
比如这里加 B,则 AB 可以推出 C,已经得到所有了,所以 A、D、B 为候选关键字。
比如这里加 C,则 CD 可以推出B,已经得到所有了,所以 A、D、C 为候选关键字。
所以答案为:有2个候选关键字 ACD 和 ABD
候选关键字中的所有属性都是主属性:所以 A、B、C、D都是主属性,所以有 0个非主属性和4个主属性。
第二题:
第二范式:任何非主属性都完全依赖于任何一个候选码,不存在部分函数依赖。
第三范式:不存在非主属性对于候选码的传递依赖
BCNF:每一个依赖左边决定因素都包含候选键或候选键之一。
所以要先算出候选键:
1、右边未出现过的:E、M
2、E、M 可以推出所有键,且只有(E,M)可以推出,所以候选键为(E、M)
3、N完全依赖于E,但候选键为(E,M)所以部分依赖于(E,M),Q完全依赖于 EM,L 完全依赖于 M,但候选键为(E,M)所以部分依赖于(E,M)。所以没有达到第二范式。
4、所以只达到了第一范式,没有达到第二范式。
所以关系模式达到了第一范式,需要进行分解,因为存在冗余、修改操作的不一致性、插入和删除异常。
解题关键:
第二范式:没有部分依赖
第三范式:没有传递依赖
BCNF范式:左侧决定因素都在候选码之中
真题1:属于典型被动攻击的是(会话拦截)
真题2:Kerberos 系统中可通过在报文中加入(时间戳)来防止重放攻击。
分为被动攻击和主动攻击。
被动攻击可以理解为:我攻击了你,但是你不知道。(比如给窃听数据:给数据加了个监听摄像头,不拦截、也不修改数据,只是窃听、监听等;非法登录:比如窃取了用户名密码,偷偷登录系统获取一些资料等)
主动攻击可以理解为:是破坏性攻击,攻击后你就知道你被攻击了。比如 假冒身份:本来是 A 给 B 发消息,结果 C 假冒了B得到了 A 的消息;抵赖:比如 A 给B发消息,结果A却否认发过消息;
**重放攻击(可能会考):**拦截信息,并利用这些信息重新发送。
假如客户端给服务器发送消息,消息为用户名、密码,比如用户名、密码是加密的了,一般来说需要解密才能知道,但是第三方解惑后不用解密,因为它知道了这是用户名、密码的信息,还用这些加密后的信息伪装成客户,去登录,重发用户名、密码给服务器。此时服务器拿到加密的用户名、密码并不知道这是伪装的,因为用户名、密码都是对的。这就是重放攻击。
解决重放攻击的方法也很简单,就是在信息里加上时间戳,比如真正的发消息的时间是1点,重放攻击的发消息的时间是2点,这个时候服务器就可以验证那个时间点的消息是真实的请求、发送。
拒绝服务(DOS)(重要、可能会考):
网站都是有一个负载的,也就是访问次数。不能无限制的访问的。
因此如果是正常的用户访问,但是是大量的正常用户同时访问,首先访问本身是合法的,但访问被限制了甚至拒绝了服务,这就是拒绝服务(DOS)。
所以拒绝服务(DOS)攻击就是一瞬间或短时间同时用大量(比如上百万)的用户同时访问进行的攻击,导致服务器没有负载了,网站就崩溃了。这个时候真正的用户去访问的时候也无法访问了。
流量分析属于被动攻击。
软件需求分为三大类(重要):
◆业务需求:反映企业或客户对系统高层次的目标要求,**通常来自项目投资人
客户、市场营销部门或产品策划部门。**通过业务需求可以确定项目视图和范围。(不懂开发的人,提出的业务需求,比如提出要手机上可以使用等)
◆用户需求:描述的是用户的具体目标,或用户要求系统必须能完成的任务。
即描述了**用户能使用系统来做什么。**通常采取用户访谈和问卷调查等方式,对用户使用的场景进行整理,从而建立用户需求。
◆系统需求:从系统的角度来说明软件的需求,包括功能需求、非功能需求和
设计约束等。
1)功能需求:也称为行为需求,规定了**开发人员必须在系统中实现的软件功能,**用户利用这些功能来完成任务,满足业务需要。
2)非功能需求:指系统必须具备的属性或品质,又可以细分为软件质量属性
(如可维护性、可靠性、效率等)和其他非功能需求。
3)设计约束:也称为限制条件或补充规约,通常是对系统的一些约束说明,例
如必须采用国有自主知识产权的数据库系统,必须运行在UNIX操作系统之下等。
质量功能部署(QFD)是一种将用户要求转换成软件需求的技术。QFD将软件需求分为三类:
(1)常规需求:用户认为系统应该做到的功能或性能,实现越多用户越满意。
(2)期望需求:用户想当然认为系统应该具备的功能或性能,但很难正确描述出来或没有写在需求说明书里、合同里的自己想要得到的这些功能或性能需求,比如它可能是常规性、常识性的东西,比如系统里要有复制粘贴功能等。比如它也可能是很高级、很难描述,但想要有的东西。如果期望需求没有得到实现,会让用户感到不满意。
(3)意外需求。意外需求也称之为兴奋需求,是用户要求范围外的功能或性能(但通常是软件开发人员很乐意赋予系统的技术特性),实现这些需求用户会更高兴,但不识实现也不影响其购买的决策。
答案:
1、B、用户需求(非常具体、细节,第一句和第三句是对应的。)
2、C、功能需求
3、A、业务需求(相对抽象,只说了能纠正,但没说怎么做)
真题1:某文件系统采用多级索引结构,若磁盘块的大小为1KB,每个块号需占3B,那么采用二级索引时的文件最大长度为(11628)KB。
解析:
块号:1024/3 = 341(取整)
一级:341 * 1KB = 341KB
二级:341 * 341 * 1KB = 116281KB
如图所示,系统中由13个索引节点,0-9为直接索引,即每个索引节点存放的是内容,假设每个物理盘大小为4KB,供可存放4KB * 10 = 40KB 数据。
10号索引节点为一级间接索引节点,大小为4KB,存放的并非直接数据,而是链接到直接物理盘块的地址,假设每个地址占4B,则共有 (4KB / 4B = 1024)1024个地址,对应1024个物理盘,可存 1024 * 4KB = 4096KB数据。
**二级索引节点类似,直接盘存放一级地址,一级地址再存放物理盘块地址,而后链接到存放数据的物理盘块,**容量又扩大了一个数量级,为 1024 * 1024 * 4KB 数据。
4字节 = 4B,1B=8bit
8个地址项,5个未直接索引,所以是 0 - 4 为直接索引,2个一级间接,1个二级间接
所以磁盘索引块:
直接索引:0~4
一级间接:5~(1KB/4B) * 2 + 5 - 1 = 5 ~ 516
二级间接:517~(1KB/4B) * (1KB*4B) + 517 - 1 = 517 ~ 256 * 256 + 517 - 1
上式中的 1KB为磁盘索引块的大小,因为这里计算的是逻辑块号的编号。
单个文件的最大长度则需要计算磁盘数据块的大小了(也为1KB):
直接索引大小:(4 - 0 + 1)* 1KB = 5KB
一级间接:(516 - 5 + 1)* 1KB = 512KB
二级间接:(256 * 256 + 517 - 1 - 517 + 1) = 256 * 256 KB = 65536KB
总共大小:5 + 512 + 65536 = 66053KB
真题1:配置管理包括(软件配置标识、变更管理、版本控制、系统建立、配置审核和配置状态报告)。
◆以下内容都可以作为配置项进行管理:外部交付的软件产品和数据、指定的内部软件工作产品和数据、指定的用于创建或支持软件产品的支持工具、供方/供应商提供的软件和客户提供的设备/软件。
◆典型配置项包括项目计划书、需求文档、设计文档、源代码、可执行代码、测试用例,运行软件所需的各种数据,它们经评审和检查通过后进入配置管理。
◆每个配置项的主要属性有:名称、标识符、文件状态、版本、作者、日期等
◆配置项可以分为基线配置项和非基线配置项两类,例如,基线配置项可能包括所有的设计文档和源程序等;非基线配置项可能包括项目的各类计划和报告等。
◆所有配置项的操作权限应由CMO(配置管理员)严格管理,基本原则是:基线配置项向开发人员开放读取的权限;非基线配置项向PM、CCB及相关人员开放。
◆配置项的状态可分为**“草稿”“正式”和“修改”三种。配置项刚建立时其状态为“草稿”。配置项通过评审后,其状态变为“正式”。此后若更改配置项,则其状态变为“修改”。当配置项修改完毕并重新通过评审时,其状态又变为“正式”。**如图所示:
◆配置项版本号:0 开头就是草稿,X.Y 为正式状态,X.YZ 为修改状态。
(1)处于**“草稿"状态的配置项的版本号格式为0.YZ**,YZ的数字范围为01~99。随着草稿的修正,YZ的取值应递增。YZ的初值和增幅由用户自己把握。
(2)处于**“正式"状态的配置项的版本号格式为X.Y**,X为主版本号,取值范围为1一9。Y为次版本号,取值范围为0~9。配置项第一次成为“正式”文件时,版本号为1.0。如果配置项升级幅度比较小,可以将变动部分制作成配置项的附件,附件版本依次为1.0,1.1…。当附件的变动积累到一定程度时,配置项的值可适量增加,Y值增加一定程度时,X值将适量增加。当配置项升级幅度比较大时,才允许直接增大x值。
(3)处于**“修改"状态的配置项的版本号格式为X.YZ**。配置项正在修改时,一般只增大Z值,X.Y值保持不变。当配置项修改完毕,状态成为“正式”时,将Z值设置为0,增加X.Y值。参见上述规则(2)。
◆配置项版本管理:在项目开发过程中,绝大部分的配置项都要经过多次的修改才能最终确定下来。对配置项的任何修改都将产生新的版本。由于我们不能保证新版本一定比旧版本“好”,所以不能抛弃旧版本。版本管理的目的是按照一定的规则保存配置项的所有版本避免发生版本丢失或混淆等现象,并且可以快速准确地查找到配置项的任何版本。
◆基线通常对应于开发过程中的里程碑,一个产品可以有多个基线,也可以只有一个基线。交付给外部顾客的基线一般称为发行基线(Release),内部开发使用的基线一般称为构造基线(Build)
◆一组拥有唯一标识号的需求、设计、源代码文卷以及相应的可执行代码、构造文卷和用户文档构成一条基线。
◆产品的一个测试版本(可能包括需求分析说明书、概要设计说明书、详细设计说明书、已编译的可执行代码、测试大纲、测试用例、使用手册等)是基线的一个例子。
◆建立基线还可以有如下好处:
(1)基线为开发工作提供了一个定点和快照。
(2)新项目可以在基线提供的定点上建立。新项目作为一个单独分支,将与随后对原始项目(在主要分支上)所进行的变更进行隔离。
(3)当认为更新不稳定或不可信时,基线为团队提供一种取消变更的方法
(4)可以利用基线重新建立基于某个特定发布版本的配置,以重现已报告的错误。
◆配置库存放配置项并记录与配置项相关的所有信息,是配置管理的有力工具。主要作用:
(1)记录与配置相关的所有信息,其中存放受控的软件配置项是很重要的内容
(2)利用库中的信息评价变更的后果,这对变更控制有着重要的意义。
(3)从库中可提取各种配置管理过程的管理信息
◆使用配置库可以帮助配置管理员把信息系统开发过程的各种工作产品,包括半成品或阶段产品和最终产品管理得井井有条,使其不致管乱、管混、管丢。
不属于产品组成部分工作成果的配置项:工作计划,而需求文档(需求阶段)、设计文档(设计阶段)、源代码(实施阶段)的产出物是产品组成部分工作成果的配置项。
真题1:
答案:A、B
Factory Method 工厂方法模式:只生产鼠标的工厂,可以生产、创造不同品牌的鼠标(对象),比如华为、小米等。子类决定实例化
Abstract Factory 抽象工厂模式:工厂概念的进一步抽象,有生产鼠标的工厂,有生产键盘的工厂,有生产屏幕的工厂等。工厂扩展后,就可以是鼠标工厂、键盘工厂、屏幕工厂。比如这里定义工厂具有 create 即 生产这个行为;需要鼠标工厂,就扩展工厂为 createMouse等。抽象接口
Builder 构建器模式:比如创建一个人物,要创建身体、脸型、发型进而组合成一个人物。通过提供创建不同身体(胖的、瘦的)、创建不同脸型(大的、小的)创建不同发型(长发、短发)等,进而组合成一个人物。类和构造分离
Prototype 原型模式:原来有个原型、对象,然后直接Copy 这个对象的代码,然后改一改变成新的对象、原型。 原型实例,拷贝。
Adapter 适配器模式:转换,兼容接口。
Bridage 桥接模式:抽象和实现分离。 比如学习资料,抽象层可以分为纸质资料、电子资料;具体的载体、实现可以是文字、图片。这样就在抽象和实现之间构建了一个桥梁:
这样就有了 纸质文字、纸质图片、电子文字、电子图片四种形式了。
抽象和实现是分开的,都可以单独扩展,比如扩展实现的增加一个视频形式等。
Composite 组合模式:整体-部分,树形结构。文件夹与文件、总公司与各地子公司。
Decorator 装饰模式:运行时、动态增加额外职责。
Facade 外观模式:对外统一接口。比如智能家居的一个家居模式,一个家居模式的接口、按钮,就可以把电视的接口、风扇的接口、电灯的接口一次唤起
Flyweight 享元模式:细粒度,共享。汉字、语音识别、输入法等,当要编写一句话的时候,不能每一句话中的每个字都单独创建一个对象,这个时候就把常见的比如2000个字创建2000个对象,当要写一句话的时候,就从共享的2000个对象中抽出 从而组合成这句话,这样就永远是2000个对象。
Proxy 代理模式:代理控制,比如软件的快捷方式。
Chain of Responsibility 职责链模式:传递请求、职责链接。比如请假流程,对个对象处理请求等。
Command 命令模式:日志记录、可撤销。
Interperter 解释器模式:解释器、虚拟机。比如游戏中自定义游戏地图、自定义游戏难度等
Iterator 迭代器模式:顺序访问,不暴露内部。
Mediator 中介者模式:不直接引用,对象交互。中介:买方、卖方、中介。ESB(企业服务总线) 就是中介者模式。
Memento 备忘录模式:保存、恢复。比如游戏的存档、读档。
Oserver 观察者模式:通知、自动更新。比如订阅微信公众号,公众号文章更新就会通知订阅它的人。
State 状态模式:状态变成类,比如会员模式:超级会员、普通会员等。会员等级发生改变时,对应的权限、行为也发生改变了。
Strategy 策略模式:算法替换
Template Method 模板方法模式:使用模板。
Visitor 访问者模式:数据和操作分离,比如人的、类的固有属性、数据结构是不会改变的:比如名字、身份证,但是行为是改变的:比如幼儿时是爬着的,长大了就是能走、能跑了。
真题:采用三级结构/两级映像的数据库体系结构,如果对数据库的一张表创建聚簇索引,改变的时数据库的(内模式)
创建聚簇索引意味着中心确定表中数据的物理顺序。
内模式:管理如何存储物理的数据,对应具体物理存储文件。
**模式:**又称为概念模式,就是我们通常使用的基本表,根据应用、需求将物理数据划分成一张张表。
**外模式:**对应数据库中的视图这个级别,将表进行一定的处理后再提供给用户使用。
**外模式-模式映像(逻辑上):**是表和视图之间的映射,存在于概念级和外部级之间,若表中数据发生了修改,只需要修改此映射,而无需修改应用程序。
**模式-内模式映像(物理上):**是表和数据的物理存储之间的映射,存在于概念级和内部级之间,若修改了数据存储方式,只需要修改此映射,而不需要去修改应用程序。
表:是行列组成,比如
年龄 | 性别 | 学历 |
---|---|---|
22 | 女 | 本科 |
25 | 男 | 硕士 |
27 | 女 | 博士 |
视图:是业务需要的通过 SQL 语句筛选、查询出来的表的数据的一部分,这部分正是业务需要的。即视图是表的一部分,比如业务需要知道表中女性员工的信息,于是得到:
年龄 | 性别 | 学历 |
---|---|---|
22 | 女 | 本科 |
27 | 女 | 博士 |
视图是一张假表,筛选、查询出来的临时表,修改此临时表,不会影响数据库中的表。
模式映像的目的是数据库数据修改后,业务/应用层数据不需要修改。即无论数据库数据怎么变,业务层的代码、SQL语句不需要变。因为数据库中的数据是频繁变动的。
事务:由系列操作组成,这些操作,要么全做,要么全不做,拥有四种特性,详解如下:
(操作)原子性:要么全做,要么全不做。比如做了查询等,突然断电了,则已经做的就全都撤销。
(数据)一致性:事务发生后数据是一致的,例如银行转账,不会存在A账户转出,但是B账户没收到的情况。
(执行)隔离性:任一事务的更新操作直到其成功提交的整个过程对其他事务都是不可见的,不同事务之间是隔离的,互不干涉。
(改变)持续性:事务操作的结果是持续性的。
真题:
答案:A、D、C
SQL 语法关键字:
创建表:create table;
指定主键 primary key();
指定外键 foreign key();
修改表 alter table;
删除表 drop table;
下图需要掌握:(考点)
GROUP BY 是跟 SELECT 中的内容对应的,比如 SELECT 零件号,ADV(库存量) as 平均库存量,则后面的 GROUP BY 后面的一定是 零件号,即 SELECT 中可用于分组的内容。
SELECT 中有 AVG、MAX、MIN 等,则后面一定是 GROUP BY,而不会是 ORDER BY。
答案:D、D
范式之间的转换一般都是通过拆分属性,即模式分解,将具有部分函数依赖和传递依赖的属性分离出来,来达到一步步优化,一般分为以下两种:
保持函数依赖分解:
对于关系模式R,有依赖集F,若对R进行分解,分解出来的多个关系模式,保持原来的依赖集不变,则为保持函数依赖的分解。另外,注意要消除掉冗余依赖(如传递依赖)。
实例:设原关系模式R(A,B,C),依赖集F(A->B,B->C,A->C),将其分解为两个关系模式R1(A,B)和R2(B,C),此时R1中保持依赖A->B,R2保持依赖B->C,说明分解后的R1和R2是保持函数依赖的分解,因为A->C这个函数依赖实际是一个冗余依赖,可以由前两个依赖传递得到,因此不需要管。
U1={A,B},保持了 A->B的依赖;但没有保持B->C的依赖;
所以不保持函数依赖;
无损分解:分解后的关系模式能够还原出原关系模式,就是无损分解,不能还原就是有损。
当分解为两个关系模式,可以通过以下定力判断是否无损分解:
定理:如果R的分解为p={R1,R2},F 为 R 所满足的函数依赖集合,分解p具有无损连接性的充分必要条件式R1∩R2->(R1-R2) 或者 R1∩R2->(R2-R1) .
当分解为**三个及以上关系模式时,可以通过表格法求解,**如下:
1、右边未出现过的:C、D
2、C、D 可以推出所有,候选键为 (C、D)
连接性:
R1∩R2->(R1-R2) :
R1∩R2 = ABCE ∩ CD = C
R1-R2 = ABCE - CD = ABE
C -> ABE ? 可以得到 C 是推不出来 ABE的
R1∩R2->(R2-R1)
R2-R1 = CD-ABCE = D
C -> D? C 也推不出来D
所以是有损连接。
函数依赖:
ABCE 保持了 A->E、AC->B、B->A,但没有保持 D->A,所以不保持函数依赖。
所以答案是:不具有无损连接性,也不保持函数依赖。
线性结构:每个元素最多只有一个出度和一个入读*度,表现为一条线状。线性表按存储方式分为顺序表和链表。**
真题:通过元素在存储空间中的相对位置来表示数据元素之间的逻辑关系,是(顺序存储)的特点。
真题:在线性表L中进行二分查找,要求L(顺序存储,元素有序排列)。
真题:(三元组顺序表和十字链表)是对稀疏矩阵进行压缩存储的方式。
存储结构:
顺序存储:用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素物理上也相邻。
链式存储:存储各数据元素的结点的地址并不要求是连续的,数据元素逻辑上相邻,物理上分开。
顺序存储,比如数字 1,2,3,4 按顺序存储,在内存地址上也是连续的,比如 1 存在 8地址上,2存在9地址上,3存在10地址上,4存在11地址上。
链式存储:比如数字1,2,3,4,但物理上比如 1存在 12地址上,2存在7地址上,3存在15地址上,4存在20地址上。此时知道数字1及数字1的地址12之后,如何拿到数字2、3、4的地址呢?这个时候就需要指针了,即指向下一个或上一个相邻数字的存储地址。
顺序存储和链式存储的对比如下图所示:
在空间方面,因为链表还需要存储指针,因此有空间浪费存在。
在时间方面,当**需要对元素进行破坏性操作(插入、删除)时,链表效率更高,**因为其只需要修改指针指向即可,而顺序表因为地址是连续的,当删除或插入一个元素后,后面的其他节点位置都需要变动。
而当需要对元素进行**不改变结构操作时(读取、查找),顺序表效率更高,**因为其物理地址是连续的,如同数组一般,只需按索引号就可快速定位,而链表需要从头节点开始,一个个的查找下去。
2、栈和队列
真题1:档函数调用执行时,在栈顶创建且用来支持被调用函数执行的一段存储空间称为活动记录或者栈帧,栈帧中不包括(B)
A、形参变量 B、全局变量 C、返回地址 D、局部变量
真题2:在程序执行的过程中,系统用(栈)实现嵌套调用(递归调用)函数的正确返回。
真题3:但函数调用执行时,在栈顶创建且用来支持被调用函数执行的一段存储空间称为活动记录或栈帧,栈帧中不包括(全局变量)。
队列、栈结构如下图,队列是先进先出,分队头和队尾;栈是先进后出,只有栈顶能进出。
循环队列中:
队尾指针指向最后一个元素的下一个元素(因为最后一个元素的下一个 元素为空,可用于插入、写入数据的);
队头指针指向第一个有值的元素(队头指向第一个非空元素,因为要读 到值,这里的读意味着出队,读到之后元素就为空了,因此要加1,指向 非空元素。);
队尾元素的指针 和 队尾指针 是不一样的哦。
队尾指针是最后一个元素的下一个空间;队尾元素的指针就是最后一个元素所在的空间。
答案为 :需要 % M,防止越界以及实现循环。
实际答案应为:(Q.front + Q.size - 1) % M
而 (Q.front + Q.size - 1 + M) % M = (Q.front + Q.size - 1) % M + M % M。
而 M % M = 0
所以答案为 B
◆树的基本概念如下
(1)双亲、孩子和兄弟。结点的子树的根称为该结点的孩子;相应地,该结点称为其子结点的双亲。具有相同双亲的结点互为兄弟。比如上图 B 是 A 的孩子结点,也是 E 和 F 的双亲结点。
(2)结点的度。一个结点的子树的个数记为该结点的度。例如A的度为3,B的度为2,C的度为0,D的度为1。
(3)叶子结点。叶子结点也称为终端结点,指度为0的结点。例如,E、F、C、G都是叶子结点。
(6)树的高度。一棵树的最大层数记为树的高度(或深度)。例如,图中所示树的高度为3。
设某二叉树采用二叉链表表示(即结点的两个指针分别指示左右孩子)。当二叉树包含k个节点时,其二叉链表结点中必有(k+1)个空的孩子指针。
这里可以取特殊值法:
1个结点时,空孩子指针为 2 (没有左右孩子)。
2个结点,且第二个结点为左孩子时(1个空指针 + 2个空指针)
3个结点,且第二三个结点分别为第一个结点的左右孩子时(2个空指针+2个空指针)。
所以:1时,2个空;2时3个空;3时4个空。所以答案为 k + 1
满二叉树:除了最后一层的叶子结点,其余节点的度都为2。
完全二叉树:除最后一层外,其余层都是满的,最后一层从左到右是连续的、不间断的(可以右侧缺少,但从左到右中间不缺)
二叉树的计算可以用 特殊值法,即 举例子 进行验证、计算。
二叉树的顺序存储结构:
顺序存储,就是用一组连续的存储单元存储二叉树中的节点,按照从上到下,从左到右的顺序依次存储每个节点。
二叉树的链式存储结构:
二叉链表:左右指针
三叉链表:左右指针、父指针
二叉树的遍历:
一颗非空的二叉树由根节点、左子树、右子树三部分组成,遍历这三部分,也就遍历了整棵二叉树。这三部分遍历的基本顺序是先左子树后右子树,但根节点顺序可变,以根节点访问的顺序为准有三种遍历模式:
先序(前序)遍历:根左右
中序遍历:左根右
后序遍历:左右根
前序:12457836
中序:42785136
后序:48752631
层次遍历:按层次,从上到下,从左到右。
反向构造二叉树:
仅仅有前序和后序是无法构造二叉树的,**必须要是和中序遍历的集合才能构造出二叉树。**即必须 前序+中序 或 后序 + 中序 才能构造出二叉树。
构造时,**前序和后序遍历可以确定根节点,中序遍历用来确定根节点的左子树和右子树节点,**而后按此方法进行递归,直至得出结果。
真题1:假设某消息中只包含7个字符{a,b,c,d,e,f,g},这7个字符在消息中出现的次数为{5,24,8,17,34,4,13},利用哈夫曼树(最优二叉树)为该消息中的字符构造符合前缀编码要求的不等长编码。各字符的编码长度分别为(58)
A.a:4,b:2,c:3,d:3,e:2,f:4,g:3 B. a:6, b:2, c:5, d:3, e:l, f:6, g:4
C.a:3,b:3,c:3,d:3,e:3,f:2,g:3 D. a:2,b:6, c:3, d:5, e:6, f:1, g:4
哈夫曼树:左小于右
构造过程:根据字符、出现次数,可以构造如下:
a:5,b:24,c:8,d:17,e:34,f:4,g:13
先选取上述中两个最小的,先构造一棵树,需要满足 左小于右,则有:
得到此时树顶为9,比9小的则是 c,于是要同样满足 左小于右的话,则有:
此时树顶为17,比17小的为 g:13,比17大的有b:24,于是要同样满足 左小于右 的话,则有:
此时已有a/b/c/f,还剩d:17、e:34、g:13,于是同样要满足左小于右的话,则有 34 < 41,13 + 17 < 34,所以可以组成:
所以就可以得到编码长度了:
a:4、b:2、c:3、d:2、f:4、g:3
当然以上的哈夫曼树是不唯一的,但是各对应叶子节点所在层次相同。
所以答案为 A。
真题:关于Huffman (哈夫曼)树的叙述中,错误的是(D)
A、权值越大的叶子离跟结点越近
B、Huffman(哈夫曼)树种不存在只有一个子树的结点
C、Huffman(哈夫曼)树种的结点数一定为奇数
D、权值相同的结点到树根的路径长度一定相同
哈夫曼树的求法(要掌握、会考):
给出一组权值,**将其中两个最小的权值作为叶子节点,其和作为父节点,组成二叉树,而后删除这两个叶子节点权值,并将父节点的值添加到该组权值中。**重复进行上述步骤,直至所有权值都被使用完。
◆若需要构造哈夫曼编码(要保证左节点值小于右节点的值,才是标准的哈夫曼树),将标准哈夫曼树的左分支设为0,右分支设为1,写出每个叶节点的编码,会发现,哈夫曼编码前缀不同,因此不会混淆,同时也是最优编码。
哈夫曼:左小于右
◆查找二叉树上的每个节点都存储一个值,且每个节点的所有左孩子结点值都小于父节点值,而所有右孩子结点值都大于父节点值,是一个有规律排列的二叉树,这种数据结构可以方便查找、插入等数据操作。(左小于父,右大于父。)
◆二叉排序树的查找效率取决于二叉排序树的深度,对于结点个数相同的二叉排序树,平衡二叉树的深度最小,而单枝树的深度是最大的,故效率最差。
◆平衡二叉树又称为AVL树,它或者是一棵空树,或者是具有下列性质的二叉树:
它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度之差的绝对值不超过1。若将二叉树结点的平衡因子(Balance Factor,BF)定义为该结点左子树的高度减去其右子树的高度,则平衡二叉树上所有结点的平衡因子只可能是-1、0和1。只要树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
先序、后序遍历可以确定根节点;中序遍历可以确定左右节点;
先序(根左右) 为 cabfedg,所以 c 为根节点,中序(左根右)中 abcdefg 所以,ab 为左子树、defg 为右子树。
此时 a、b 不知道左右、先后,此时再看先序(根左右),cab,所以a为根。即:
此时,需要确定b到底是左子树还是右子树,则需要看中序(左根右)abc,a 是根,b在 a后,所以b是右子树,所以:
接下来判断defg。还是先看先序,先序中为fedg(根左右),所以 f 为 根。而中序中 defg 所以 de 为左,g为右(左根右),所以:
此时,需要确定 de,先看先序(根左右)ed,所以 e 为根,同时中序(左根右)de,所以d为左子树,所以得到最终结果:
所以答案为:C。
二叉树的关键码序列就是根据给与的顺序是否尅构造出符合条件的二叉树,比如下方:
不可能的答案是:C。因为其他按照顺序都能构造出上图右侧中的二叉树。
某二叉树的先序遍历序列为 ABCDEF,中序遍历序列为 BADCFE,则该二叉树的高度(即层数)为(4)
先序:根左右
中序:左根右
后序:左右根
先序为 ABCDEF,所以 A 为根节点,根据中序,所以 B 为左节点、DCFE 为右节点。
所以有:
继续:先序的CDEF 中 C 为根节点,根据中序的 DCFE,所以 D 为左节点,FE 为右节点,所以有:
继续,先序的 EF 中 E 为根节点,根据中序的 FE,所以 F 为左节点,所以有:
不说按什么排序的,都可以按照先序进行排序,绘制成二叉树。
上图是 A、B、C 的二叉树,都符合。D 不符合。所以答案是D。
完全二叉树:除最后一层,都是满的。
最优二叉树:哈夫曼树,带权路径。左小于右
平衡二叉树:左子树、右子树深度差绝对值不超过1.(即深度差为-1或0或1)
满二叉树:所有层都是满的。
查找二叉树:左<父<右
真题1:当二叉树的结点数目确定时,(B)的高度一定是最小的。
A、二叉排序树 B、完全二叉树 C、线索二叉树 D、最优二叉树
真题1:关于无向联通图G的叙述中,不正确的是:A
A、G 中任意两个顶点之间均有边存在
B、G中任意两个顶点之间均存在路径
C、从G中任意顶点出发可遍历图中所有顶点
D、G的邻接矩阵是对称矩阵
真题2:若无向图 G 有n个顶点e条边,则 G 采用 邻接矩阵存储时,矩阵的大小为 (n2)
◆无向图:图的结点之间连接线是没有箭头的,不分方向。
◆有向图:图的结点之间连接线是箭头,区分A到B,和B到A是两条线
◆完全图:无向完全图中,节点两两之间都有连线,n个结点的连线数为(n-
1)+(n-2)+…+1= n * (n-1)/2;有向完全图中,节点两两之间都有互通的两个箭头,n个节点的连线数为n * (n-1).
◆度、出度和入度:顶点的度是关联与该顶点的边的数目。在有向图中,顶点
的度为出度和入度之和。
◆路径:存在一条通路,可以从一个顶点到达另一个顶点。
◆子图:有两个图G=(V, E)和G’=(V’,E’),如果V’属于V且E’属于E,则称G’为G 的子图
◆连通图和连通分量:针对无向图。若从顶点v到顶点u之间是有路径的,则说
明v和u之间是连通的,若无向图中任意两个顶点之间都是连通的,则称为连通图。无向图G的极大连通子图称为其连通分量。
◆强连通图和强连通分量:针对有向图。若有向图任意两个顶点间都互相存在
路径,即存在v到u,也存在u到v的路径,则称为强连通图。有向图中的极大连通子图称为其强连通分量。
◆网:边带权值的图称为网。
邻接矩阵:假设一个图中有n个节点,则使用n阶矩阵来存储这个图中各节点的
关系,规则是若节点i到节点j有连线,则矩阵Ri,j=1,否则为0,示例如下图所示:
◆邻接链表:用到了两个数据结构,先用一个一维数组将图中所有顶点存储起来,而后,对此一维数组的每个顶点元素,使用链表挂上其出度到达的结点的编号和权值,示例如下图所示:
◆存储特点:图中的顶点数决定了邻接矩阵的阶和邻接表中的单链表数目,边
数的多少决定了单链表中的结点数,而不影响邻接矩阵的规模,因此采用何种存储方式与有向图、无向图没有区别,要看图的边数和顶点数,完全图适合采用邻接矩阵存储。
◆图的遍历是指从图的任意节点出发,沿着某条搜索路径对图中所有节点进行访问且只访问一次,分为以下两种方式:
◆深度优先遍历:从任一顶点出发,遍历到底,直至返回,再选取任一其他节
点出发,重复这个过程直至遍历完整个图;
◆广度优先遍历:先访问完一个顶点的所有邻接顶点,而后再依次访问其邻接
顶点的所有邻接顶点,类似于层次遍历。
◆图的最小生成树
假设有n个节点,那么这个图的最小生成树有n-1条边(不会形成环路,是树非图)这n-1条边应该会将所有顶点都连接成一个树,并且这些边的权值之和最小,因此称为最小生成树。共有下列两种算法:
◆普里姆算法:任意顶点出发,找出与其邻接的边权值
最小的,此此边的另外一个顶点自动加入树集合中,而后再从这这个树集合的所有顶点中找出与其邻接的边权值最小的,同样此边的另外一个顶点加入树集合中,依次递归,直至图中所有顶点都加入树集合中,此时此树就是该图的最小生成树。普里姆算法的时间复杂度为0(n^2),与图中的边数无关,因此该算法适合于求边稠密的网的最小生成树。
先从 A(任意顶点)开始,找与A相邻的边权值最小的,就到了的 b。
然后再找出 A、C 两个顶点中相邻的边里最小的,此时与 C 相邻的 CF 边,在 A、C各自相邻边种最小(AB = 6,AD = 5,BC=5,CD=5,CE=6,CF=4,其中 CF 最小)。因此得到 c。
同理就再找 A、C、F 各自相邻边中最小的,FD 最小,于是得到 d。
此时,再找 A、C、F、D 各自相邻边最小的(此时 BC = 5,CD=5,AD=5,需要注意的是 不能选择 AD、CD,因为不能形成环路,如果选择了 BC 也会形成环路,则此时就不能找最小的5了,就要选择6了,比如 CE、EF,而此时BC 不是环路,所以可以得到 e 图),于是得到 e。
然后得到最终的图 f。
◆克鲁斯卡尔算法(推荐):这个算法是从边出发的,因为本质是选取权值最
小的n-1条边,因此,就将边按权值大小排序,依次选取权值最小的边,直至囊括所有节点,要注意,每次选边后要检查不能形成环路。
克鲁斯卡尔算法的时间复杂度为o(eloge),与图中的顶点数无关,因此该算法适合于求边稀疏的网的最小生成树。
首先,将 边移除,然后选择全图中权值最小的,就是 AC,于是连接AC,得到 b。
然后,再选剩下图中权值最小的,于是得到 DF,于是连接 DF,得到 c。
然后再选图中权值最小的,于是得到 BE,于是连接 BE,得到 d。
然后再选图中权值最小的,于是得到 CF,于是连接 CF,得到 e。
最后再选择图中权值最小的,BC = 5,CD = 5,AD = 5,注意不能形成环路,于是得到 BC,于是连接 BC,得到最终的 f。
◆拓扑序列
若图中一个节点入度为0,则应该最先执行此活动,而后删除掉此节点和其关联的有向边,再去找图中其他没有入度的结点,执行活动,依次进行,示例如下(有点类似于进程的前趋图原理)
a 图中,②③④⑤都是有箭头指向它的,即入度不为0.只有①、⑥是入度为0的。有箭头指向它,意思就是前者执行完(①执行完)后者(②)才能执行。
因此要先执行①或⑥,假如这里先执行⑥(先执行①或⑥都可以)。则把⑥相关的出度的箭头都删除掉。于是得到图 b。
此时 ①和④入度为0.假如这里先执行①(这里先执行①或④都可以)。则①执行完后,把①相关的出度的箭头都删除掉,于是得到 c。
同理可以得到 d、e、f。
顺序查找的思想:将待查找的关键字为key的元素从头到尾与表中元素进行比
较,如果中间存在关键字为key的元素,则返回成功;否则,则查找失败。
假如 1…n个数字,最短查找1次,即就是查找1;最长查找n次,即就是查找n。
所以就是 1 + 2 + 3 + … + n = (1 + n)* n / 2;平均 n 个,所以平均查找长度就是 (1+n) * n / 2 / n = (1 + n ) / 2。
◆只适用于待查找序列中的元素是有序排列的情况。
◆设查找表的元素存储在一维数组r[1…n]中,在表中元素已经按照关键字递增(或递减)方式排序的情况下,进行折半查找的方法是:
1、首先将待查元素的关键字(key)值与表r中间位置上(下标为mid)记录的关
键字进行比较,若相等,则查找成功;
2、若key>r[mid].key,则说明待查记录只可能在后半个子表r[mid+1…n]中,下一步应在后半个子表中查找;
3、若key<r[mid].key,说明待査记录只可能在前半个子表r[1…mid-1]中,下一步应在r的前半个子表中查找;
4、重复上述步骤,逐步缩小范围,直到查找成功或子表为空失败时为止
要注意两点:中间值位置求出若为小数,应该向下取整,即4.5=4,非四舍五入;中间值已经比较过不相等,在划分下一次比较区间时,无需将中间值位置再纳入下一次比较区间。
折半查找的时间复杂度为 O(log₂n)
哈希又叫散列。
哈希表通过一个以记录的关键字为自变量的函数(称为哈希函数)得到该记
录的存储地址,所以在哈希表中进行查找操作时,需要用同一哈希函数计算得到待查记录的存储地址,然后到相应的存储单元去获得有关信息再判定查找是否成功。
mod 是取余运算。
首先按顺序依次基于哈希函数求取数字的哈希地址,则首先求出哈希地址的先存该数字。比如34的哈希地址为1,同样 后面的 12 的哈希地址也为1。则1的地址存 34(因为34先计算出的地址1)。
按照顺序,3地址存了47,1地址存了34,2地址存了13,此时12 得到了哈希地址1,此时的1地址已经存了34,此时就产生了哈希冲突。哈希冲突的解决方法有很多,比如这里采用+1的(线性探测)方式,此时地址已经存到了最大的3地址,所以此时的12就存到地址4。
同理如果后续还有冲突,还可以采用+1的地址方式,在后续地址中存储。
首先 (1+13)/ 2 = 7,比较 M[7]
然后比较 1到 6(不能再包含7了),所以是 (1 + 6) / 2 = 3.5 = 3(向下取整),所以比较 M[3]
然后就是比较 4 到 6 了,所以是 (4 + 6) / 2 = 5,所以比较 M[5]
最后是 M[4]
所以答案是 A。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。