腾讯研发工程师笔试题(整理)

腾讯研发工程师笔试题

1.爸爸去哪儿中的3对父子站成一排,各自父子之间不能相邻,比如石头不能和郭涛挨着,以此类推,共有几种站法?

  A.120

  B.48

  C.240

  D.144

分析:答案为C。

解题方法1:假设三对父子分别是Aa、Bb、Dd;

第一个位置有6种选择,假设为A;
第二个位置有4种选择(因为不能为a),假设为B;
第三个位置需要分类讨论一下,如果为a,则可以确定后面三位只有两种选择了,如果不为a,第三个位置有两种选择(D和d),假设为D,进而再确定后面三个位置的选择数。
那么结果是:6*4*(2+2*4)=240。
解题方法2:容斥原理,6个人全排列-一对父子相邻+两对父子相邻-三队父子相邻 
A(6,6)-C(3,1)*A(2,2)*A(5,5) + C(3,2)*A(4,4)*A(2,2)*A(2,2) - A(3,3)*A(2,2)*A(2,2)*A(2,2) = 240
解题方法3:易理解的办法:
(1)先取出两对父子,3对父子取出2对有C(3,2)=3种,
(2)进行排序,保证父子不相邻,就有 2*2=4种排序
(3)剩下一对父子插入上面4个人的5个缝隙中,就有 A(5,2)=5*4=20
(4)3*4*20=240
 
2、请找出下面程序中有哪些错误:
int main()
{
   int i=10;
   int j=1;
   const int *p1;//(1)
   int const *p2=&i; //(2)
   p2=&j;//(3)
   int *const p3=&i;//(4)
   *p3=20;//(5)
   *p2=30;//(6)
   p3=&j;//(7)
return 0;
}

      A.1,2,3,4,5,6,7

  B.1,3,5,6

  C.6,7

  D.3,5

分析:答案为C。

(1)const int*p1:表示不能通过指针p1修改它指向的内存单元的值,但是p1本身可修改。

(2)int const*p2=&i:与p1相同,即不能修改p2指向的内存单元的值,但是可以修改p2使其指向其它的内存单元。这里p2指向了整型变量i
(3)p2=&j:修改p2,使其指向整型变量 j ,由(2)可知(3)没错。
(4)int *constp3=&i:p3本身是指向整型变量的常指针,即p3初始化后不能再指向其它的内存单元,但是可以修改p3指向的内存单元的值。这里p3指向了整型变量i。
(5)*p3=20:通过p3将变量i的值修改为2,由(4)可知(5)没错。
(6)*p2=30:通过p2修改它所指向的内存单元的值,由(2)可知(6)错误。
(7)p3=&j:修改p3,使其指向j,由(4)可知(7)错误。

 

3、以下代码输出什么____.  

main()
{
   int a[5]={1,2,3,4,5};
   int *p=(int *)(&a+1);
   printf("%d",*(p-1));
}

    A.1

  B.2

  C.5

  D.出现错误

分析:答案:C。

数组名a可以作为数组的首地址,而&a是数组的指针,那么&a+1就是表示一个指向大小为5的下一个数组的指针,也就是数组a最后一个元素的下一个位置,那么int*p=(int*)(&a+1)进行强制类型转换,将指向数组的指针转换为指向第二个数组中首元素的指针,所以p-1则是指向第一个数组中最后一个元素的指针,所以输出是5。

 

5、有如下C++代码:

struct A{
  void foo(){printf("foo");}
  virtual void bar(){printf("bar");}
  A(){bar();}
};
struct B:A{
  void foo(){printf("b_foo");}
  void bar(){printf("b_bar");}
};

  那么

A *p=new B;
p->foo();
p->bar();

  输出为:

      A.barfoob_bar

  B.foobarb_bar

  C.barfoob_foo

  D.foobarb_fpp

分析:答案:A

A *p=newB;// A类指针指向一个实例化对象B, B类继承A类,先调用父类的无参构造函数,bar()输出bar,B类没有自己显示定义的构造函数。

p->foo();//执行B类里的foo()函数,因为foo不是虚函数,所以直接调用父类的foo函数,输出foo
p->bar();//执行B类的bar()函数, 该函数为虚函数,调用子类的实现,输出b_bar

 

6、linux下,指定文件file1为所有用户可读,可写,可执行,执行命令:___1__。修改file1的拥有者为test,拥有组为user,执行命令:___2__。

  A.chmod 776,chown test

  B.chmod 777,chown user

  C.chmod 777,chown test

  D.chmod 778,chown user

分析:答案:C。

# 改变权限
chmod 777 filepath
# 改变所有者
chown test filepath 
# 改变所属组
chgrp user filepath
 
7、(不定项)哪些设计模式是降低资源使用率:

  A.prototype

  B.singleton

  C.flyweight

  D.abstract factory

分析:答案:BC

---单例模式肯定降低了资源使用率,保证该类的实例永远只有一个!

---原型模式适用于在初始化信息不发生变换的情况,克隆的方法比较适合,主要的目的是避免重新初始化对象,如果后面需要对新对象进行,还需要区分深拷贝和浅拷贝。无论是深拷贝还是浅拷贝只是复制了资源,并没有降低资源使用率。

---享元模式(Flyweight): 基于共享技术用于把一些共同的信息(或模块)抽象出来,避免了大量相似类的开销,也降低了资源的使用率。

---抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。

 

9、n个顶点,m条边的全连通图,至少去掉____边才能构成一棵树?

  A.n-1

  B.m-1

  C.m-n+1

  D.m-n-1

分析:答案:C。

由于n个顶点的树一定有n-1条边,所以需要去掉m-(n-1)=m-n+1条边。

 

