1.
同一进程的线程共享:
1.进程代码段
2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)
3.进程打开的文件描述符、
4.信号的处理器、
5.进程的当前目录和
6.进程用户ID与进程组ID
线程独有的内容包括:
1.线程ID
2.寄存器组的值
3.线程的堆栈
4.错误返回码
5.线程的信号屏蔽码
2
Tcp虽然面向字节,但是有流控
3
在数据库系统中,导致数据不一致的根本原因是:数据冗余
4
考虑左递归文法S->Aa|b
A->Ac|Sd|e,消除左递归后应该为( )
S->Aa | b
A->Ac | Sd | e
将S带入A:
A->Ac | Aad | bd | e
直接消除左递归:
A->bdA' | A'
A'->cA' | adA'|e
5
在UNIX系统中,目录结构采用( )
带链接树形目录结构
6
请问下面的程序一共输出多少个“-”?
1 2 3 4 5 6 7 8 9 |
int main(void) { int i; for (i = 0; i < 2; i++) { fork(); printf("-"); } return 0; |
这是因为printf("-");语句有buffer,所以,对于上述程序,如果没有”\n”则不会刷新缓存区。printf("-");把“-”放到了缓存中,并没有真正的输出。所以答案是8次。,如果有”\n”则刷新缓存区,6次。
7
分派延迟指分派程序停止一个进程的执行到启动另一个执行所花费的时间。
8
假定我们有3个程序,每个程序花费80%的时间进行I/O,20%的时间使用CPU。每个程序启动时间和其需要使用进行计算的分钟数如下,不考虑进程切换时间:
程序编号 启动时间 需要CPU时间(分钟)
1 00:00 3.5
2 00:10 2
3 00:15 1.5
请问,在多线程/进程环境下,系统的总响应时间为( )
0~10分钟内,只有一个进程在跑,进程1总共使用了10*0.2=2分钟的CPU,10*0.8=8分钟的IO,还剩下1.5分钟的CPU要使用;
10~15这5分钟内,有进程1和进程2两个进程,CPU利用率为1-0.8*0.8=0.36,所以CPU一共跑了5*0.36=1.8分钟,假定两个进程完全平等,CPU使用时间平分,则每个进程使用了1.8/2=0.9分钟的CPU时间,这样进程1剩下1.5-0.9=0.6分钟的CPU,进程2剩下2-0.9=1.1分钟的CPU;
15开始,有3个进程, CPU利用率为1-0.8*0.8*0.8=0.488,此时X分钟之内,CPU总共执行了0.488*X分钟,由于三个进程平分,所以每个进程的CPU使用时间为0.488*X/3, 所以进程1在 0.6*3/0.488=3.69分钟之后,也就是15+3.69=18.69分完成;
之后CPU利用率又为0.36(两个进程),此时进程2剩下1.1-0.6=0.5分钟的CPU,进程3剩下1.5-0.6=0.9分钟的CPU, 之后进程2在在0.5*2/0.36=2.78分钟之后也就是2.78+18.69=21.46时候进程2结束;
之后进程3开始单跑,此时进程3还剩下 0.9-0.5=0.4的CPU时间,此时的CPU利用率为0.2,0.4/0.2=2, 即2分钟之后进程3结束,也就是21.46+2=23.46≈23.5 所以答案应该选B
9
面向对象的三个基本元素,五个基本原则:封装、继承、多态
原则:
单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。
Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。
依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口。
10
1 |
void *pszStringRotate(char *pszString, intnCharsRotate) |
比如ABCDEFG,移3位变DEFGABC,要求空间复杂度O(1),时间复杂度O(n)。
void Rorder(char *pF, char *pE) { char temp; while (pF <= pE) { temp = *pF; *pF = *pE; *pE = temp; } }
void *pszStringRotate(char *pszString, int nCharsRotate) { char *pR = pszString; int n = 0; while (pszString + n++ ! = ‘\n’); //得到字符串长度 if (n < nCharsRotate) return pR; //入口参数检测
Rorder(pszString, pszString + nCharsRotate ); //C B A pszString = pR;//归位 Rorder( pszString + nCharsRotate, pszString + n - 1); //GFED pszString = pR; Rorder(pszString, pszString + n - 1); //DEFGABC return pR; } |
大致过程如下:
ABCDEFG
第一步:局部翻转
ABC DEFG == = 》 CBA GFED
第二步:整体翻转
CBA GFED == = 》 DEFGABC
11
windows内存管理的机制以及优缺点
分页存储管理基本思想:
用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
分段存储管理基本思想:
将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。
段页式存储管理基本思想:
分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。
在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。
段页式系统中,作业的地址结构包含三部分的内容:段号 页号 页内位移量
程序员按照分段系统的地址结构将地址分为段号与段内位移量,地址变换机构将段内位移量分解为页号和页内位移量。
为实现段页式存储管理,系统应为每个进程设置一个段表,包括每段的段号,该段的页表始址和页表长度。每个段有自己的页表,记录段中的每一页的页号和存放在主存中的物理块号。
12
设只含根节点的二叉树高度为1,现有一颗高度为h(h>1)的二叉树上只有出度为0和出度为2的结点,则此二叉树中所包含的结点数至少为________个。
分析:由于都是出度为0或2,所以要有左子树,就必须存在右子树。所以节点最少的情况如图:
所以答案为2h+1
13
某地电信局要对业务号码进行梳理,需要检测开通的市话号码是否存在某一个是另一个的前缀的情况,以简化电话交换机的逻辑。例如:某用户号码是“11001100”,但与"110"报警电话产生前缀配对。已知市话号码最长8位,最短3位,并且所有3位的电话号码都以1开头。由于市话号码众多,长度也未必一直,高效的算法可以用O(n)的时间复杂度完成检测(n为开通市话号码个数,数量是千万级的)。那么,该算法最坏情况下需要耗费大约________内存空间。
分析:最长 8 位, 最短 3 共6种情况:
三位都是 1 开头 ,因此有 10^2=100 种
四位: 10^4=10,000 种
五位: 10^5=100,000 种
六位: 10^6=1,000,000 种
七位: 10^7=10,000,000 种
八位: 10^8=100,000,000种
相加一共为111,110,100种,因为电话号码唯一,所有号码最后1位不用判断,总数除以10 = 11,111,010种
一位号码 4bit(号码 从 0-9 ,所以至少用 4 个 bit 位才能表示 ),8位的号码占 32bit 即 4字节/byte(其实可以只存前7位,3.5byte)
最后:11,111,010 * 4 / 1024 / 1024 = 42.4 Mb
14
甲乙两人捡到一个价值10元的购物卡。协商后打算通过这样的拍卖规则来确定归属:两人单独出价(可以出0元),出价高者得到购物卡同时将与出价相同数量的前给对方。如果两人出价相同,则通过掷硬币来决定购物卡的归属。例如:甲和乙都出价1元,他们通过掷硬币来决定购物卡的归属。此时,得到购物卡的人赚9元,另一人赚1元。两人都同意用手头的现金来进行出价。甲和乙都知道甲有6元、乙有8元,两人都期望自己尽可能多赚。那么________。
分析:甲乙两人赚的一样多
明显甲乙两人如果都想要获得最多的利益,那么就会互相揣摩对方是怎么想的,会出多少钱。
首先,由于获得价值10元购物卡的一方需要付出与出价相同数量的钱给对方,那么如果出价高于5元,获得购物卡的一方将收益少于(10-5)5元,很明显不划算,所以
1.双方都不会出价5元以上。
2.任一方都不会出价5元以下。
我们可以假设有一方出价4.9,那么如果另一方出价4.91呢?那另一方就获益5.09。
因为双方都不知道对方到底会出多少元,都只能猜测,或者去推出对方想获益最多应该出多少元,但不能保证对方一定会出那么多钱,所以任何一方都不会出5元以下,除非确定对方一定会出5元,可是对方不一定会出5元,万一对方是傻子。。
所以如一方0元,一方5元这种组合虽然双方利益相等,但不太现实。
所以,结果应该是双方都出价5元,扔硬币决定购物卡归属,最终得到购物卡的要付出5元,双方收益相等。
15
TCP断开时四次挥手
16
某公司有这么一个规定:只要有一个员工过生日,当天所有员工全部放假一天。但在其余时候,所有员工都没有假期,必须正常上班。假设一年有365天,每个员工的生日都概率均等地分布在这365天里。那么,这个公司需要雇用多少员工,才能让公司一年内所有员工的总工作时间期望值最大?
由于期望值满足线性关系(即对于随机变量 X 和 Y 有 E(X) + E(Y) = E(X+Y) ),因此我们只需要让每一天员工总工作时间的期望值最大就可以了。假设公司里有 n 个人,那么在特定的一天里,没有人过生日的概率是 (364/365) n 。因此,这一天的期望总工作时间就是 n · (364/365) n 个工作日。为了考察函数 n · (364/365) n 的增减性,我们来看一下 ((n+1) · (364/365) n+1 ) / (n · (364/365) n ) 的值,它等于 (364 · (n+1)) / (365 · n) 。如果分子比分母小,解得 n > 364 。可见,要到 n = 365 以后,函数才是递减的。
答案:365
17
给定一个排好升序的数组A[1]、A[2]、……、A[n],其元素的值都两两不相等。请设计一高效的算法找出中间所有A[i] = i的下标。并分析其复杂度。(不分析复杂度不得分)
解析:
用二分法来找。
解题时需要注意三点:
1.A[i]=i的位置一定是连着的,前面的是A[i]<i,后面是A[i]>i。转换成前面A[i]-i<0,A[i]-i=0,A[i]-i>0;
2.采用二分查找确定A[i]-i=0的位置
3.如何确定最左边的A[i]-i=0的位置,在进行二分查找时进行判断if(A[mid]-mid<0&&A[mid+1]-(mid+1)==0)return mid+1;
那就可以利用分治思想,进行二分一般的情况O(logn); 最差情况0(n);
public static int search(int[] A) {
int len = A.length;
int start = 0;
int end = len;
while (start <= end) {
int j = (start + end) / 2;
if (A[j] == j) {
return j;
}
if (A[j] > j) {
end = j - 1;
} else if (A[j] < j) {
start = j + 1;
}
}
return -1;
}
18
某怪物被海水冲上一个孤岛。醒来时他发现自己处于险境。周围有N条鳄鱼都虎视眈眈的盯着他。每条鳄鱼看上去都饿得足以把他吞下去。不过,事情也未必真的那么糟糕。鳄鱼吞下他是要花费体力的。这些鳄鱼现在的体力都相当,由于猎食需要花费体力,所以吞下怪物的鳄鱼会由于体力下降而可能被周围的某条鳄鱼吞了。类似的,吞鳄鱼的这条鳄鱼也可能被其他鳄鱼吞了。因此,虽然有食物可猎,但他们自己并不想成为其他鳄鱼的猎食对象。正所谓,螳螂捕蝉,黄雀在后。所以鳄鱼们在确保自己生命安全的情况下才会发动进攻。那么,怪物到底安全么?为什么?
解析:当鳄鱼为偶数的时候,鳄鱼们两两相互制约,只要谁先吃了怪物,那这条鳄鱼就会被吃掉,所以这个时候怪物是安全的。当鳄鱼为奇数的时候,当第一条鳄鱼吃掉了怪物而变得虚弱时,剩下的鳄鱼们为偶数,两两相互制约,谁也不能吃这个吃了怪物的鳄鱼,所以第一个动手的是安全的,既然这样谁都想第一个动手,所以这个时候怪物就完蛋了。
19
当你在浏览器输入一个网址,如http://www.taobao.com,按回车之后发生了什么?请从技术的角度描述,如浏览器、网络(UDP、TCP、HTTP等),以及服务器等各种参与对象上由此引发的一系列活动,请尽可能的涉及到所有的关键技术点。
首先是查找浏览器缓存,浏览器会保存一段时间你之前访问过的一些网址的DNS信息,不同浏览器保存的时常不等。
如果没有找到对应的记录,这个时候浏览器会尝试调用系统缓存来继续查找这个网址的对应DNS信息。
如果还是没找到对应的IP,那么接着会发送一个请求到路由器上,然后路由器在自己的路由器缓存上查找记录,路由器一般也存有DNS信息。
如果还是没有,这个请求就会被发送到ISP(注:Internet Service Provider,互联网服务提供商,就是那些拉网线到你家里的运营商,中国电信中国移动什么的),ISP也会有相应的ISP DNS服务器,一听中国电信就知道这个DNS服务器的规模肯定不会小,所以基本上都能在这里找得到。题外话:会跑到这里进行查询是因为你没有改动过"网络中心"的"ipv4"的DNS地址,万恶的电信联通可以改动了这个DNS服务器,换句话说他们可以让你的浏览器跳转到他们设定的页面上,这也就是人尽皆知的DNS和HTTP劫持,ISP们还美名曰“免费推送服务”。强烈鄙视这种霸王行为。我们也可以自行修改DNS服务器来防止DNS被ISP污染。
如果还是没有的话, 你的ISP的DNS服务器会将请求发向根域名服务器进行搜索。根域名服务器就是面向全球的顶级DNS服务器,共有13台逻辑上的服务器,从A到M命名,真正的实体服务器则有几百台,分布于全球各大洲。所以这些服务器有真正完整的DNS数据库。如果到了这里还是找不到域名的对应信息,那只能说明一个问题:这个域名本来就不存在,它没有在网上正式注册过。或者卖域名的把它回收掉了(通常是因为欠费)。
这也就是为什么打开一个新页面会有点慢,因为本地没什么缓存,要这样递归地查询下去。
多说一句,例如"mp3.baidu.com",域名先是解析出这是个.com的域名,然后跑到管理.com域名的服务器上进行进一步查询,然后是.baidu,最后是mp3,
所以域名结构为:三级域名.二级域名.一级域名。
浏览器终于得到了IP以后,浏览器接着给这个IP的服务器发送了一个http请求,方式为get,例如访问nbut.cn
这个get请求包含了主机(host)、用户代理(User-Agent),用户代理就是自己的浏览器,它是你的"代理人",Connection(连接属性)中的keep-alive表示浏览器告诉对方服务器在传输完现在请求的内容后不要断开连接,不断开的话下次继续连接速度就很快了。其他的顾名思义就行了。还有一个重点是Cookies,Cookies保存了用户的登陆信息,在每次向服务器发送请求的时候会重复发送给服务器。Corome上的F12与Firefox上的firebug(快捷键shift+F5)均可查看这些信息。
发送完请求接下来就是等待回应了,如下图:
当然了,服务器收到浏览器的请求以后(其实是WEB服务器接收到了这个请求,WEB服务器有iis、apache等),它会解析这个请求(读请求头),然后生成一个响应头和具体响应内容。接着服务器会传回来一个响应头和一个响应,响应头告诉了浏览器一些必要的信息,例如重要的Status Code,2开头如200表示一切正常,3开头表示重定向,4开头,如404,呵呵。响应就是具体的页面编码,就是那个<html>......</html>,浏览器先读了关于这个响应的说明书(响应头),然后开始解析这个响应并在页面上显示出来。在下一次CF的时候(不是穿越火线,是http://codeforces.com/),由于经常难以承受几千人的同时访问,所以CF页面经常会出现崩溃页面,到时候可以点开火狐的firebug或是Chrome的F12看看状态,不过这时候一般都急着看题和提交代码,似乎根本就没心情理会这个状态吧-.-。
如果是个静态页面,那么基本上到这一步就没了,但是如今的网站几乎没有静态的了吧,基本全是动态的。所以这时候事情还没完,根据我们的经验,浏览器打开一个网址的时候会慢慢加载这个页面,一部分一部分的显示,直到完全显示,最后标签栏上的圈圈就不转了。
这是因为,主页(index)页面框架传送过来以后,浏览器还要继续向服务器发送请求,请求的内容是主页里面包含的一些资源,如图片,视频,css样式等等。这些"非静态"的东西要一点点地请求过来,所以标签栏转啊转,内容刷啊刷,最后全部请求并加载好了就终于好了。
需要说明的是,对于静态的页面内容,浏览器通常会进行缓存,而对于动态的内容,浏览器通常不会进行缓存。缓存的内容通常也不会保存很久,因为难保网站不会被改动。