2013.9.28 天津 百度软件研发笔试题

希望大家对一些有疑问的题,多提提自己的看法,谢谢!

简答题

1:动态链接库和静态链接库有什么优缺点?


二者是都是共享代码的方式,也可称为程序模块化的方式;
静态库的结构比较简单,其实就是把原来的目标代码放在一起,链接程序根据每一份目标代码的符号表查找相应的符号(函数和变量的名字),找到的话就把该函数里面需要定位的进行定位,然后将整块函数代码放进可执行文件里,若是找不到需要的函数就报错退出。
优点:
1:性能上有一定的提高。比如,exe模块调用动态链接库的导出函数时,需要先加载动态链接库,有一定的性能损失;
2:静态链接可以避免运行程序的系统缺少动态库而无法运行的情况。比如,VS2008编译的MFC动态链接的应用程序,会需要MFC90U.Dll这个动态库文件。如果目标系统缺少这个动态库的话,程序就会加载失败。
缺点:
1:链接后产生的可执行文件包含了所有需要调用的函数的代码,因此占用磁盘空间较大。
2:如果有多个(调用相同库函数的)进程在内存中同时运行,内存中就存有多份相同的库函数代码,因此占用内存空间较多。
动态链接的意思就是在程序装载内存的时候才真正的把库函数代码链接进行确定它们的地址。
优点:
1、有利于程序共享
2、多个程序使用DLL的单一映射,节约内存空间
3、通过程序拆分,有利于程序升级
4、可以节约大量硬盘空间
缺点:
1:性能上应该不如静态链接
2:程序如果缺少动态库将无法运行

2:轮询任务调度与抢占式任务调度的区别?

轮询调度算法的原理是每一次把来自用户的请求轮流分配给内部中的服务器,从1开始,直到N(内部服务器个数),然后重新开始循环。
抢占式任务调度允许调度程序根据某种原则去暂停某个正在执行的进程,将已分配给该进程的处理机重新分配给另一进程。抢占方式的优点是,可以防止一个长进程长时间占用处理机,能为大多数进程提供更公平的服务,特别是能满足对响应时间有着较严格要求的实时任务的需求。

3:请列出数据库中常用的锁,分别给出应用场景?

排它锁(写锁):若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。保证其他事务在T释放A上的锁之前不能在读取和修改A。
共享锁(读锁):若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加上锁,而不能加X锁,直到T释放A上的锁。保证其他事务可以读A,但在T释放A上的锁S之前不能对A做任何操作。
活锁:如果事务T1封锁了数据R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍等待。然后T4又请求封锁R,当T3释放了R上的锁之后系统又批准了T4的请求……T2有可能永远等待,这就是活锁。
死锁:如果事务T1封锁了数据R1,T2封锁了数据R2,然后T1又请求封锁R2,因T2已封锁了R2,于是T1等待T2释放R2上的锁。接着T2又申请封锁R1,T2也只能等待T1释放R1上的锁。这样就出现了T1在等待T2,而T2又在等待T1的局面,T1和T2两个事务将永远不能结束。这就是死锁。
几种意向锁
意向共享锁(IS锁):如果对一个数据对象加IS锁,表示它的后裔结点拟加S锁。例如,事务T1要对R1中某个元组加S锁,则要先对关系R1和数据库加IS锁。
意向排它锁(IX锁):如果对一个数据对象加IX锁,表示它的后裔结点拟加X锁。例如,事务T1要对R1中某个元组加X锁,则要先对关系R1和数据库加IX锁。
共享意向排它锁(SIX锁):如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁,即SIX=S+IX。例如,对某个表加SIX锁,则表示该事务要读取整个表(所以要对该表加S锁),同时会更新个别元组(所以要对该表加IX锁)。

算法与程序设计题

1:n为正整数,求比这个数大且最小的不重复数,重复数为相邻两位数字相同,如1101为重复数,1231为不重复数。

方法一:暴力解法
#include "stdafx.h"
#include <iostream>
using namespace std;

bool IsNorepeatNum(int key)
{
	int n1,n2;
	int p = 1, q = 10;
	bool flag = true;
	while ( key )
	{
		n1 = key/p%q;
		n2 = key/(p*10)%q;
		if(n1 == n2)
		{
			flag = false;
			break;
		}
		key /= 10;
	}
	return flag;
}
int FindNextNotrepeatNum(int inputNum)
{
	while ( !IsNorepeatNum(inputNum) )
		inputNum++;

	return inputNum;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a[] = {1101,1231,9901,1099,1001};
	for ( int i = 0; i < 5; i++ )
	{
		cout << FindNextNotrepeatNum(a[i]) <<endl;
	}
	
	return 0;
}
方法二:一种优化策略
分析:例如处理数字1222,从右往左处理每一位数字。首先处理第一位1222,发现它和前一位相同,该位加上1*该位权值后为1223,再处理第二位,1223,也发现该位与前一位相同再加上该位权值,变为1233,此时,我们将该位的后几位数字全部清0,即1230,重新重第一位进行处理,直到没有相邻相同数字为止。
代码:
#include "stdafx.h"
#include <iostream>
using namespace std;