10、10.在序列(22,34,55,77,89,93,99,102,120,140)中,采用二分查找,分别查找77,34,99,所需的查找次数分别为()

  A.3,3,3

  B.3,3,4

  C.3,4,3

  D.4,2,4

分析:答案:D。

22 34 55 77 89 93 99 102 120 140

0   1    2   3  4    5   6     7    8     9
假设低下标用low表示,高下标用high表示。
查找77:
开始low = 0, high = 9
第一次查找,找到中心的下标为(0+9)/2 = 4,即89,由于89大于77,所以,调整low = 0,high = 3(注意:由于知道下标为4的元素比77大,所以不会让high等于4,即二分法 mid是要判断大小进行加1或减1,再运算的)
第二次查找,找到中心的下标为(0+3)/2 = 1,即34,由于34小于77,所以,调整low = 2,high = 3
第三次查找,找到中心的下标为(2+3)/2 = 2,即55,由于55小于77,所以,调整low = 3,high = 3
第四次查找,找到中心的下标为(3+3)/2 = 3,即77,找到所要找的元素
 
查找34和99的过程类似。。。
 
11、ip地址10.1.8.0/24和10.1.9.0/24,下列哪个是正确的汇总网段:

  A.10.0.0.0/8

  B.10.1.0.0/16

  C.10.1.8.0/23

  D.10.1.10.0/24

分析:答案:C。

从第一位不同的开始往后全为0;后面位相同的前缀位的位数

10.1.8.0/24 == 10.1.  0000 100 0 .0/24
10.1.9.0/24 == 10.1.  0000 100 1 .0/24
从不同的位开始,替换为0,得
                        10.1.  0000 100 0  .0 = 10.1.8.0
子网掩码为       8+8           +7          =23位
所以汇总网段为10.1.8.0/23
 
12、以下代码是否完全正确,执行可能得到的结果是____。
class A{
   int i;
};
class B{
   A *p;
public:
   B(){p=new A;}
   ~B(){delete p;}
};
void sayHello(B b){
}
int main(){
   B b;
   sayHello(b);
}

  A.程序正常运行

  B.程序编译错误

  C.程序崩溃

  D.程序死循环

分析:答案:C。

class A{
   int i;
};
class B{
   A *p;
public:
   B(){p=new A;}
   ~B(){delete p;}
   /*
   B(const B& ths){
       p = ths.p;
   }*/
};
void sayHello(B x){
}
int main(){
   B b;
   sayHello(b);
}

这里的错误原因是编译器在生成default copy construction的时候使用的bitwise copy语义,也就是只是简单的浅拷贝。 上面被注释掉的程序就是编译器自动添加的部分。 从而导致在sayHello中向参数x传递值时,调用了bitwise copy的拷贝构造函数,使得x对象和b对象中的值完全一致,包括p指针的值,在x离开作用域(也就是sayHello函数结束),x发生析构,调用delete 销毁了指针p,同时在main函数结束的时候,析构b时又会调用一次delete删除指针p。

也就是本程序会delete一直已经被delete 的指针。可以做如下改进,来修复程序:
 
class A{
   int i;
};
class B{
   A *p;
public:
   B(){p=new A;}
   ~B(){delete p;}
   B(const B& other){
       p = new A;       //构建新的指针
       *p = *(other.p); //将指向的内容复制,依然指向不同的位置
   }
};
void sayHello(B b){
}
int main(){
   B b;
   sayHello(b);
}

如上,在B中添加copy 构造函数

 

13.(不定项)在C++面向对象编程语言中,以下阐述不正确的是:

  A.接口中可以用虚方法

  B.一个类可以实现多个接口

  C.接口不能被实例化

  D.接口中可以包含已经实现的方法

分析:答案:AD。

所谓的接口是指只包含纯虚函数的抽象类,和普通的抽象类含不一样。所以A不对,必须是纯虚函数。然后B是正确的没有问题:接口的实现是通过子类的继承来实现的,可以有多个子类继承。然后是C,刚才说接口是特殊的抽象类,抽象类的唯一左右就是创建派生类,不能定义抽象类的对象,所以C是正确的。对于D,接口即只包含纯虚函数的抽象类,所以D是不对的。

 

14、(不定项)下面关于HTTP协议的说法正确的是:

  A.HTTP是基于TCP协议之上的应用层协议

  B.HTTP是一个普通用在浏览器和web服务器之间进行数据交换的流式二进制协议

  C.HTTP协议的ETAG响应头主要用于信息的过期验证

  D.HTTP1.0中的cache-control响应头主要用于控制信息在浏览器的缓存

分析:答案:AC。

HTTP是文本协议,不是二进制协议,B是错的;cache-control是在HTTP1.1中才有的,D是错的。

 

15、(不定项)关于多线程和多进程编程,下面描述正确的是():

  A.多进程里,子进程可获得父进程的所有堆和栈的数据;而线程会与同进程的其他线程共享数据,拥有自己的栈空间

  B.线程因为有自己的独立栈空间且共享数据,所有执行的开销相对较大,同时不利于资源管理和保护

  C.线程的通信速度更快,切换更快,因为他们在同一地址空间内

  D.线程使用公共变量/内存时需要使用同步机制,因为他们在同一地址空间内

  E.因多线程里,每个子进程有自己的地址空间,因此相互之间通信时,线程不如进程灵活和方便

分析:答案:ACD。

线程和进程的区别联系:

1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
两者都可以提高程序的并发度,提高程序运行效率和响应时间。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:
1、速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。
2、资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
3、同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内
 
 
 
posted @ 2016-02-24 13:28  小金乌会发光-Z&M  阅读(1752)  评论(0编辑  收藏  举报