搜狗笔试整理

来源:互联网 发布:房价m2知乎 编辑:程序博客网 时间:2024/05/16 17:15

一、定义一个空的类型,里面没有任何成员变量和成员函数。对该类型求sizeof,得到的结果是多少?

1

空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法适用这些实例。至于占用多少内存,由编译器决定。Visual Studio中每个空类型的实例占用1字节的空间。

如果在该类型中添加一个构造函数和析构函数,再对该类型求sizeof,得到的结果又是多少?

还是1

调用构造函数析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。

那如果把析构函数标记为虚函数呢?

C++编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4字节的空间,因此求sizeof得到4;如果是64位的机器,一个指针占8字节的空间,因此求sizeof则得到8

以上代码使用VC编译成32位可执行程序,请问:nLenA、nLenAObject、nLenB、nLenAObject、nLenC、nLenCObject的值分别为(A

A: 1,1,4,4,8,8
B: 0,0,4,4,4,4
C: 0,0,4,4,8,8
D: 1,1,4,4,4,4

C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1,所以nLenA和nLenAObject的值为1;

如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。因此,LenB和nLenBObject的值为4;

 对于普通继承,派生类和基类共享虚函数指针,派生类对象的存储空间=基类存储空间+派生类特有的非static数据成员的空间,由于t_classA为空类,t_classB和t_classC共享虚函数指针,因此LenC和nLenCObject的值为4;

 

2表达式3*2^(4+2*2-6*3)-5,求值过程中当扫描到6时,对象栈和算符栈为(),其中^为乘幂。(D)

A: 3,2,8;*^-
B: 3,2,4,2,2;*^+*-
C: 3,2,4,2,2,;*^(+*-
D: 3,2,8;*^(-

和运算符的优先级有关,

1、操作数栈置空,将表达式起始符"#"作为运算符栈的栈底元素

2、依次读入每个字符,若是操作数则入操作数栈;若是运算符s1则和运算符栈顶元素s2比较优先级后进行相应的操作,直至整个表达式求完。

i)若优先级s1>s2,s1入栈

ii)若s1<s2,当前栈顶运算符s2退栈,操作数栈顶的两个操作数退栈与操作符一起运算,并将运算结果入操作数栈;

过程如下:(1)操作数3,入栈s1; (2)运算符*,入栈s2;

(3)操作数2,入栈s1; (4)运算符^ ,入栈s2;(^的优先级比*高)

(5)运算符(,入栈s2; (6)操作数4,入栈s1;

(7)运算符+,入栈s2; (8)操作数2,入栈s1;

(9)运算符*,入栈s2;(理由是:*的优先级比+高)

(10)操作数2,入栈s1; (11)运算符 -,(-的优先级低于*)栈顶字符 * 出栈,完成2*2=4的运算,将结果4存入s1中;---s1:3,2,4,4;

(-的优先级低于+)栈顶字符+出栈,完成4+4=8的运算,将结果8存入s1中;---s1:3,2,8;此时,- 成为了(后的运算符,则直接入栈s2;---s2:*^(-;(12)操作数6 扫描;

 

 

稳定的排序算法:

选择排序,快速排序,希尔排序,堆排序 都不稳定。

冒泡排序,插入排序,归并排序,基数排序 都稳定。

二叉树:

二叉树具有五种基本形态:

1.空二叉树。

2.只有一个根结点。

3.根结点只有左子树。

4.根结点只有右子树。

5.根结点既有左子树又有右子树。

 

满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上。

满二叉树的特点:

(1)叶子只能出现在最下一层,出现在其他层就不可能达到平衡。

(2)非叶子结点的度一定是2.

(3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

 

完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树编号为i的结点在二叉树位置完全相同,则这颗二叉树成为完全二叉树。

满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的。

 

完全二叉树的特点:

1)叶子结点只能出现在最下两层。

2)最下层的叶子一定集中在左部连续位置。

3)倒数二层,若有叶子结点,一定都在右部连续位置。

4)如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况。

5)同样结点数的二叉树,完全二叉树的深度最小。

二叉树性质:

1.在二叉树的第i层上至多有个结点;

2.深度为k的二叉树至多有个结点();

3.对任何一颗二叉树T,如果其终端结点数为,度为2的结点数为,则

性质4:具有n个结点完全二叉树的深度为 ;( 表示不大于 的最大整数)。

性质5:如果对一棵有n个结点的完全二叉树(其深度为 )的结点按层序编号(从第一层到第 层,每层从左到右),对任一结点 有:

1.如果 ,则结点 是二叉树的根,无双亲;如果 ,则其双亲是结点 。

2.如果 ,则结点 无左孩子(结点i为叶子结点);否则其左孩子是结点 。

3.如果 ,则结点 无右孩子;否则其右孩子是结点 。

B树、B-树、B+树、B*树相关

B树,即二叉搜索树:

1.所有非叶子结点至多拥有两个儿子(LeftRight);

2.所有结点存储一个关键字;

3.非叶子结点的左指针小于其关键字的子树,右指针指向大于其关键字的子树;

B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;否则,如果查询关键字比结点关键字小,就进入左儿子;如果比结点关键字大,就进入右儿子;如果左儿子或右儿子的指针为空,则报告找不到相应的关键字;如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变B树结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;如:

但B树在经过多次插入与删除后,有可能导致不同的结构:

右边也是一个B树,但它的搜索性能已经是线性的了;同样的关键字集合有可能导致不同的树结构索引;所以,使用B树还要考虑尽可能让B树保持左图的结构,和避免右图的结构,也就是所谓的“平衡”问题;

实际使用的B树都是在原B树的基础上加上平衡算法,即“平衡二叉树”;如何保持B树结点分布均匀的平衡算法是平衡二叉树的关键;平衡算法是一种在B树中插入和删除结点的策略;

B-树,是一种多路搜索树(并不是二叉的)

1.定义任意非叶子结点最多只有M个儿子;且M>2;

2.根结点的儿子数为[2,M];

3.除根结点以外的非叶子结点的儿子数为[M/2, M];

4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字);

5.非叶子结点的关键字个数=指向儿子的指针个数-1;

6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

8.所有叶子结点位于同一层;如:(M=3)

B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;

B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;

B-树的特性:

1.关键字集合分布在整颗树中;

2.任何一个关键字出现且只出现在一个结点中;

3.搜索有可能在非叶子结点结束;

4.其搜索性能等价于在关键字全集内做一次二分查找;

5.自动层次控制;由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最底搜索性能为:

其中,M为设定的非叶子结点最多子树个数,N为关键字总数;所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

B+

B+树是B-树的变体,也是一种多路搜索树:

1.其定义基本与B-树同,除了:

2.非叶子结点的子树指针与关键字个数相同;

3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树

(B-树是开区间);

5.为所有叶子结点增加一个链指针;

6.所有关键字都在叶子结点出现;如:(M=3)

B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

B+的特性:1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

2.不可能在非叶子结点命中;

3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

4.更适合文件索引系统;

B*

是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;

B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3

(代替B+树的1/2);

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;

B-树:多路搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;

B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点

中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;

B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率

从1/2提高到2/3;

 

Select函数原型:

int select(int n,fd_set * readfds,fd_set *writefds,fd_set * exceptfds,struct timeval * timeout);

1) int n:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

 2)fd_set*readfds指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
 3) fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
 4) fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
 
5)struct timeval *timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

 

 

原创粉丝点击