明天要去完美笔试的缘故,特意在网上找了完美2014年的校招笔试题练手。可能是自己基础比较薄弱的缘故吧,感觉笔试题还是很难的。答案都是我自己上网查资料做的,有不对的地方还希望小伙伴们能够指正。选择题第13题和最后一道编程题,实在是不会,还希望能有小伙伴给解答一下。
一、选择题
4.在C++中,以下四类转化,失败时有可能引发std::bad_cast异常的是:B
A.static_cast B.dynamic_cast C.reinterpret_cast D.const_cast
解析:C++中预定义了多个可用于创建异常对象的类。主要有以下几种:
1)runtime_error类(定义于头文件<stdexcept>中)是多个描述运行时错误的标准异常类的基类。类overflow_error描述了算术运算向上溢出的错误,即一个值太大,超出了C++能描述的范围。类underflower_error描述了算术运算向上溢出的错误,即一个值太 小,超出了C++能描述的范围。
2)logic_error类(定义于头文件<stdexcept>中)是多个描述逻辑错误的标准异常类的基类。类invalid_argument描述的是将非法的实参数传递给函数的错误。类length_error 描述的是对象大小超过最大允许长度的错误。类out_of_range描述的是值超出允许范围的错误。
3)类bad_alloc、bad_cast、bad_typeid和bad_exception描述了C++运算符抛出的异常。bad_alloc异常是new运算符在无法分配内存时抛出的。bad_cast异常是dynamic_cast运算符在没有转化为引用类型时抛出的。bad_typeid异常是typeid运算符在运算对象为空指针时抛出的。bad_exception类则是描述了从未预料的异常处理程序所抛出的异常。
5.正则表达式中,用于匹配字符串的开始位置的字符是:B
A.< B.^ C.$ D.+
解析:^用于匹配字符串的开始位置,$用于匹配字符串的结束位置。
6.当对一个对象的改变需要同时改变其它对象,但它不知道其它对象是谁,并且也不知道具体有多少个对象有待改变时,我们应该使用:D
A.模板方法 B.责任链 C.代理 D.观察者
7.在X86体系中,以下哪个是栈顶指针寄存器:D
A.EAX B.ECX C.ESI D.ESP
解析:eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。EDX 则总是被用来放整数除法产生的余数。ESI/EDI分别叫做"源/目标索引寄存器"(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串。EBP是"基址指针"(BASEPOINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer)。ESP 专门用作堆栈指针,被形象地称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP也就越来越小。
8.假设有std::vector<int> v,数值依次为{12,17,17,22,22,23,30}。 Lower_bound(v.begin(),v.end(),17)和upper_bound(v.begin(),v.end(),22)所返回的iterator分别指向数组中的第几个值(假设从0开始):C
A.0,5 B.1,4 C.1,5 D.2,3
解析:lower_bound在排序的vector中进行二分查找,查找第一个大于等于的值,upper_bound在排序的vector中进行二分查找,查找第一个大于的值。
9.下列哪个C++关键字用于表示变量或者函数的定义在别的文件中:B
A.static B.extern C.inline D.default
解析:static是用来声明静态变量;extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义;inline用于定义内联函数;default是用在switch语句结尾,表示其他情况。
10.print()函数是一个类的常成员函数,它无返回值,下列表示中正确的是:A
A.void print()const B.const void print() C.void const print() D.void print(const)
解析:A中const是用来修饰成员函数,B的用法是用来修饰函数返回值,C无此类用法,D的用法是修饰函数传递的参数值。
11.对于int *pa[5]的描述,以下哪个选项是正确的:A
A.pa是一个具有5个元素的指针数组,每个元素是一个int类型的指针
B.pa[5]表示某个数组的第5个元素的值
C.pa是一个指向数组的指针,所指向的数组是5个int类型的元素
D.pa是一个指向某个数组中第5个元素的指针,该元素是int类型的变量。
解析:int *pa[n]是指针数组,[]优先级高,先与pa结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。int (*p)[n]是数组指针,()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。
12.inti=(int)((unsigned int)0xffffffff+(unsigned int)0xffffffff);
printf(“%d”,i);结果是:C
A.0 B.-1 C.-2 D.2
解析:计算机中是用补码来存数字的。原码中,最高位表示符号位,0为正,1为负。正数的反码和补码是其本身,负数的反码是在原码的基础上,符号位不变,其余位取反,补码是反码加1。因此0xffffffff在int型时表示时-1,在unsigned int中表示的是2^32-1。
13.由a,b,c组成的5位字符串,aa至少出现过一次的共有多少个:
A.77 B.78 C.79 D.80
这道题实在是不会了。。
14.已知有n个进程共享一个互斥段,如果最多允许m个进程(m<n)进入互斥段,则信号量的变化范围是:C
A.-m-1 B.-m-0 C.-(n-m)-m D.-(m-1)-n
解析:当没有进程进入互斥段时,信号量为m;当有m个进程进入互斥段且有1个进程等待进入互斥段时,信号量为-1;最多能有n-m进程等待进入互斥段,因此信号量的变化范围是-(n-m)~m。
15.师徒四人西天取经,途中必须跨过一座桥,四个人从桥的同一端出发,你得帮助他们到达另一端。天色很暗,而他们只有一只手电筒。一次同时最多可以有两人一起过桥,而过桥的时候必须持有手电筒,所以就得有人把手电筒带来带去,来回桥两端。手电筒不能用丢的方式来传递。四个人的步行速度各不同,若两人同行则以较慢者的速度为准。大师兄只需花1分钟过桥,二师兄需花2分钟过桥,三师兄需花5分钟过桥,师父需花10分钟过桥。请问他们最短在多少分钟内能过桥:B
A.16 B.17 C.18 D.19
解析:首先让大师兄和二师兄过河,大师兄回来送手电筒,三师兄和师傅过河,二师兄回来送手电筒,大师兄和二师兄再一起过河。所需时间为2+1+10+2+2=17分钟。
16.下面各项中属于不可重载的一组运算符是:C
A.+、-、*、/ B.[ ]、( ) C.::、.、?:、sizeof、. * D.++、--
解析:大多数运算符都是可以重载的,不可重载的运算符很少,有作用域操作符 ::、条件操作符 ?:、点操作符 .、指向成员操作的指针操作符 ->*,.*、预处理符号 #和sizeof。
二、填空题
1.求下面函数的返回值
int func(int a)
{
int count=0;
while(a)
{
count++;
a=a&(a-1);
}
return count;
}
假设a=911,则函数返回值是:7
2.请简单解释以下三种页面调度算法的含义以及原理。
1)FIFO:先进先出调度算法,根据页面进入内存的时间先后选择淘汰页面,先进入内存的页面先淘汰,后进入内存的后淘汰。
2)LRU:最近最不常用调度算法,根据一段时间内页面的访问次数来选择淘汰页面,每次淘汰访问次数最少的页面。
3)LFU:最近最少调度算法,在选择淘汰页面时会考虑页面最近的使用,总是选择在最近一段时间以来最少使用的页面予以淘汰。
3.给出至少两种用于优化C++项目编译时间的方法
1)删除不必要的#include。
2)删除不必要的一大堆私有成员变量。
3)删除不必要的类之间的继承
4.下面代码输出什么:>6
void foo(void)
{
unsigned int a=6;
int b=-20;
(a+b>6)?puts(“>6”):puts(“<=6”);
}
unsigned int和int型相加时,程序会自动按无符号数来计算,两者相加的值为4294967282,远远大于6。
4.顺序搜索和二分搜索代表了搜索时间和预处理时间之间的折中,处理一个n元素 格时,需要执行多少次二分搜索才能弥补对表进行排序所消耗的预处理时 间?lgn
解析:顺序搜索的复杂度是O(n),二分搜索的复杂度是O(lgn),但是二分查找要求数组有序。用复杂度低的排序算法,比如快排,复杂度为O(nlgn)。假设需要x次,则x*n=nlgn+lgn*x,所以x=nlgn/(n-lgn),当n比较大时,x接近于lgn。
5.到商店里买200的商品返还100优惠券(可以在本店代替现金),请问实际上折 扣是多少?6.7折
解析:相当于花了200块钱买了300块钱的东西,所以折扣是200/300,6.7折。
6.若二叉树中有n个度为2的结点,则该二叉树中的叶子节点数为:n+1
解析:二叉树的基本性质,度为0的结点个数为度为2的结点个数加上1。
7.写出至少三种常见的设计模式,并简单介绍其用法或目的:
1)观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,在它的状态发生变化时,会通知所有的观察者。
2)代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
3)模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
9.写出判断float f是否等于零的if语句:if(x<0.000001f && x>-0.000001f)
解析:由于精度的问题,float型不能用“==”直接来判断,要判断这个数是否在一个很小的区间内。
10.编译时的多态性通过重载来实现。
解析:编译时的多态性是通过函数重载和运算符重载实现的,运行时的多态性是通过继承和虚函数来实现。
11.class A
{
public:
A(){}
virtual ~A(){}
char m_x;
};
A a;
sizeof(a)的值是:8
解析:虚函数放在虚表中,如果定义了虚函数,则需要存放一个指向虚表的指针。在C++中,指针占4个字节,char类型占1个字节,根据字节对齐的原理,需要占据8个字节,因此类的大小为8。
12.C语言编译器将源程序翻译成二进制可执行文件,需要经过预处理、编译和优化、汇编、链接等阶段。
13.下面二叉树的中序遍历是:HDBEAFCG
三、简答题
1.引用和指针的区别?
1)引用相当于是变量的一个别名;指针是指向变量的内存地址。
2)引用只能在定义时初始化一次,之后不可变;指针可变。
3)引用不能为空,指针可以为空。
2.怎么理解TCP连接中的数据传输是无边界的,在实际编程中需要怎样处理。
TCP协议是无消息边界的,即不能保证来自单个send方法的数据能被单个receive方法读取。比如:第一次发送:abcdefg 第二次发送:123456 接收方接收数据时,可能会出现以下情况:第一次接收:abcdefg123456 也可能出现:第一次接收:abc第二次接收:efg12第三次接收:3456。
解决办法:1)发送固定长度的消息。这种方适用于信息长度固定的场合。2)将消息长度与消息一起发送。这种方法增加了数据传送信息量,也增加了编程工作量。3)使用特殊标记分隔消息。这种方法适合信息本身不包含特殊标记的场合。如每发送一行信息,就可以用回车换行作为分隔符。
3.请你对快速排序、归并排序和堆排序这3种算法进行描述,并说明他们各自的适用条件。
快速排序:选择数组的第一个数作为主元,把所有比它小的数移到它的左边,所有比它大的数移到它的右边。以此递归该操作,直到整个数组有序。
归并排序:将数组划分为两半,对每一半递归地进行归并排序,当两个子列表都已排序完毕,将它们合并为一个有序列表。
堆排序:堆排序的过程是先建立一个大根堆,大根堆的要求是每个节点的值都不大于其父节点的值。然后把根节点与最后一个叶子节点互换,再重新建立最大堆。递归执行,直到遍历完整个数组。
快速排序适用于原数组是无序、随机分布的,归并排序适用于要求排序稳定的场景,堆排序的最差情况下复杂度也为O(nlgn),适用于对最差情况下复杂度有要求的场景。
四、编程题
1.请自己用双向链表实现一个队列,队列里节点里存的值为int,要求实现入队,出队和查找指定结点的三个功能。
链表结点定义为:
struct node_t {
Int value;
node_t *prev;
node_t *next;
};
请实现如下三个函数并补齐需要成员:
class Queue{
……
void push_back(int value);
int pop_front();
node_t *find(int value);
};
解析:声明一个head头结点,执行push_back时,则在head前加入新插入的结点;执行pop_front()时,则返回head指向的值,并把头结点删除,让head指向下一结点;执行find时,则依次遍历链表,如果找不到值,就返回空。代码如下:
struct node_t
{
int value;
node_t *prev;
node_t *next;
};
class Queue
{
public:
Queue();
void push_back(int value);
int pop_front();
node_t *find(int value);
private:
node_t *head;
};
Queue::Queue()
{
head=NULL;
}
void Queue::push_back(int value)
{
if(head==NULL)
{
head=(node_t*)malloc(sizeof(node_t));
head->value=value;
head->next=NULL;
head->prev=NULL;
}
else
{
node_t *newNode=(node_t*)malloc(sizeof(node_t));
newNode->value=value;
newNode->next=head;
newNode->prev=NULL;
head->prev=newNode;
head=newNode;
}
}
int Queue::pop_front()
{
int value=head->value;
node_t *temp=head->next;
head->next=NULL;
temp->prev=NULL;
delete head;
head=temp;
return value;
}
node_t *Queue::find(int value)
{
if(head==NULL)
return head;
node_t *temp=head;
while(temp!=NULL)
{
if(temp->value==value)
{
return temp;
}
else
{
temp=temp->next;
}
}
return NULL;
}
2.写一个C/C++函数,判断一个单链表是否具有环,如果存在环,则给出环的入口点。
解析:这是leetcode第142题,也是很常见的一道题。解析过程请见文章:http://blog.csdn.net/kevin_zhai/article/details/47656537。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL)
return head;
ListNode *fast=head,*slow=head;
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next->next;
slow=slow->next;
if(slow==fast)
{
break;
}
}
if(fast==NULL||fast->next==NULL)
return NULL;
slow=head;
while(slow!=fast)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
};
3.游戏和图形渲染中,物体间的碰撞检测是很常见的需求。假设有一个可以抽象为凸边形的物体和一个可以抽象为质点的物体,凸边形各个顶点的坐标和质点的坐标都已知,设计一个算法来检测这两个物体是否碰撞(即质点是否在凸边形的内部或者边上)。可以采用文字描述、伪代码或者真实代码来描述。
网上有很多解法,但都不太懂。推荐博文:http://blog.csdn.net/kaitiren/article/details/11913453