数据结构面试大全6.docx
《数据结构面试大全6.docx》由会员分享,可在线阅读,更多相关《数据结构面试大全6.docx(15页珍藏版)》请在冰豆网上搜索。
数据结构面试大全6
数据结构面试大全
1.判断链表是否存在环型链表问题:
判断一个链表是否存在环,例如下面这个链表就存在一个环:
例如N1->N2->N3->N4->N5->N2就是一个有环的链表,环的开始结点是N5这里有一个比较简单的解法。
设置两个指针p1,
p2。
每次循环p1向前走一步,p2向前走两步。
直到p2碰到NULL指针或者两个指针相等结束循环。
如果两个指针相等则说
明存在环。
structlink
{
intdata;
link*next;
};
boolIsLoop(link*head)
{
link*p1=head,*p2=head;
if(head==NULL||head->next==NULL)
{
returnfalse;
}
do{
p1=p1->next;
p2=p2->next->next;
}while(p2&&p2->next&&p1!
=p2);
if(p1==p2)
returntrue;
else
returnfalse;
}
2,链表反转
单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。
比如一个链表是这样的:
1->2->3->4->5
通过反转后成为5->4->3->2->1。
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的
下一个元素,然后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。
源代码如下:
structlinka{
intdata;
linka*next;
};
voidreverse(linka*&head)
{
if(head==NULL)
return;
linka*pre,*cur,*ne;
pre=head;
cur=head->next;
while(cur)
{
ne=cur->next;
cur->next=pre;
pre=cur;
cur=ne;
}
head->next=NULL;
head=pre;
}
还有一种利用递归的方法。
这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。
源代码如下。
不
过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL
。
因为要改变head指针,所以我用了引用。
算法的源代码如下:
linka*reverse(linka*p,linka*&head)
{
if(p==NULL||p->next==NULL)
{
head=p;
returnp;
}
else
{
linka*tmp=reverse(p->next,head);
tmp->next=p;
returnp;
}
}
3,判断两个数组中是否存在相同的数字
给定两个排好序的数组,怎样高效得判断这两个数组中存在相同的数字?
这个问题首先想到的是一个O(nlogn)的算法。
就是任意挑选一个数组,遍历这个数组的所有元素,遍历过程中,在另一
个数组中对第一个数组中的每个元素进行binarysearch。
用C++实现代码如下:
boolfindcommon(inta[],intsize1,intb[],intsize2)
{
第1页
数据结构面试大全
inti;
for(i=0;i{
intstart=0,end=size2-1,mid;
while(start<=end)
{
mid=(start+end)/2;
if(a[i]==b[mid])
returntrue;
elseif(a[i]
end=mid-1;
else
start=mid+1;
}
}
returnfalse;
}
后来发现有一个O(n)算法。
因为两个数组都是排好序的。
所以只要一次遍历就行了。
首先设两个下标,分别初始化为两
个数组的起始地址,依次向前推进。
推进的规则是比较两个数组中的数字,小的那个数组的下标向前推进一步,直到任
何一个数组的下标到达数组末尾时,如果这时还没碰到相同的数字,说明数组中没有相同的数字。
boolfindcommon2(inta[],intsize1,intb[],intsize2)
{
inti=0,j=0;
while(i{
if(a[i]==b[j])
returntrue;
if(a[i]>b[j])
j++;
if(a[i]
i++;
}
returnfalse;
}
4,最大子序列
问题:
给定一整数序列A1,A2,...An(可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大
例如:
整数序列-2,11,-4,13,-5,2,-5,-3,12,-9的最大子序列的和为21。
对于这个问题,最简单也是最容易想到的那就是穷举所有子序列的方法。
利用三重循环,依次求出所有子序列的和然后
取最大的那个。
当然算法复杂度会达到O(n^3)。
显然这种方法不是最优的,下面给出一个算法复杂度为O(n)的线性算法
实现,算法的来源于ProgrammingPearls一书。
在给出线性算法之前,先来看一个对穷举算法进行优化的算法,它的算法复杂度为O(n^2)。
其实这个算法只是对对穷举
算法稍微做了一些修改:
其实子序列的和我们并不需要每次都重新计算一遍。
假设Sum(i,j)是A[i]...A[j]的和,那
么Sum(i,j+1)=Sum(i,j)+A[j+1]。
利用这一个递推,我们就可以得到下面这个算法:
intmax_sub(inta[],intsize)
{
inti,j,v,max=a[0];
for(i=0;i{
v=0;
for(j=i;j{
v=v+a[j];//Sum(i,j+1)=Sum(i,j)+A[j+1]
if(v>max)
max=v;
}
}
returnmax;
}
那怎样才能达到线性复杂度呢?
这里运用动态规划的思想。
先看一下源代码实现:
intmax_sub2(inta[],intsize)
{
inti,max=0,temp_sum=0;
for(i=0;i{
temp_sum+=a[i];
if(temp_sum>max)
max=temp_sum;
elseif(temp_sum<0)
temp_sum=0;
}
returnmax;
}
在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。
如果往前扫描中遇到负数,那么当前子序列的和将会减小。
此时temp_sum将会小于max,当然max也
就不更新。
如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。
然后,temp_sum
将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。
这样一趟扫描结果也就出来了。
5,找出单
向链表的中间结点
这道题和解判断链表是否存在环,我用的是非常类似的方法,只不过结束循环的条件和函数返回值不一样罢了。
设置两
个指针p1,p2。
每次循环p1向前走一步,p2向前走两步。
当p2到达链表的末尾时,p1指向的时链表的中间。
link*mid(link*head)
{
link*p1,*p2;
p1=p2=head;
if(head==NULL||head->next==NULL)
returnhead;
do{
p1=p1->next;
p2=p2->next->next;
}while(p2&&p2->next);
returnp1;
}
6,按单词反转字符串
并不是简单的字符串反转,而是按给定字符串里的单词将字符串倒转过来,就是说字符串里面的单词还是保持原来的顺
序,这里的每个单词用空格分开。
例如:
Hereis
经过反转后变为:
isHere
如果只是简单的将所有字符串翻转的话,可以遍历字符串,将第一个字符和最后一个交换,第二个和倒数第二个交换,
依次循环。
其实按照单词反转的话可以在第一遍遍历的基础上,再遍历一遍字符串,对每一个单词再反转一次。
这样每
个单词又恢复了原来的顺序。
char*reverse_word(constchar*str)
{
intlen=strlen(str);
char*restr=newchar[len+1];
strcpy(restr,str);
inti,j;
for(i=0,j=len-1;i{
chartemp=restr[i];
restr[i]=restr[j];
restr[j]=temp;
}
intk=0;
while(k{
i=j=k;
while(restr[j]!
=''&&restr[j]!
='')
j++;
k=j+1;
j--;
for(;i{
chartemp=restr[i];
restr[i]=restr[j];
restr[j]=temp;
}
}
returnrestr;
}
C#实现
stringreversse_word(stringrestr)
{
char[]str=str.ToCharArray();
inti,j;
for(i=0,j=str.Length-1;i{
restr[i]^=restr[j]^=restr[i]^=restr[j];
}
intk=0;
while(k{
i=j=k;
while(str[j]!
=''&&jj++;
k=j+1;
j--;
for(;i{
restr[i]^=restr[j]^=restr[i]^=restr[j];
}
}
returnstr.ToString();
}
如果考虑空间和时间的优化的话,当然可以将上面代码里两个字符串交换部分改为异或实现。
例如将
chartemp=restr[i];
restr[i]=restr[j];
restr[j]=temp;
改为
restr[i]^=restr[j];
restr[j]^=restr[i];
restr[i]^=restr[j];
7,字符串反转
我没有记错的话是一道MSN的笔试题,网上无意中看到的,拿来做了一下。
题目是这样的,给定一个字符串,一个这个字
符串的子串,将第一个字符串反转,但保留子串的顺序不变。
例如:
输入:
第一个字符串:
"Thisisfishsky'sChinesesite:
子串:
"fishsky"
输出:
"nc/nc.moc.fishsky.www//:
ptth:
etisesenihCs'fishskysisihT"
一般的方法是先扫描一边第一个字符串,然后用stack把它反转,同时记录下子串出现的位置。
然后再扫描一遍把记录下
来的子串再用stack反转。
我用的方法是用一遍扫描数组的方法。
扫描中如果发现子串,就将子串倒过来压入堆栈。
最后再将堆栈里的字符弹出,这样子串又恢复了原来的顺序。
源代码如下:
第3页
数据结构面试大全
#include
#include
#include
usingnamespacestd;
//reversethestring‘s1′exceptthesubstring‘token’.
constchar*reverse(constchar*s1,constchar*token)
{
assert(s1&&token);
stackstack1;
constchar*ptoken=token,*head=s1,*rear=s1;
while(*head!
=”)
{
while(*head!
=”&&*ptoken==*head)
{
ptoken++;
head++;
}
if(*ptoken==”)//containthetoken
{
constchar*p;
for(p=head-1;p>=rear;p–)
stack1.push(*p);
ptoken=token;
rear=head;
}
else
{
stack1.push(*rear);
head=++rear;
ptoken=token;
}
}
char*return_v=newchar[strlen(s1)+1];
inti=0;
while(!
stack1.empty())
{
return_v[i++]=stack1.top();
stack1.pop();
}
return_v[i]=”;
returnreturn_v;
}
intmain(intargc,char*argv[])
{
cout<<"Thisisfishsky'sChinesesite:
";
cout<
第5页
数据结构面试大全
otherwise"No".
SampleInput
3
4
6
12
SampleOutput
No
Yes
Yes
No
没什么好说的,解法很简单,解法如下:
#include
#include
usingnamespacestd;
boolisprime(longtest)
{
inti;
for(i=2;i<=sqrt((longdouble)test);i++)
{
if(test%i==0)
returnfalse;
}
returntrue;
}
boolisSemiPrime(longtest)
{
inti;
for(i=2;i<=sqrt((longdouble)test);i++)
{
if(test%i==0)
{
inttemp=test/i;
returnisprime(i)&&isprime(temp);
}
}
returnfalse;
}
intmain()
{
longn;
while(cin>>n&&n!
=0)
{
if(isSemiPrime(n))
cout<<"Yes"<else
cout<<"No"<}
}
12,淘汰赛问题
题目:
Ourschoolisplanningtoholdanewexcitingcomputerprogrammingcontest.Duringeachroundofthe
contest,thecompetitorswillbepaired,andcompetehead-to-head.Theloserwillbeeliminated,andthe
winnerwilladvancetonextround.Itproceedsuntilthereisonlyonecompetitorleft,whoisthe
champion.Inacertainround,ifthenumberoftheremainingcompetitorsisnoteven,oneofthemwillbe
chosedrandomlytoadvancetonextroundautomatically,andthentheotherswillbepairedandfightas
usual.Thecontestcommitteewanttoknowhowmanyroundsisneededtoproducetochampion,thenthey
couldprepareenoughproblemsforthecontest.
Input
Theinputconsistsofseveraltestcases.EachcaseconsistsofasinglelinecontainingaintegerN-
thenumberofthecompetitorsintotal.1<=N<=2,147,483,647.Aninputwith0(zero)signalstheendof
theinput,whichshouldnotbeprocessed.
Output
Foreachtestcase,outputthenumberofroundsneededinthecontest,onasingleline.
SampleInput
8
16
15
0
SampleOutput
3
4
4
题目比较简单,下面是我给的解法。
其实就是计算一个数是2的几次方。
#include
第6页
数据结构面试大全
usingnamespacestd;
longcalculate(longtest)
{
longret=0;
boolis2square=true;
while(test!
=1)
{
if(test%2)
is2square=false;
test/=2;
ret++;
}
if(!
is2square)
ret++;
returnret;
}
intmain()
{
longn;
while(cin>>n&&n!
=0)
{
cout<}
}