①现在有1千万个随机数,随机数的范围在1到1亿之间。现在要求写出一种算法,将1到1亿之间没有在随机数中的数求出来。
解决办法:
一)用一个32位的整数32位表示32个数,1亿/32 = 3125000,使用3.125 * 4M byte空间即可保存1亿个数,即index[3125000].
二)对于数n,(n-1) / 32 为其在数组中的下标,table[(n - 1) % 32]与数组中下标(n-1)/32的值使用或操作。
三)表table中值为 table[ 0 ]=0x00000001,
table[ 1 ]=0x00000002,
... ...
table[29]=0x20000000,
table[31]=0x80000000, 等这样的表示方式,具体的数值使用查表法加快速度。
四)最后算某值是否存在,使用与操作即可计算出。
数据存取比如:
第一个N=30是一个随机数,则存储可以表示为:index[(30-1)/32] = index[0] = index[0] || table[(30-1)%32]
注: /*刚开始时候初始化index[32]={0}*/
= 0 || 0x20000000 = 0x20000000;
第二个N=31是一个随机数,则存储可以表示为:index[(31-1)/32] = index[0] = index[0] || table[(31-1)%32]
注:/*第30位1,其他位为0*/
= 0x20000000 || 0x40000000 = 0x60000000;
... ...
依次类推,即可。
数据验证比如:
1. 当要查询30是否存在的时候,由于:(30-1)/32 = 0;(30-1)%32=29;我们只需要计算:index[0] & table[29] 是真还是假,就可以得出30是否存在。
2. 当要查询31是否存在的时候,由于:(31-1)/32 = 0;(31-1)%32=30;我们只需要计算:index[0] & table[30] 是真还是假,就可以得出31是否存在。
... ...
依次类推,即可。
小结:
通过分析此题目,首先这种思路和方法,在一定程度上用相对小的空间存储了大量的数据,节省了比较大的内
存空间;在运算方面,位运算的速度相当来说效率是比较高的,因而也再一定程度上节省了时间复杂。
总之,这种存储方式和思维方式,在一定方面能够有效的解决海量数据存储与运算。基于此题目,凡是大量数据筛选,判断是否存在等问题,我们都可以借鉴此题目的思维和方法。
② 如下,其中每个单元格的长宽均为1。任意给定长x,宽为y的如下形状,设计算法找出总共有多少个长方形(不计正方形个数)?(百度面试)
//算法实现
#include
int FindTotalNumOfRectangle(const unsigned int x, const unsigned int y)
{
unsigned int i, j, sum = 0;
for(i = 1; i <= x; ++ i)
{
for(j = 1; j <= y; ++ j)
{
if(i != j)
{
sum += (x - i + 1) * (y - j + 1);
}
}
}
return sum;
}
int main()
{
printf("%d\n",FindTotalNumOfRectangle(2, 3));
return 0;
}
③ 十进制转换为N进制(百度面试)
char *simple_itoa(unsigned int i)
{
/* 21 digits plus null terminator, good for 64-bit or smaller ints
* for bigger ints, use a bigger buffer!
*
* 4294967295 is, incidentally, MAX_UINT (on 32bit systems at this time)
* and is 10 bytes long
*/
static char local[22];
char *p = &local[21];
*p = '\0';
do {
*--p = '0' + i % 10;
i /= 10;
} while (i != 0);
return p;
}
#include
#include
void ReverseString(char *pString)
{
if(NULL == pString)
return ;
char *pBegin = pString;
char *pEnd = pString + strlen(pString) - 1;
while(pBegin < pEnd)
{
char temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
++ pBegin;
-- pEnd;
}
}
char * FromTenToN (const int num, const unsigned int N)
{
if(NULL == num || N < 2 || N > 16)
return NULL;
char *result = new char[];
int tmpNum = num;
unsigned int nCount = 0;
while(tmpNum)
{
int tmp = tmpNum % N ;
if(tmp >= 10)
tmp = 'A' + (tmp - 10) - '0';
result[ nCount ++] = tmp + '0';
tmpNum /= N;
}
result[ nCount ] = '\0';
ReverseString(result);
return result;
}
int main()
{
printf("the Result is :%s\n",FromTenToN(166, 16));
}
④ 快速排序算法(百度面试)
#include
int Partition(int *iArray, int i, int j)
{
int pivot = iArray[ i ];
while(i < j)
{
while(i < j && iArray[ j ] >= pivot )
{
j --;
}
if(i < j)
{
iArray[ i ++ ] = iArray[ j ];
}
while(i < j && iArray[ i ] <= pivot)
{
i ++;
}
if(i < j)
{
iArray[ j -- ] = iArray[ i ];
}
}
iArray[ i ] = pivot;
return i;
}
void QuickSort(int *iArray, int low, int high)
{
if(low < high)
{
int pivotpos = Partition(iArray, low, high);
QuickSort(iArray, low, pivotpos - 1);
QuickSort(iArray, pivotpos + 1, high);
}
}
int main()
{
int iArray[] = {1,0,9,3,7,2,-90,78,45,4,77,79,78,37,0,-1,2,3,6,9,5,4,78,78,78,1,1,1};
int len = sizeof(iArray) / sizeof(int);
QuickSort(iArray, 0, len - 1);
for(int i = 0; i < len; ++ i)
{
printf("%3d ",iArray[ i ]);
}
printf("\n");
}
⑥查找兄弟字符串(百度笔试)
//judge two string is brother string or not
bool IsBrotherString(const char *src,const char *dst)
{
if( strlen(src) != strlen(dst) )
return false;
//usually we only have 256 chars
unsigned int hashTable[256];
memset(hashTable,0,sizeof(hashTable));
//compute the num of every char in the src
const char *pSrc = src;
while(*pSrc != '\0')
{
++ hashTable[*pSrc ++];
}
//minus the num of every char in the dst
const char *pDest = dst;
while(*pDest != '\0')
{
-- hashTable[*pDest ++];
}
//at last,if all the num in hashTable is zero, it is brother string.
pSrc = src;
while(*pSrc != '\0')
{
if(hashTable[*pSrc ++] != 0)
{
return false;
}
}
return true;
}
⑦整形数组的整合。例如已知数组a前半部分a[0,mid - 1],后半部分a[mid,num-1],现前半部分和后半部分均已排好序。要求:实现a数组的从小到大排序。空间复杂度为O(1).(百度笔试)
void MergeSort(int *v, const int len, const int mid)
{
int i, temp;
int left = 0, right = mid;
while (left < right && right < len)
{
while (v[left] < v[right]) {
++ left;
}
temp = v[right];
for(i = right; i > left ; -- i) {
v[i] = v[i-1];
}
v[left] = temp;
//move the current right postion
++ right;
}
}
#include
int main()
{
// int a[] = {0,2,4,6,8,9,10,11,12,14,16,18,1,3,5,7};
// int a[] = {0,2,4,6,8,8,9,9,1000,1,3,5,7,10,11,12,14,16,18,20,21,23,25,45,68};
// int a[] = {100,1,3,5,7,10,11,12,14,16,18,20,21,23,25,45,68};
// int a[] = {8,8,8,9,9,1,5,8,9,10,12,13,14};
// int a[] = {1,2,3,4,5,6,7,8,9,2,4,5,7,8,9,10};
// int a[] = {0,1,2,3,4,5,6,7,8,9,9,17,100,5,5,5,5,9,10,10000};
int a[] = {1,2,3,4,5,6,7,100,1000,2,3,5,6,8,1001};
int len = sizeof(a) / sizeof(int);
int mid = 9;
MergeIntData(a, len, mid);
for(int i = 0; i < len; ++ i)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
2012年校园招聘笔试题目
一 简单题
1. 对远程Linux/Unix系统进行操作,通常的途径是采用终端软件通过SSH登录远程系统,进行操作。但是在网络发生中
断时,Linux/Unix端运行的程序将会中断。
请简述这种问题发生的原来,通过何种途径避免这种问题,以及该途径可以避免此问题的原理。
2.一个最小值堆,同时是一棵完全二叉树(只有最下面两次节点的子节点数可以少于2且最下面一层的节点都存储在最
左面),如图所示
1
/ \
2 6
/ \ / \
4 3 8 7
/ \
5 9
该堆顺序存储在一个数组a中,1 2 6 4 3 8 7 5 9
1)对于任意节点a[n],其在二叉树中左,右子节点访问方式
2)完成函数,向堆中加入一个元素后仍然满足堆的原有性质
void add_element(int *a,int size,int val) //a存储堆是数组,size是数组内的已有元
素个数,val是元素的值,数组内大小不需要考虑
3)完成函数,取出堆顶最小元素后仍然满足堆的原有性质
3 通过某种hash算法,可以让用户稳定的均匀分布到一个区间内,这个区间的大小为100%,分布的最小粒度为:0.1%,我们把这种区间叫做一层。现在有两个区间A,B,如何让层A中的任意子区间都均匀分布到层B的100%中?例如:层A中取10%,这10%会均匀分布到层B中,即:层B的每一个10%区间都会有1%的区间A中的10%,也可以说层B的。如果现在有超过10层,每一层之间都需要有这种关系,又如何解决?
二 算法与程序设计
1。不知所云
2 。1)给定一个序列s=[a1,a2,a3,...,an];构造一个函数,生成序列s的全排列;
示例:
>>>permu([1,2,3])
[[ 1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] ]
2)构造一个算法,生成序列s的所有组合;
示例:
>>>comb([1,2,3])
[ [ ],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3] ]
说明:算法均可用伪代码表示
三 系统设计题 -全排列算法原理和实现
全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。现以{1, 2, 3, 4, 5}为例说明如何编写全排列的递归算法。
1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。由于一个数的全排列就是其本身,从而得到以上结果。
2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。
百度笔试题目
LINUX和C相关问题:
1. static关键字的作用。为什么static变量只初始化一次?说下进程的地址空间(代码段,数据段,堆,栈等)
2.进程和线程的区别?为什么线程的调度开销小?
3.说下select机制
4.为什么需要字节对齐?字节对齐的规则?
算法和数据结构
(运气比较好,面试官没有要求写出程序,只要能说出算法思路就可以):
1.如何将一个字符串中的某一个字符全部删除,原字符串顺序不变?如输入abcdefbbg,删除b后得到acdefg,要求时间复杂度O(N),空间复杂度O(1)
2.如果要求对一个集合进行查询,插入,删除,你会怎么设计它的数据结构?平衡二叉树特点?怎么查询,如果时间复杂度要求比O(logn)更小,采用什么?hash的冲突解决方法有哪些?如果要求有序的输出,是选二叉树还是hash?怎么输出?
3.如何在一个二叉树中找两个节点的最近祖先节点?
4.台阶问题:有n个台阶,每次可以踏一个台阶,或2个,问有多少种走法?
(PS:我写出动态规划的表达式后,面试官问这个对吗?我想了半分钟,觉得有问题,正准备说应该是….,面试官笑着说
哦,别看了,没问题,倒….)
百度笔试题2005
题目大致是这样的:
第一部分选择题:
有几道网络相关的题目,巨简单,比如第一题是TCP、RIP、IP、FTP中哪个协议是传输层的......。有一道linux的chown使用题目。其他的全是数据结构的题目!什么链,表,码的,不知所云.唉,我可以没有学过数据结构的人呐!真残忍!这一部分迅速猜完!
第二部分简答题:
1、在linux中如何编译C程序,使之成为可执行文件?如何调试?
答案: 1)检查程序中.h文件所在的目录,将其加入系统PATH中;
2)执行C编译:#gcc [源文件名] -o [目标文件名]
执行C++编译:#g++ [源文件名] -o [目标文件名]
3)改变目标文件为可执行文件:#chmod +x [目标文件名]
4)如需将多个可执行文件连续执行,可生成批处理文件:
#vi [批处理文件名]
可执行文件1
可执行文件2
.........
最后将该批处理文件属性该位可执行。
调试:在编译时使用-g参数,就可以使用gdb进行调试。
2、写出内存分配和释放的函数,并指出区别。
答案:
C语言的标准内存分配函数:malloc,calloc,realloc,free等。
malloc与calloc的区别为1块与n块的区别:
malloc调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为“size”字节的连续区域,返
回该区域的首地址。
calloc调用形式为(类型*)calloc(n,size):在内存的动态存储区中分配n块长度为“size”字节的连续区域,
返回首地址。
realloc调用形式为(类型*)realloc(*ptr,size):将ptr内存大小增大到size。
free的调用形式为free(void*ptr):释放ptr所指向的一块内存空间。
C++中为new/delete函数。
3、写出socket函数,并指出其功能。
socket():建立socket通信描述符;
bind():将套接字和机器上的一定的端口关联;
connect():连接到远程主机;
listen():使套接字做好连接的准备,规定等待服务请求队列的长度;
accept():接受连接,一旦有客户端发出连接,accept返回客户地址信息和一个新的sock;
有了这个新的sock,双方就可以开始收发数据:
send()和recv():用于流式套接字或者数据套接字的通讯;
sendto()和recvfrom():用于无连接的数据报套接字;
close():关闭套接字;
shutdown():选择性的关闭套接字,可以只允许某一方向的通讯关闭;
getpeername():返回流式套接字时对端peer信息;
gethostname():返回程序所运行的机器的主机名字;
gethostbyname():返回本机IP;
第三部分编程题:
1、从文件中读取字符串数据,反序显示并大小写转换。
2、给定26字母表以及对应的密码表,编程实现加密及解密功能。
第四部分思考题(正是传说中的字典纠错题):
用户在输入英文单词时经常出错,现对其进行就错。给定一个正确的英文词典,考虑纠错实现。1)指出思路。2)流程、算法难易程度及可能的改进策略。
一道算法题目答案
int Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为V,并返回置换次数
{
for(n=0,i=1;i〈=Strlen(S)-Strlen(T)+1;i++) //注意i的取值范围
if(!StrCompare(SubString(S,i,Strlen(T)),T)) //找到了与T匹配的子串
{ //分别把T的前面和后面部分保存为head和tail
StrAssign(head,SubString(S,1,i-1));
StrAssign(tail,SubString(S,i+Strlen(T),Strlen(S)-i-Strlen(T)+1));
StrAssign(S,Concat(head,V));
StrAssign(S,Concat(S,tail)); //把head,V,tail连接为新串
i+=Strlen(V); //当前指针跳到插入串以后
n++;
}//if
return n;
}//Replace
分析:i+=Strlen(V);这一句是必需的,也是容易忽略的.如省掉这一句,则在某些情况下,会引起不希望的后果,虽然在大多数情况下没有影响.请思考:设S='place', T='ace', V='face',则省掉i+=Strlen(V);运行时会出现什么结果? (无限递归face)
百度2005年的笔试题
1.实现 void delete_char(char * str, char ch);
把str中所有的ch删掉
2.把字符串S中所有A子串换成B,这个没给函数原型
3.搜索引擎的日志要记录所有查询串,有一千万条查询,不重复的不超过三百万
要统计最热门的10条查询串. 内存<1G. 字符串长 0-255
(1) 主要解决思路 //具体用词和原题不大一样
(2) 算法及其复杂度分析
4.有字典,设计一个英文拼写纠正算法 (1) 思想 (2) 算法及复杂度 (3) 改进
5. { aaa, bb, ccc, dd }, { bbb, ff }, { gg } 等一些字符串的集合
要求把交集不为空的集合并起来,如上例会得到 { aaa, bb, ccc, dd, ff }, {gg}
(1) 思想 (2) 算法及复杂度 (3) 改进
2006百度笔试题
一、选择题:15分 共10题
1.一个含有n个顶点和e条边的简单无向图,在其邻接矩阵存储结构中共有__D__个零元素。
A.e B.2e C.n2-e D.n2-2e
2.__D__是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以
关联于具体的对象。
A.继承(Inhertance) B.模板(Template)
C.对象的自身引用(Self-Reference) D.动态绑定(Dynamic Binding)
3.应用层DNS协议主要用于实现网络服务功能. C
A. IP地址到网络设备名字的映射 B. IP地址到网络硬件地址的映射
C. 网络设备名字到IP地址的映射 D. 网络硬件地址到IP地址的映射
补充:ARP协议工作原理:将IP地址映射为MAC地址
4.linux默认情况下,一个进程最多能打开多少文件?A
A.64 B. 128 C. 512 D. 1024
5.下面结构体
struct s1 {
char ch, *ptr;
union {
short a, b;
unsigned int c:2, d:1;
}
struct s1 *next;
};
的大小是__D___:
A. 12字节 B.16字节 C.20字节 D. 24字节
6.任何一个基于"比较"的内部排序的算法,若对6个元素进行排序,则在最坏情况下所需的比较次数至少为_D___。
A.10 B.11 C.21 D.36
7.以下不是进程间通讯的是__C_ 补充:ABD+管道
A 共享内存 B 信号量 C线程局部存储 D 消息队列
8.下面程序,求count的值 A
int func(x)
{
int count= 0;
x=9999;
while(x)
{
Count ++;
x = x&(x-1);
}
return count;
}
A 8; B 10; C 5; D 11
补充:
#include
int func(int x)
{
int count= 0;
x=9999;
while(x)
{
count ++;
x = x&(x-1);
}
return count;
}
void main()
{
int y,z;
y=func(z);
cout< }
9.使用malloc系统调用分配的内存是在__D__ 上分配的?
A 栈; B bss; C 物理内存; D 堆
10.最坏情况下,合并两个大小为n的已排序数组所需要的比较次数__B___
A.2n B.2n-1 C.2n+1 D.2n-2
二、简答题:20分,共3题
1.(5分)下面这段代码是把中英文混合字符串(汉字用两个字节表示,特点是第一个字节的最高位为1)中的大写字母转
化为小写字母,请找出其中的bug,注意各种异常情况。
for (char *piterator = szWord; *piterator != 0; piterator++)
{
if (*piterator & 0x80 != 0)
{
piterator++;
}
else if (*piterator >= 'A' && *piterator <= 'Z')
piterator += 32; // *piterator += 32
}
2.(5分)对给定的上亿条无序的url,请按照domain、site以及path分别排序,并请指出排序过程中可能会遇到的哪些
问题?如何提高效率?
例如:http://www.baidu.com/path/about.html,domain、site以及path的定义分别如下:
Domain:baidu.com
Site:www.baidu.com
Path: www.baidu.com/path
可以用索引压缩排序法
3.(10分)某型CPU的一级数据缓存大小为16K字节,cache块大小为64字节;二级缓存大小为256K字节,cache块大小为
4K字节,
采用二路组相联。经测试,下面两段代码运行时效率差别很大,请分析哪段代码更好,以及可能的原因。
为了进一步提高效率,你还可以采取什么办法?
A段代码
int matrix[1023][15];
const char *str = "this is a str";
int i, j, tmp, sum = 0;
tmp = strlen(str);
for(i = 0; i < 1023; i++) {
for(j = 0; j < 15; j++) {
sum += matrix[j] + tmp;
}
}
B段代码
int matrix[1025][17];
const char *str = "this is a str";
int i, j, sum = 0;
for(i = 0; i < 17; i++) {
for(j = 0; j < 1025; j++) {
sum += matrix[j] + strlen(str);
}
}
A段代码效率会高出很多!首先A中函数strlen(str)只执行了一次,而B中执行了17*1025次.
其次是A段代码的cache块交换比B段代码少,相应的执行时间也少,效率高............
根据一级缓存和二级缓存的大小和chach块的大小,把数组定义为matrix[1024][16],外循环为1024次,内循环为1
6次,则效率会更高.
A段代码效率要远远高于B段代码,原因有三:
1、
B效率低最要命的地方就是每次都要调用strlen()函数,这是个严重问题,属于逻辑级错误。假设A的两层循环都不改变
,仅仅是把A的那个循环
里面的temp换成strlen()调用,在Windows 2000 (Intel 双) 下测试,竟然是A的执行时间的3.699倍。(这里没有涉及
不同CPU有不同的Cache
设计)仅仅是这一点就已经说明B段代码垃圾代码。
2、
这也是一个逻辑级的错误。在这里我们再做个试验,A、B段代码分别采用大小一样的数组[1023][15]、[1023]
[16]、[1023][17],只是
在循环上采取了不同的方式。两者在运行时间上也是有很大差异的了。B的运行时间大概是A的1.130倍。
那么这是因为什么呢?其实也很简单,那就是A段代码中的循环执行语句对内存的访问是连续的,而B段代码中的
循环执行语句对内存
的访问是跳跃的。直接降低了B代码的运行效率。
这里不是内层循环执行多少次的问题,而是一个对内存访问是否连续的问题。
3、
A的二维数组是[1023][15],B的二维数组是[1027][17],在这里B段代码有犯了一个CPU级错误(或者是Cache级的错误)
。
因为在Cache中数据或指令是以行为单位存储的(也可以说是Cache块),一行又包含了很多字。如现在主流的设计是一行
包含64Byte。每一行拥
有一个Tag。因此,假设CPU需要一个标为Tag 1的行中的数据,它会通过CAM对Cache中的行进行查找,一旦找到相同Tag
的行,就对其中的数据
进行读取。
A的是15 *4B = 60B,一个Cache行刚好可以存储。B的是17*4B = 68B,超过了一个Cache行所存储的数据。
很明显17的时候命中率要低于15的时候。
现在我们先不管A、B的循环嵌套的顺序,仅仅拿A段代码来做个试验,我们将会分三种情况来进行:
[1023][15] [1023][16] [1023][17]
运行结果并没有出乎意料之外 17 的时候的运行时间大概是 15 的时候的1.399倍,除去有因为17的时候多执行循环,
17/15 = 1.133 。
进行折算,17的时候大概是15的时候的1.265倍。
16的时候的执行时间要比15的时候的执行时间要短,因为是16的时候,Cache命中率更高。16/15 = 1.066 ,
而15的执行时间却是16的1.068倍,加上16多执行的消耗,进行折算,15的时候大概是16的时候执行时间的1.134倍。
因为A段代码是15,而B段代码是17,在这一点上B段代码的效率要低于A段代码的效率。这是一个CPU级的错误(或者是
Cache级的错误),
这里涉及到Cache的块大小,也就涉及到Cache命中率,也就影响到代码效率。
不再假设什么,仅仅对A段和B段代码进行测试,B段代码的执行效率将是A段代码执行效率的3.95倍。
当然最大的罪魁祸首就是B中的重复调用strlen()函数。后面两个错误告诉我们当需要对大量数据访问的时候,
一定要注意对内存的访问要尽量是连续而且循环内层的访问接近Cache的块大小,以提高Cache的命中率,从而提高程序
的运行效率。
所以可以对代码进行一下修改:
#define XX 15
#define YY 1023
int matrix[XX][YY];
const char *str = "this is a str";
int i, j, tmp, sum = 0;
tmp = strlen(str);
for(i = 0; i < XX; i++)
for(j = 0; j < YY; j++)
sum += matrix[i][j] + tmp;
这个程序仅仅是把数组的声明给颠倒了一下,循环也颠倒了一下,看起来和运行起来和上面给出的A段代码没有多大的区别。
但是如果当XX很小,比如:8,那么这段程序和给出的A段代码就有区别了。这是因为这样做可以提高Cache的命中率。
三、编程题:30分 共1题
注意:要求尽可能提供完整代码,如果可以编译运行酌情加分。
1.内存中有一个长数组,条目数为10万,数组单元为结构体struct array,sizeof(struct array)为512字节。
结构有一int型成员变量weight。现需要取得按weight值从大到小排序的前500个数组单元,请实现算法,要求效率尽可能高。
step1. 在内存中建立一个长度为500的缓冲区int型缓冲区
step2.遍历此长数组,按weight大小顺序填充此缓冲区,填入的值为数组的下表,非weight值 (由数组下表访问weight是O(1)时间)
step3.遍历完毕以后,按照由大到小的顺序输出数组单元
时间复杂度:O(n) (遍历为O(n),每一次操作,即插入一个有序的数组复杂度为O(logm),m最大为500,所以总的时间复杂度是O(n))
空间复杂度:O(1)
四、设计题:35分 共1题
注意:请尽可能详细描述你的数据结构、系统架构、设计思路等,建议多写一些伪代码或者流程说明。
1.请设计一个字典。以字符串为索引,存储用户定义的定长结构。要求有增、删、查、改的功能。
已经给定一个函数,可以由字符串映射到一个签名,每个签名由两个unsigned int类型组成。
假设每一个字符串能够对应唯一的一个签名,完全没有重复(或者重复的概率可以忽略),并且签名分布足够均匀。
请描述你的数据结构?内存如何申请?增、删、查、改的功能如何实现?如果操作很频繁,该如何优化?
2007年百度校园招聘会笔试题
1选错的
基类public成员在派生类中仍是public
基类protected成员在派生类中仍是protected
基类private成员在派生类中是隐藏
回去想的,我忘了错的怎么说的来着
2边长为n的正方形可以分成多个边长为1的正方形,如边长为2的正方形有2×2个边长为1的正方形和1个边长为2的正方形;问边长为5的正方形有几个正方形;
3
public class Person {
public void printValue(int i,int j){
System.out.println("1111111111111");
}
public void printValue(int i){
System.out.println("22222222222");
}
}
public class Teacher extends Person {
public void printValue(){
System.out.println("333333333");
}
public void printValue(int i){
System.out.println("4444444444");
}
public static void main(String[] args) {
Person t=new Teacher();
t.printValue(10);
}
}
输出结果是:4444444444
4.找错误
int tolower(const char *str)
{
if(NULL==str) return 0;
int i=0,iCount=0;
for(;i {
if(str[i]<='Z'||str[i]>='A')
{
str[i]+='z'-'Z';
iCount++;
}
}
return iCount;
}
5有个长度为12的无重复有序表,按折半查找法进行查找,在表内各元素等概率情况下,查找成功所需的平均比较(三元比较)的次数为()
A 35/12 B37/12 C 39/12 D 43/12
6从n个数里面找最大的两个数理论最少需要比较
A 2logn B 2 logn -1 C n+ logn -2 D 2n-3
7 386781634*234659874= 6(30秒)
8Linux的非root用户,在自己的目录中,不可以删除非空目录dirs的方法是:
A rm dir dirs B rm-rdirs C mv dirs /dev/null D destroy dirs
9 Shell运算结果是3的是
A echo(9/3)
B echo$[16/5]
C echo$((10-7))
D echo’21/6’|bc
大题:
1 每个整数0-9这10个数组成,如223有2个2 ,和1个3,输入m和n(0 求出m到n之间所有整数共包含了多少个0,1。。。。9
实现函数void foo(const char*m, const char * n, char * result, size_t len )
result为输出缓冲,len为result的长度。
要求写出思路、程序程序效率,计算时间复杂度和空间复杂度
2 linux32位系统下有10个无序文件,各文件大小不一(均小于1G)现在需要将此10个文件归并为一组,不超过10个有序文件(第一个文件最大数小于或等于第二个文件最小数,依次类推)请选择你擅长的语言实现说明 文件的每一行最大不超过128位的阿拉伯数字组合,每一行只有一个数字,头一位不是零
要求写出思路和程序,计算时间复杂度和空间复杂度
3 网页3种操作,查询,删除,最加到末尾
例如:每页显示20个,现在要查第50页。假如用有序数组,则从下标20×49开始,直接返回后面20个即可,但是当删除时会有大量数据移动,所以数组对删除效率低,另外一种方法是,不删除只作标记,但是查询时必须又从头开始计数,数一下应该从哪个位开始返回
设计一种数据结构高效率的完成3种功能
限制:1 操作在硬盘上发生
2 网页大小不相同
3总数小于10M
4单个小于100K
4数据库方面,不敢兴趣没记,不过看了下,不是很难
百度0711月4日网上笔试题及答案(仅供参考)
1 编程:
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
2 编程:
用C语言实现函数void * memmove(void *dest,const void *src,size_t n)。memmove函数的功能是拷贝src所指的
内存内容前n个字节到dest所指的地址上。
3 英文拼写纠错:
在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包含了正确英文单词的词典,请你设计一个拼写纠错的程序。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度;
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
4 寻找热门查询:
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度。
5 集合合并:
给定一个字符串的集合,格式如: {aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh} 要求将其中交集
不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出 {aaa bbb ccc ddd hhh},{eee fff}, {ggg}
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
1 题
char *revert(char * str)
{
int n=strlen(str);
int i=0;
char c;
for(i=0;i {
c=str;
str=str[n-i];
str[n-i]=c;
}
return str;
}
///
2 题
void * memmove(void *dest,const void *src,size_t n)
{
assert((dest!=0)&&(src!=0));
char * temp=(char * )dest;
char * ss=(char * )src;
int i=0;
for(;i {
*temp =*ss ;
}
return temp;
}
/
3 题
(1)思路: 字典以字母键树组织,在用户输入同时匹配
(2) 流程:
每输入一个字母:
沿字典树向下一层,
a)若可以顺利下行,则继续至结束,给出结果;
b)若该处不能匹配,纠错处理,给出拼写建议,继续至a);
算法:
1.在字典中查找单词
字典采用27叉树组织,每个节点对应一个字母,查找就是一个字母
一个字母匹配.算法时间就是单词的长度k.
2.纠错算法
情况:当输入的最后一个字母不能匹配时就提示出错,简化出错处理,动态提示可能 处理方法:
(a)当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;
(b)当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可 以有更多的)
根据分析字典特征和用户单词已输入部分选择(a),(b)处理
复杂性分析:影响算法的效率主要是字典的实现与纠错处理
(a)字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;
(b)纠错策略要简单有效 ,如前述情况,是线性复杂度;
(3)改进
策略选择最是重要,可以采用统计学习的方法改进。
//
4 题
(1)思路:用哈希做
(2) 首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度(注意值与日志项对应关系)
my.chinahrlab.com 选出前十的频度,取出对应的日志串,简单不过了。哈希的设计是关键。
//
5 题
(1)思路:先将集合按照大小排列后,优先考虑小的集合是否与大的集合有交集。有就合并,如果小集合与所有其他集
合都没有交集,则独立。独立的集合在下一轮的比较中不用考虑。这样就可以尽量减少字符串的比较次数。当所有集合
都独立的时候,就终止。
(2)处理流程:
1.将集合按照大小排序,组成集合合并待处理列表
2.选择最小的集合,找出与之有交集的集合,如果有,合并之;如果无,则与其它集合是独立集合,从待处理列表 中删
除。
3.重复直到待处理列表为空
算法: 1。将集合按照大小从小到大排序,组成待处理的集合列表。 2。取出待处理集合列表中最小的集合,对于集合的
每个元素,依次在其他集合中搜索是否有此元素存在:
1>若存在,则将此小集合与大集合合并,并根据大小插入对应的位置 。转3。
2>若不存在,则在该集合中取下一个元素。如果无下一个元素,即所有元素都不存在于其他集合。则表明此集合独立,
从待处理集合列表中删除。并加入结果集合列表。转3。
3。如果待处理集合列表不为空,转2。
如果待处理集合列表为空,成功退出,则结果集合列表就是最终的输出。
算法复杂度分析:
假设集合的个数为n,最大的集合元素为m 排序的时间复杂度可以达到n*log(n) 然后对于元素在其他集合中查找,最坏情况下为(n-1)*m 查找一个集合是否与其他集合有交集的最坏情况是m*m*(n-1) 合并的时间复杂度不会超过查找集合有交集的最坏情况。所以最终最坏时间复杂度为O(m*m*n*n)
需要说明的是:此算法的平均时间复杂度会很低,因为无论是查找还是合并,都是处于最坏情况的概率很小,而且排序后优先用最小集合作为判断是否独立的对象,优先与最大的集合进行比较,这些都最大的回避了最坏情况。
(3)可能的改进:
首先可以实现将每个集合里面的字符串按照字典序进行排列,这样就可以将查找以及合并的效率增高。另外,可能采取恰当的数据结构也可以将查找以及合并等操作的效率得到提高。