计算机应届毕业生常见面试题目.docx
《计算机应届毕业生常见面试题目.docx》由会员分享,可在线阅读,更多相关《计算机应届毕业生常见面试题目.docx(85页珍藏版)》请在冰豆网上搜索。
![计算机应届毕业生常见面试题目.docx](https://file1.bdocx.com/fileroot1/2023-1/28/9737b451-bec2-4e90-bd6d-12198af42e1a/9737b451-bec2-4e90-bd6d-12198af42e1a1.gif)
计算机应届毕业生常见面试题目
1.把二元查找树转换成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
比如将二元查找树
10
/\
614
/\/ \
4812 16
转换成双向链表
4=6=8=10=12=14=16。
分析:
本题是微软的面试题。
很多与树相关的题目都是用递归的思路来解决,本题也不例外。
二元查找树BST的一个重要特性便是如果按照中序方式对其进行遍历,结果是一个有序的序列。
所以我们可以中序遍历整棵树。
按照这个方式遍历树,比较小的结点总是最先被访问。
如果我们每访问一个结点,假设之前访问过的结点已经调整成一个排序好的双向链表,我们再调整当前结点的指针,将其链接到链表的末尾。
当所有结点都访问过之后,整棵树也就转换成一个排序双向链表了。
源码:
#include
usingnamespacestd;
typedefstructnode
{intdata;
structnode*left;
structnode*right;
}BSTNode;
intInsertBST(BSTNode*&bt,intk)
{//作为叶子结点插入BST树
if(bt==NULL)
{bt=(BSTNode*)malloc(sizeof(BSTNode));
bt->data=k;
bt->left=bt->right=NULL;
return1;
}
if(bt->data==k)
return0;
if(bt->data>k)
returnInsertBST(bt->left,k);
if(bt->datareturnInsertBST(bt->right,k);
}
voidCreateBST(BSTNode*&bt,intr[],intn)
{inti;
for(i=0;i{InsertBST(bt,r[i]);
}
}
voidConvertToDLinkList(BSTNode*bt,BSTNode*&visited)
{BSTNode*cur=bt;
if(cur!
=NULL)
{ConvertToDLinkList(cur->left,visited);
cur->left=visited;
if(visited!
=NULL)
visited->right=cur;
visited=cur;
ConvertToDLinkList(cur->right,visited);
}
}
voidDispDLinkList(BSTNode*bt)
{while(bt!
=NULL)
{printf("%d",bt->data);
bt=bt->right;
}
}
intmain()
{intr[5]={3,88,9,42,16};
BSTNode*bt=NULL;
CreateBST(bt,r,5);
BSTNode*h=NULL;
ConvertToDLinkList(bt,h);
//查找双链表的开始结点
while(h->left!
=NULL)
h=h->left;
//逐个打印显示结点值
DispDLinkList(h);
system("pause");
return0;
}
2.设计包含min函数的栈
题目:
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及pop的时间复杂度都是O
(1)。
分析:
这是去年google的一道面试题。
我们需要一个辅助栈。
每次push一个新元素的时候,同时将最小元素(或最小元素的位置。
考虑到栈元素的类型可能是复杂的数据结构,用最小元素的位置将能减少空间消耗)push到辅助栈中;而每次pop一个元素出栈的时候,同时pop辅助栈。
源码:
#include
usingnamespacestd;
#defineMaxSize100
typedefstruct
{intdata[MaxSize];
inttop;
}SqStack;
/**
*s作为主要存放数据的栈,t作为辅助栈,存放当前栈中最小元素的下标
*/
voidInitStack(SqStack*&s,SqStack*&t)
{s=(SqStack*)malloc(sizeof(SqStack));
t=(SqStack*)malloc(sizeof(SqStack));
s->top=t->top=-1;
}
intPush(SqStack*s,SqStack*t,intdata)
{if(s->top==MaxSize-1)
return0;
s->top++;
s->data[s->top]=data;
if(t->top==-1)
{t->top++;
t->data[t->top]=0;
}
else
{t->top++;
if(datadata[t->data[t->top-1]])
t->data[t->top]=s->top;
else
t->data[t->top]=t->data[t->top-1];
}
return1;
}
intPop(SqStack*s,SqStack*t,int&data)
{if(s->top==-1)
return0;
data=s->data[s->top];
s->top--;
t->top--;
return1;
}
intmin(SqStack*s,SqStack*t)
{assert(s->top!
=-1&&t->top!
=-1);
returns->data[t->data[t->top]];
}
intmain()
{SqStack*s,*t;
InitStack(s,t);
Push(s,t,12);
Push(s,t,4);
Push(s,t,-5);
intm;
printf("%d\n",min(s,t));
Pop(s,t,m);
printf("%d\n",min(s,t));
Pop(s,t,m);
printf("%d\n",min(s,t));
system("pause");
return0;
}
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。
要求时间复杂度为O(n)。
例如输入的数组为1,-2,3,10,-4,7,2,-5,和最大的子数组为3,10,-4,7,2,因此输出为该子数组的和18。
分析:
本题最初为2005年浙江大学计算机系的考研题的最后一道程序设计题,在2006年里包括google在内的很多知名公司都把本题当作面试题。
由于本题在网络中广为流传,本题也顺利成为2006年程序员面试题中经典中的经典。
如果不考虑时间复杂度,我们可以枚举出所有子数组并求出他们的和。
不过非常遗憾的是,由于长度为n的数组有O(n2)个子数组;而且求一个长度为n的数组的和的时间复杂度为O(n)。
因此这种思路的时间是O(n3)。
因为要求是O(N)的复杂度,因此需采用的DP的思想,记录下当前元素之和(为其最优状态,既最大),将其与目前所得的最大和比较,若大于则更新,否则继续。
状态的累加遵循这个过程:
如果当前和小于0,则放弃该状态,将其归零。
很容易理解,当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。
如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。
基于这样的思路,我们可以写出如下代码。
源码:
#include
usingnamespacestd;
#defineMaxSize100
intFindGreatestSubSum(intr[],intn,int&start,int&end)
{intmaxSum,curSum;
maxSum=curSum=0;
intcurStart=0,curEnd=0;
for(inti=0;i{
curSum+=r[i];
curEnd=i;
if(curSum<0)
{curSum=0;
curStart=i+1;
}
if(curSum>maxSum)
{maxSum=curSum;
start=curStart;
end=curEnd;
}
}
if(maxSum==0){//若是数组中的元素均为负数,则输出里面的最大元素
maxSum=r[0];//当然这步也可以写到上面一个循环里
intk;
for(inti=1;iif(maxSum}
start=end=k;
}
returnmaxSum;
}
intmain()
{inta[8]={-51,-2,-3,10,-4,-7,-2,-5};
intstart=0,end=0;
cout<cout<system("pause");
return0;
}
4.在二元树中找出和为某一值的所有路径
题目:
输入一个整数和一棵二元树。
从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数22和如下二元树
10
/\
512
/\
47
则打印出两条路径:
10,12和10,5,7。
二元树结点的数据结构定义为:
structBinaryTreeNode//anodeinthebinarytree
{
intm_nValue;//valueofnode
BinaryTreeNode*m_pLeft;//leftchildofnode
BinaryTreeNode*m_pRight;//rightchildofnode
};
分析:
这是XX的一道笔试题,考查对树这种基本数据结构以及递归函数的理解。
当访问到某一结点时,把该结点添加到路径上,并累加当前结点的值。
如果当前结点为叶结点并且当前路径的和刚好等于输入的整数,则当前的路径符合要求,我们把它打印出来。
如果当前结点不是叶结点,则继续访问它的子结点。
当前结点访问结束后,递归函数将自动回到父结点。
我们不难看出保存路径的数据结构实际上是一个栈结构,因为路径要与递归调用状态一致,而递归调用本质就是一个压栈和出栈的过程。
源码:
#include
usingnamespacestd;
#defineMaxSize100
typedefstructnode
{intdata;
structnode*left,*right;
}BSTNode;
intInsertBST(BSTNode*&t,intd)
{if(t==NULL)
{t=(BSTNode*)malloc(sizeof(BSTNode));
t->data=d;
t->left=t->right=NULL;
return1;
}
if(t->data==d)
return0;
if(t->data>d)
returnInsertBST(t->left,d);
if(t->datareturnInsertBST(t->right,d);
}
voidCreateBST(BSTNode*&t,intr[],intn)
{inti;
for(i=0;iInsertBST(t,r[i]);
}
voidAllPath(BSTNode*b,intpath[],intcurp,intexpSum)
{inti,tmp=0;
if(b!
=NULL)
{if(b->left==NULL&&b->right==NULL)
{for(i=0;itmp+=path[i];
if(tmp+b->data==expSum)
{printf("%d",b->data);//主要是最后这个点没压栈,故单独打印
for(i=curp-1;i>=0;i--)
printf("%d",path[i]);
printf("\n");
}
}
else
{path[curp]=b->data;
curp++;
AllPath(b->left,path,curp,expSum);
AllPath(b->right,path,curp,expSum);
curp--;
}
}
}
intmain()
{intr[7]={6,3,4,7,1,9,10};
BSTNode*t=NULL;
CreateBST(t,r,7);
intpath[MaxSize];
AllPath(t,path,0,13);
system("pause");
return0;
}
5.查找最小的k个元素
题目:
输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
分析:
这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数就是最小的k个数。
只是这种思路的时间复杂度为O(nlogn)。
我们试着寻找更快的解决思路。
我们可以先创建一个大小为k的数据容器来存储最小的k个数字。
接下来我们每次从输入的n个整数中读入一个数。
如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;如果容器中已有k个数字了,也就是容器已满,此时我们不能再插入新的数字而只能替换已有的数字。
我们找出这已有的k个数中最大值,然后拿这次待插入的整数和这个最大值进行比较。
如果待插入的值比当前已有的最大值小,则用这个数替换替换当前已有的最大值;如果带插入的值比当前已有的最大值还要大,那么这个数不可能是最小的k个整数之一,因为我们容器内已经有k个数字比它小了,于是我们可以抛弃这个整数。
因此当容器满了之后,我们要做三件事情:
一是在k个整数中找到最大数,二是有可能在这个容器中删除最大数,三是可能要插入一个新的数字,并保证k个整数依然是排序的。
如果我们用一个二叉树来实现这个数据容器,那么我们能在O(logk)时间内实现这三步操作。
因此对于n个输入数字而言,总的时间效率就是O(nlogk)。
我们可以选择用不同的二叉树来实现这个数据容器。
由于我们每次都需要找到k个整数中的最大数字,我们很容易想到用最大堆。
在最大堆中,根结点的值总是大于它的子树中任意结点的值。
于是我们每次可以在O
(1)得到已有的k个数字中的最大值,但需要O(logk)时间完成删除以及插入操作。
源码:
#include
usingnamespacestd;
#defineMaxSize100
#defineK3
intheap[K];
voidHeapAdjust(intr[],intlow,inthigh)
{inti=low,j=2*i+1,tmp;
tmp=r[i];
while(j<=high)
{if(jj++;
if(tmp{r[i]=r[j];
i=j;
j=2*i+1;
}
else
break;
}
r[i]=tmp;
}
voidCreateHeap(intr[],intn)
{inti;
for(i=n/2-1;i>=0;i--)
{HeapAdjust(r,i,n-1);
}
}
voidGetTopK(intr[],intn)
{inti;
for(i=0;iheap[i]=r[i];
CreateHeap(heap,K);
for(i=K;i{if(heap[0]>r[i])
{heap[0]=r[i];
HeapAdjust(heap,0,K-1);
}
}
}
intmain()
{intr[12]={3,7,-24,60,17,0,36,788,224,40,27,99};
GetTopK(r,12);
inti;
for(i=0;iprintf("%d",heap[i]);
system("pause");
return0;
}
6.判断整数序列是不是二元查找树的后序遍历结果
题目:
输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。
如果是返回true,否则返回false。
例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:
8
/\
610
/\/\
57911
因此返回true。
如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。
分析:
这是一道trilogy的笔试题,主要考查对二元查找树的理解。
在后续遍历得到的序列中,最后一个元素为树的根结点。
从头开始扫描这个序列,比根结点小的元素都应该位于序列的左半部分;从第一个大于跟结点开始到跟结点前面的一个元素为止,所有元素都应该大于跟结点,因为这部分元素对应的是树的右子树。
根据这样的划分,把序列划分为左右两部分,我们递归地确认序列的左、右两部分是不是都是二元查找树。
源码:
#include
usingnamespacestd;
intVerifyBST(intr[],intn)
{inti,j,root,left,right;
if(n<=0)
return0;
root=r[n-1];
left=right=1;
for(i=0;iif(r[i]>root)
break;
for(j=i;jif(r[j]return0;
if(i>0)
left=VerifyBST(r,i);
if(jright=VerifyBST(r+i,n-i-1);
return(left&&right);
}
intmain()
{/*intarr1[]={5,7,6,9,11,10,8};
intarr2[]={7,4,6,5};
if(VerifyBST(arr1,7))
printf("yes\n");
else
printf("no\n");*/
system("pause");
return0;
}
7.翻转句子中单词的顺序
题目:
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
句子中单词以空格符隔开。
为简单起见,标点符号和普通字母一样处理。
例如输入“Iamastudent.”,则输出“student.aamI”。
分析:
由于编写字符串相关代码能够反映程序员的编程能力和编程习惯,与字符串相关的问题一直是程序员笔试、面试题的热门题目。
本题也曾多次受到包括微软在内的大量公司的青睐。
由于本题需要翻转句子,我们先颠倒句子中的所有字符。
这时,不但翻转了句子中单词的顺序,而且单词内字符也被翻转了。
我们再颠倒每个单词内的字符。
由于单词内的字符被翻转两次,因此顺序仍然和输入时的顺序保持一致。
还是以上面的输入为例子。
翻转“Iamastudent.”中所有字符得到“.tnedutsamaI”,再翻转每个单词中字符的顺序得到“students.aamI”,正是符合要求的输出。
源码:
#include
usingnamespacestd;
voidreverse(char*begin,char*end)
{chartmp;
if(begin==NULL||end==NULL)
return;
while(begin{tmp=*begin;
*begin=*end;
*end=tmp;
begin++;end--;
}
}
//先逆转整个句子,然后从首字符开始扫描,
//每扫描到一个单词(遇到空白或结束字符),对这个单词进行逆转。
char*ReverseSentence(char*s)
{if(s==NULL)
returnNULL;
char*begin,*end;
begin=end=s;
while(*end!
='\0')
end++;
end--;
reverse(begin,end);
while(*begin!
='\0')
{end=begin;
while(*end!
=''&&*end!
='\0')
end++;
end--;
reverse(begin,end);
if(*(end+1)=='')
begin=end+2;
else
begin=end+1;
}
returns;
}
intmain()
{chara[100]="Iamastudent.";
ReverseSentence(a);
cout<system("pause");
return0;
}
8.求1+2+...+n
题目:
求1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?
B:
C)。
分析:
这道题没有多少实际意义,因为在软件开发中不会有这么变态的限制。
但这道题却能有效地考查发散思维能力,而发散思维能力能反映出对编程相关技术理解的深刻程度。
通常求1+2+…+n除了用公式n(n+1)/2之外,无外乎循环和递归两种思路。
由于已经明确限制for和while的使用,循环已经不能再用了。
同样,递归函数也需要用if语句或者条件判断语句来判断是继续递