一、简答题
1.简述数据库以及线程死锁产生的原理及必要条件,简述如何避免死锁。
数据可以及线程死锁产生的原理:进程P1(事务)占用资源R1时,进程P2(事务)占用资源R2时;P1下一步要用R2,P2要用R1;此时,两者均得不到所申请的资源而无法继续向前推进,这种由于竞争资源而引起的僵持称为死锁。
产生死锁的必要条件:
资源独占:一个资源同时只能分配给一个进程(事务)。
不可剥夺:资源申请者不能强行从资源占有者手中夺取资源,既资源只能有占有者使用完后自愿释放。
保持申请 : 进程占有部分资源后还可以申请新的资源,而且在申请新的资源的时候并不释放它已经占有的资源。
循环等待:存在一个进程等待序列{P1,P2,.......,Pn},其中P1等待P2,P2等待P3,.......,Pn等待P1.
避免死锁的方法:就是破坏上面4个必要条件中的任一个。具体方法有:
一次封锁法:每个进程(事务)将所有要使用的数据全部加锁,否则就不能继续执行。
顺序封锁法:预先对数据对象规定一个封锁顺序,所有进程(事务)都按照这个顺序加锁。
等待图法:如果出现了回路,则表示系统中出现了死锁;
超时法:如果一个进程(事务)的等待时间超过了规定的时间,则认为发生了死锁。
银行家算法:保证进程处于安全进程序列;
2.请列举面向对象设计的三个基本要素和五种主要设计原则。
三个基本要素:封装、继承、多态。
五种设计原则:
单一职责原则:一个类只有一种功能。
开放封闭原则:软件实体是可扩展的而是不可修改的。
里氏替换原则:子类必须能够替换基类。
依赖倒置原则:依赖于抽象。
接口隔离原则:使用多个小的专门的接口,而不要使用一个大的接口。
3.简述windows内存管理的几种方式及优缺点。
单对界地址存储管理(单一连续区域存储管理):
a.内存空间划分 : 内存空间采用动态异常分区方法,整个内存被动态的划分为若干个长度各异的区域。
b.进程空间划分 : 一个进程空间有连续的区域构成,假设进程的长度为l,则其逻辑地址为0到l-1.
c.进程空进与内存空间的对应关系:一个进程在内存中占有一个连续的区域。假设进程的长度为l,在内存中的起始地址为b,则其物理地址为b至b+l-1。
d.特点:要求一个进程占用内存空间的一个连续区域,因而利用动态异长存储分配的方法可能会产生碎片。
页式存储管理:
a.内存空间划分 : 内存空间被静态的划分为若干个等长的区域。每个区域被称为一个物理页框。
b.进程空间划分 : 进程空间也被静态的划分为若干个等长的区域,每个区域称为一个逻辑页面,其长度与页框的长度相等。
c.进程空进与内存空间的对应关系:当进程运行时,需要将它的各个逻辑页面保存到存储空间的物理页框中,即需要确定逻辑页面与页框的对应关系,进程的逻辑页面是连续的,但是页框页面却不一定是连续的。
d.特点:允许一个进程占用内存空间中多个连续的区域,而这些区域的长度相等,因而采用静态等长存储分配的方法,不会产生碎片。
段式存储管理:
a.内存空间划分 : 内存空间被动态的划分为若干个长度各异的区域,每个区域称为一个物理段。
b.进程空间划分 : 进程空间被静态的划分为若干个长度各异的区域,每个区域称为一个逻辑段。一个逻辑段通常对应一个程序段,各个程序断的长度使不等的。
c.进程空进与内存空间的对应关系:进程的一个逻辑段与内存的一个物理段相对应。一个进程的多个逻辑段可存放在内存的若干个不相连的物理段中。
d.特点:会产生碎片,但是便于实现共享。
断页式存储管理:
a.内存空间划分 : 与页式存储管理相同。
b.进程空间划分 :与段式存储管理相同。
c.进程空进与内存空间的对应关系:进程空间的一个逻辑页面对应内存空间的一个页框。同一段内的逻辑地址是连续的,而对应的页框却未必是连续。
d.特点:既不会产生碎片,又便于共享。
二、算法和程序设计
1、公司组织一次羽毛球比赛,采用淘汰机制,假设公司有1001个人,如果要评出“公司羽毛球第一高手”的称号,至少需要进行多少场比赛?请简述设计过程,并写出代码模拟比赛过程。
#include<iostream> using namespace std; int main() { int n=1001;//一共的人数 int num=0;//比赛的场数 int remain=0;//剩余的人数 for(n=1001;n>1;n=n/2) { if(n%2!=0) if(remain==0)remain=1; else{remain=0;n++;} num+=n/2; } cout<<num<<endl; return 0; }
2、一百个灯泡排成一排,第一轮将所有灯泡打开;第二轮每隔一个灯泡关掉一个。即排在偶数的灯泡被关掉,第三轮每隔两个灯泡,将开着的灯泡关掉,关掉的灯泡打开。依次类推,第100轮结束的时候,还有几盏灯泡亮着。
#include<iostream> #define NUM 100 using namespace std; int main() { int a[NUM]; int count=0; for(int i=0;i<NUM;i++)//把所有的灯打开 a[i]=1; for(i=0;i<NUM;i++) for(int j=2;j<NUM;j++) if(i%j==0) if(a[i]==1)a[i]=0; else a[i]=1; for(i=0;i<NUM;i++)//统计开着的个数 if(a[i]==1) count++; cout<<count<<endl; return 0; }
3、假定有20个有序数组,每个数组有500个数字,数字类型32位uint数值,现在需要取出这10000个数字中最大的500个,怎么做?
思路:20个有序数组,不妨设数序为降序。首先把20个数组中最大的(也就是a[i][0])进行大堆排序,输出堆顶元素(最大),在输出的那个元素之前所在的数组中,取下一个元素,放到堆顶,然后进行堆排序,在输出堆顶元素,依次循环输出500个即可。(输出最小的500,同样的道理,只不过进行小堆排序),这样充分利用了题干的条件。
#include<iostream> using namespace std; struct node { int data; int next; }; node obj[20]; void sift(int k,int m,int b[])//调整一个元素在堆的位置 { int i=k,j=2*i+1,temp; while(j<m) { if(j<m-2&&b[j]<b[j+1])// 体会j<m j++; if(b[i]>=b[j]) break; else { temp=b[i]; b[i]=b[j]; b[j]=temp; i=j; j=2*j+1; } } } void s_hsort(int n,int a[][500]) { int temp=0,j=0; int b[20]; for(int k=0;k<20;k++) { b[k]=a[k][0]; obj[k].data=a[k][0]; obj[k].next=1; } for(k=0;k<500;k++) { for(int i=(n-1)/2;i>=0;i--)//把20个元素调整为大堆的形式 sift(i,n,b); cout<<b[0]<<endl;//输出堆顶 for(i=0;i<20;i++)//把堆顶值换成下一个入堆的元素 if(b[0]==obj[i].data) temp=i; b[0]=a[temp][obj[temp].next]; obj[temp].data=b[0]; obj[temp].next+=1; } } int main() { int a[20][500]; for(int j=0;j<20;j++)//初始化这个二维数组 for(int i=0;i<500;i++) a[j][i]=10000-(i+1)-50*j; s_hsort(20,a); return 0; }
4、字符串左移,void *pszStringRotate(char *pszString, intnCharsRotate),比如ABCDEFG,移3位变DEFGABC,要求空间复杂度O(1),时间复杂度O(n)。
#include<iostream> using namespace std; void reverse(int m,int n,char a[])//实现反转 { char temp; for(int i=m,j=n;i<=(m+n)/2;i++,j--) { temp=a[i]; a[i]=a[j]; a[j]=temp; } } int main() { char a[]="ABCDEFG"; reverse(0,2,a);//三次反转产生循环移位的效果,完全符合题意 reverse(3,6,a); reverse(0,6,a); cout<<a<<endl; return 0; }
三、系统设计题
手机上通常采用九键键盘输入。即:1-9个数字分别对应一定的英文字母(如:2对应ABC, 3对应DEF,...),因此,用户可以方便的输入中文内容。比如,用户输入“926”,可以对应“WXYZ”,“ABC"和”MNO“的一系列组合”WAN”,“YAN"、”ZAO“等,这些对应“万”,“严”,“早”等汉字的中文拼音。
要求我们把这样的输入方式应用在我们的手机联系人查找功能上。有一个联系人列表UserList,记录了(姓名,手机号)这样的组合,通过输入的数字字符串NumStr,按照下面的规则把对应的联系人查找出来,返回一个ReaultList。
规则:
1、手机号能连续部分匹配输入的数字字符串NumStr。如输入NumStr=926,则手机号为13926811111会被查出来;
2、联系人姓名中的汉字转化成拼音后能够连续匹配输入数字字符串NumStr对应的英文字母组合,如:输入NumStr=926,则联系人“王二”、“万事通”会被查找出来。因为“王二”的“王”的拼音“WANG”中含有“WAN”,和“926”能匹配。
输入:联系人列表UserList<UserName, PhoneNo>;汉字拼音射射表Dict,数字拼音字符串NumStr。
输出:符合规则的联系人列表ResultList<UserName, PhoneNo>。
思路:根据numstr进行字符匹配,之间在phoneno中进行就可。把numstr所对应的字母:如926,可以组成4*3*3中组合,把相应的字符串存起来,。
联系人列表UserList<UserName, PhoneNo>中可以把姓氏按拼音存到一个数据表中,并且按照自己定义的一个映射表,得到姓氏所对应的数字串,然后在进行匹配。