int FindNextNotRepeatNum1(int nNum)
{
	//先确定位数
	int nPreBitNum, nCurBitNum, nNewNum, nProcessCount, nDigitCount = 0;
	nNewNum = nNum;
	while ( nNewNum )
	{
		nNewNum /= 10;
		nDigitCount++;
	}
	int* pProcessedBitNum= new int[nDigitCount];	//记录当前以出

	int p = 1, q = 10;
	nNewNum = nNum;
	nProcessCount = 0;
	while ( nProcessCount < nDigitCount-1  )
	{
		nCurBitNum = nNewNum/p%q;
		nPreBitNum = nNewNum/(p*10)%q;
		
		if ( nCurBitNum != nPreBitNum )
		{
			nProcessCount++;
			p = p*10;
		}
		else 
		{//如果当前判断的2个数字相等,则对当前处理的数字加1,再将之后的数字清0,重新判断。
			nNewNum = nNewNum + p;	
			int nTempNum = nNewNum%p;
			nNewNum = nNewNum - nTempNum;
			nProcessCount = 0;
			p = 1;
		}
	}
	return nNewNum;
}

2:长度为N(N很大)的字符串,求这个字符串里的最长回文子串。

解析:
 
 
 
 

3:数轴上从左到右有n个点a[0],a[1],…a[n-1],给定一根长度为L的绳子,求该绳子能覆盖几个点。

方法一:利用枚举法,将任意两点区间长度与L相比,更新最大长度
#include "stdafx.h"
#include <iostream>
using namespace std;

int MaxCoverCount(int* arr, int len, int L)
{
	int maxCover = 1, nCurCount;
	for ( int i = 0; i < len; i++ )
	{
		for ( int j = i+1; j < len; j++ )
		{
			if ( (arr[j]-arr[i]) <= L )
			{
				nCurCount = j-i+1;
				maxCover = (maxCover>nCurCount)?maxCover:nCurCount;
			}
		}
	}
	return maxCover;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a[] = {1,10,90,105,150,200,203,250,300};
	int Len = 100;
	cout << MaxCoverCount(a,9,Len) <<endl;
	return 0;
}
方法二:时间复杂度为O(n)-----参考我一战友insistGoGo的解法
分析:可设置两个游标nS和nE,根据两端点的距离D与L的长度进行比较
(1)如果L>=D,nE向后移动一位,增大当前区间
(2)如果L<D,nS向后移动一位,缩小当前区间
代码:
#include "stdafx.h"
#include <iostream>
using namespace std;

int MaxCoverCount(int* arr, int len, int L)
{
	int maxCover = 1, nCurCount = 1;
	int nStart = 0;
	int nEnd = 1;
	while ( nEnd < len )
	{
		if ( (arr[nEnd]-arr[nStart]) <= L )	//长度小于等于L将区间扩大
		{
			nCurCount++;
			nEnd++;
		}
		else	//长度大于L将区间缩小
		{
			maxCover = (maxCover>nCurCount)? maxCover : nCurCount;	// 更新当前最大值区间
			//将当前区间值范围缩小
			do 
			{
				nStart++;
				nCurCount--;
			} while (arr[nEnd]-arr[nStart] > L);
		}
	}
	//将最后一区间和当前最大区间进行判断
	maxCover = (maxCover>nCurCount)? maxCover : nCurCount;

	return maxCover;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int a[] = {1,10,90,100,100,200,203,250,300};
	int Len = 105;
	cout << MaxCoverCount(a,9,Len) <<endl;
	return 0;
}


系统设计题

现代系统的设计过程中,为了减轻请求的压力,通常采用缓存技术,即分布式缓存或客户端的调度模块,将针对不同内容的用户请求分配给不同的缓存服务器进行处理,请设计该服务器满足:

1) 单台缓存服务器故障,整个分布式缓存集群,可以继续提供服务。

2)通过一定得分配策略,可以保证充分利用每个缓存服务的存储空间,及负载均衡。当部分服务器故障或系统扩容时,改分配策略可以保证较小的缓存文件重分配开销。

3)当不同缓存服务器的存储空间存在差异时,分配策略可以满足比例分配。

解析:

不懂这块的系统设计题属于哪部分知识,应该如何准备,望大牛,指点指点。

你可能感兴趣的:(2013.9.28 天津 百度软件研发笔试题)