数据结构文档格式.docx
《数据结构文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构文档格式.docx(16页珍藏版)》请在冰豆网上搜索。
p1!
=p2);
if(p1==p2)
returntrue;
else
}
2,链表反转
单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。
比如一个链表是这样的:
1->
2->
3->
4->
5通过反转后成为5->
1。
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的下一个元素,然后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。
源代码如下:
structlinka{
intdata;
linka*next;
voidreverse(linka*&
head)
if(head==NULL)
return;
linka*pre,*cur,*ne;
pre=head;
cur=head->
while(cur)
ne=cur->
cur->
next=pre;
pre=cur;
cur=ne;
head->
next=NULL;
head=pre;
还有一种利用递归的方法。
这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。
源代码如下。
不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL。
因为要改变head指针,所以我用了引用。
算法的源代码如下:
linka*reverse(linka*p,linka*&
if(p==NULL||p->
next==NULL)
head=p;
returnp;
linka*tmp=reverse(p->
next,head);
tmp->
next=p;
3,判断两个数组中是否存在相同的数字
给定两个排好序的数组,怎样高效得判断这两个数组中存在相同的数字?
这个问题首先想到的是一个O(nlogn)的算法。
就是任意挑选一个数组,遍历这个数组的所有元素,遍历过程中,在另一个数组中对第一个数组中的每个元素进行binarysearch。
用C++实现代码如下:
boolfindcommon(inta[],intsize1,intb[],intsize2)
inti;
for(i=0;
i<
size1;
i++)
intstart=0,end=size2-1,mid;
while(start<
=end)
mid=(start+end)/2;
if(a[i]==b[mid])
elseif(a[i]<
b[mid])
end=mid-1;
start=mid+1;
后来发现有一个O(n)算法。
因为两个数组都是排好序的。
所以只要一次遍历就行了。
首先设两个下标,分别初始化为两个数组的起始地址,依次向前推进。
推进的规则是比较两个数组中的数字,小的那个数组的下标向前推进一步,直到任何一个数组的下标到达数组末尾时,如果这时还没碰到相同的数字,说明数组中没有相同的数字。
boolfindcommon2(inta[],intsize1,intb[],intsize2)
inti=0,j=0;
while(i<
size1&
j<
size2)
if(a[i]==b[j])
returntrue;
if(a[i]>
b[j])
j++;
if(a[i]<
i++;
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];
size;
v=0;
for(j=i;
j<
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;
temp_sum+=a[i];
if(temp_sum>
max=temp_sum;
elseif(temp_sum<
0)
temp_sum=0;
在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。
如果往前扫描中遇到负数,那么当前子序列的和将会减小。
此时temp_sum将会小于max,当然max也就不更新。
如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。
然后,temp_sum将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。
这样一趟扫描结果也就出来了。
5,找出单向链表的中间结点
这道题和解判断链表是否存在环,我用的是非常类似的方法,只不过结束循环的条件和函数返回值不一样罢了。
当p2到达链表的末尾时,p1指向的时链表的中间。
link*mid(link*head)
link*p1,*p2;
p1=p2=head;
if(head==NULL||head->
next==NULL)
returnhead;
do{
p1=p1->
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;
j;
i++,j--)
chartemp=restr[i];
restr[i]=restr[j];
restr[j]=temp;
intk=0;
while(k<
len)
i=j=k;
while(restr[j]!
='
'
&
restr[j]!
\0'
)
k=j+1;
j--;
for(;
returnrestr;
如果考虑空间和时间的优化的话,当然可以将上面代码里两个字符串交换部分改为异或实现。
例如将
改为
restr[i]^=restr[j];
restr[j]^=restr[i];
7,字符串反转
我没有记错的话是一道MSN的笔试题,网上无意中看到的,拿来做了一下。
题目是这样的,给定一个字符串,一个这个字符串的子串,将第一个字符串反转,但保留子串的顺序不变。
输入:
第一个字符串:
"
Thisisfishsky'
sChinesesite:
子串:
fishsky"
输出:
nc/nc.moc.fishsky.www//:
ptth:
etisesenihCs'
fishskysisihT"
一般的方法是先扫描一边第一个字符串,然后用stack把它反转,同时记录下子串出现的位置。
然后再扫描一遍把记录下来的子串再用stack反转。
我用的方法是用一遍扫描数组的方法。
扫描中如果发现子串,就将子串倒过来压入堆栈。
最后再将堆栈里的字符弹出,这样子串又恢复了原来的顺序。
#include<
iostream>
cassert>
stack>
usingnamespacestd;
//reversethestring'
s1'
exceptthesubstring'
token'
.
constchar*reverse(constchar*s1,constchar*token)
assert(s1&
token);
stack<
char>
stack1;
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;
stack1.push(*rear);
head=++rear;
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<
<
"
reverse("
Thisisfishsky'
http:
//www.fishsky"
);
return0;
8,删除数组中重复的数字
一个动态长度可变的数字序列,以数字0为结束标志,要求将重复的数字用一个数字代替,例如:
将数组1,1,1,2,2,2,2,2,7,7,1,5,5,5,0转变成1,2,7,1,5,0
问题比较简单,要注意的是这个数组是动态的。
所以避免麻烦我还是用了STL的vector。
vector>
//removetheduplicatednumbersinanintgerarray,thearraywasendwith0;
//e.g.1,1,1,2,2,5,4,4,4,4,1,0--->
1,2,5,4,1,0
voidstaticremove_duplicated(inta[],vector<
int>
_st)
_st.push_back(a[0]);
for(inti=1;
_st[_st.size()-1]!
=0;
if(a[i-1]!
=a[i])
_st.push_back(a[i]);
当然如果可以改变原来的数组的话,可以不用STL,仅需要指针操作就可以了。
下面这个程序将修改原来数组的内容。
voidstaticremove_duplicated2(inta[])
if(a[0]==0||a==NULL)
intinsert=1,current=1;
while(a[current]!
=0)
if(a[current]!
=a[current-1])
a[insert]=a[current];
insert++;
current++;
a[insert]=0;
9,如何判断一棵二叉树是否是平衡二叉树
判断一个二叉排序树是否是平衡二叉树
解决方案:
根据平衡二叉树的定义,如果任意节点的左右子树的深度相差不超过1,那这棵树就是平衡二叉树。
首先编写一个计算二叉树深度的函数,利用递归实现。
template<
typenameT>
staticintDepth(BSTreeNode<
T>
*pbs)
if(pbs==NULL)
intld=Depth(pbs->
left);
intrd=Depth(pbs->
right);
return1+(ld>
rd?
ld:
rd);
下面是利用递归判断左右子树的深度是否相差1来判断是否是平衡二叉树的函数:
staticboolisBalance(BSTreeNode<
if(pbs==NULL)
intdis=Depth(pbs->
left)-Depth(pbs->
if(dis>
1||dis<
-1)
returnisBalance(pbs->
left)&
isBalance(pbs->
10,strstr()的简单实现
strstr(s1,s2)是一个经常用的函数,他的作用就是在字符串s1中寻找字符串s2如果找到了就返回指针,否则返回NULL。
下面是这个函数的一个简单实现:
staticconstchar*_strstr(constchar*s1,constchar*s2)
assert(s2&
s1);
constchar*p=s1,*r=s2;
while(*p!
while(*p++==*r++);
if(*r=='
r=s2;
p=++s1;
returnNULL;
11,半素数
题目定义了一种叫半素数的数:
只要一个数能被分解成两个素数,那么这个数就是半素数。
PrimeNumberDefinition
Anintegergreaterthanoneiscalledaprimenumberifitsonlypositivedivisors(factors)areoneanditself.Forinstance,2,11,67,89areprimenumbersbut8,20,27arenot.
Semi-PrimeNumberDefinition
Anintegergreaterthanoneiscalledasemi-primenumberifitcanbedecompoundedtoTWOprimenumbers.Forexample,6isasemi-primenumberbut12isnot.
Yourtaskis