数据结构习题.docx
《数据结构习题.docx》由会员分享,可在线阅读,更多相关《数据结构习题.docx(70页珍藏版)》请在冰豆网上搜索。
数据结构习题
2.4 程序设计题
1.设顺序表va中的数据元素递增有序。
试设计一个算法,将x插入到顺序表的适当位置上,以保持该表的有序性。
voidInsert_SqList(SqListva,intx)/*把x插入递增有序表va中*/
{inti;
if(va.length>MAXSIZE)return;
for(i=va.length-1;va.elem[i]>x&&i>=0;i--)
va.elem[i+1]=va.elem[i];
va.elem[i+1]=x;
va.length++;
}/*Insert_SqList*/
2.设A=(a1,a2,…,am)和B=(b1,b2,…,bn)均为顺序表,试设计一个比较A,B大小的算法(请注意:
在算法中,不要破坏原表A和B)。
【算法分析】比较顺序表A和B,并用返回值表示结果,值为1,表示A>B;值为-1,表示A
1)当两个顺序表可以互相比较时,若对应元素不等,则返回值为1或-1;
2)当两个顺序表可以互相比较的部分完全相同时,若表长也相同,则返回值为0;否则,哪个较长,哪个就较大
【算法源代码】intListComp(SqListA,SqListB)
{
for(i=1;i<=A.length&&i<=B.length;i++)
if(A.elem[i]!
=B.elem[i])
returnA.elem[i]>B.elem[i]?
1:
-1;
if(A.length==B.length)return0;
returnA.length>B.length?
1:
-1;
/*当两个顺序表可以互相比较的部分完全相同时,哪个较长,哪个就较大*/
}/*ListComp*/
3.已知指针ha和hb分别指向两个单链表的头结点,并且已知两个链表的长度分别为m和n。
试设计一个算法将这两个链表连接在一起(即令其中一个表的首元结点连在另一个表的最后一个结点之后),假设指针hc指向连接后的链表的头结点,并要求算法以尽可能短的时间完成连接运算。
【算法分析】1)单链表ha的头结点作为连接后的链表的头结点,即hc=ha;
2)查找单链表ha的最后一个结点,由指针p指向,即p->next==NULL;
3)将单链表hb的首元结点(非头结点)连接在p之后,即p->next=hb->next;
4)回收单链表hb的头结点空间
【算法源代码】voidListConcat(LinkListha,LinkListhb,LinkList*hc)/*把链表hb接在ha后面形成链表hc*/
{*hc=ha;
p=ha;/*由指针p指向ha的尾元结点*/
p=p->next;
p->next=hb->next;
free(hb);
}/*ListConcat*/
4.试设计一个算法,在无头结点的动态单链表上实现线性表操作INSERT(L,i,b),并和在带头结点的动态单链表上实现相同操作的算法进行比较。
【算法分析】1)生成新结点存放元素b,由指针new指向;
2)将new插入在单链表的第i个元素的位置上:
若i==1,new插在链表首部;否则查找第i-1个结点,由指针p指向,然后将new插在p之后。
【算法源代码】voidInsert(LinkList*L,inti,intb)
{LinkListnew;
new=(LinkList*)malloc(sizeof(LNode));
new->data=b;
if(i==1)
{/*插入在链表头部*/
New->next=*L;
*L=new;
}
else
{/*插入在第i个元素的位置*/
p=*L;
while(--i>1)p=p->next;
new->next=p->next;p->next=new;
}
}/*Insert*/
5.已知线性表中的元素以值递增有序排列,并以单链表作存储结构。
试设计一个高效的算法,删除表中所有值大于mink且小于maxk的元素(若表中存在这样的元素),同时释放被删结点空间(注意:
mink和maxk是给定的两个参变量。
它们的值可以和表中的元素相同,也可以不同)。
【算法分析】1)查找最后一个不大于mink的元素结点,由指针p指向;
2)如果还有比mink更大的元素,查找第一个不小于maxk的元素,由指针q指向;
3)p->next=q,即删除表中所有值大于mink且小于maxk的元素。
【算法源代码】voidDelete_Between(LinkList*L,intmink,intmaxk)
{p=*L;
while(p->next->data<=mink)p=p->next;/*p是最后一个不大于mink的元素*/
if(p->next)/*如果还有比mink更大的元素*/
{
q=p->next;
while(q->datanext;/*q是第一个不小于maxk的元素*/
p->next=q;
}
}/*Delete_Between*/
6.已知线性表中的元素以值递增有序排列,并以单链表作存储结构。
试设计一个高效的算法,删除表中所有值相同的多余元素(使得操作后的线性表中所有元素的值均不相同),同时释放被删结点空间。
【算法分析】1)初始化指针p和q,分别指向链表中相邻的两个元素;
2)当p->next不为空时,做如下处理:
①若相邻两元素不相等时,p和q都向后推一步;
②否则,当相邻元素相等时,删除多余元素。
【算法源代码】voidDelete_Equal(LinkList*L)
{
p=(*L)->next;q=p->next;/*p和q指向相邻的两个元素*/
while(p->next)
{
if(p->data!
=q->data)/*若相邻两元素不相等时,p和q都向后推一步*/
{
p=p->next;
q=p->next;
}
else
{
while(q->data==p->data)/*当相邻元素相等时删除多余元素*/
{
r=q;
q=q->next;
free(r);
}
p->next=q;p=q;q=p->next;
}/*else*/
}/*while*/
}/*Delete_Equal*/
7.试设计一个算法,对带头结点的单链表实现就地逆置。
【算法分析】1)空表或长度为1的表,不做任何处理;
2)表长大于2时,做如下处理:
①首先将整个链表一分为二,即从链表的第一元素结点处断开;
②逐个地把剩余链表的当前元素q插入到链表的头部。
【算法源代码】voidLinkList_reverse(LinkListL)
{if(!
L->next||!
L->next->next)return;
p=L->next;q=p->next;s=q->next;
p->next=NULL;/*从链表的第一元素结点处断开*/
while(s->next)
{q->next=p;p=q;
q=s;s=s->next;/*把L的元素逐个插入新表表头*/
}
q->next=p;s->next=q;L->next=s;
}/*LinkList_reverse*/
8.设线性表A=(a1,a2,…,am)和B=(b1,b2,…,bn),试设计一个按下列规则合并A,B为线性表C的算法,即使得
C=(a1,b1,…,am,bm,bm+1,…,bn)当m≤n时;
或者
C=(a1,b1,…,an,bn,an+1,…,am)当m>n时。
线性表A,B和C均以单链表作存储结构,且C表利用A表和B表中的结点空间构成。
注意:
单链表的长度值m和n均未显式存储。
【算法分析】1)初始化指针p指向链表A的当前元素,指针q指向链表B的当前元素;
2)当链表A和B均为结束时,做如下处理:
①将B的元素插入
②若A非空,将A的元素插入
③指针p和q同时后移
【算法源代码】voidmerge1(LinkListA,LinkListB,LinkList*C)
{p=A->next;q=B->next;*C=A;
while(p&&q)
{s=p->next;p->next=q;/*将B的元素插入*/
if(s)
{t=q->next;
q->next=s;/*若A非空,将A的元素插入*/
}
p=s;q=t;/*指针p和q同时后移*/
}/*while*/
}/*merge1*/
9.假设有两个按元素值递增有序排列的线性表A和B,均以单链表作存储结构,请设计一个算法将A表和B表归并成一个按元素值递减有序(即非递增有序,允许表中含有值相同的元素)排列的线性表C,并要求利用原表(即A表和B表)的结点空间构造C表。
【算法分析】按从小到大的顺序依次把A和B的元素插入新表的头部pc处,最后处理A或B的剩余元素。
【算法源代码】voidreverse_merge(LinkListA,LinkListB,LinkList*C)
{LinkListpa,pb,pre;
pa=A->next;pb=B->next;/*pa和pb分别指向A和B的当前元素*/
pre=NULL;
while(pa||pb)
{if(pa->datadata||!
pb)/*将A的元素插入新表*/
{pc=pa;q=pa->next;pa->next=pre;pa=q;}
else/*将B的元素插入新表*/
{pc=pb;q=pb->next;pb->next=pre;pb=q;}
pre=pc;
}
*C=A;
A->next=pc;/*构造新表头*/
}/*reverse_merge*/
10.已知A,B和C为三个递增有序的线性表,现要求对A表作如下操作:
删去那些既在B表中出现又在C表中出现的元素。
试对顺序表编写实现上述操作的算法,并分析你的算法的时间复杂度(注意:
题中没有特别指明同一表中的元素值各不相同)。
【算法分析】先从B和C中找出共有元素,记为same,再在A中从当前位置开始,凡小于same的元素均保留(存到新的位置),等于same的就跳过,到大于same时就再找下一个same。
【算法源代码】voidSqList_Intersect_Delete(SqList*A,SqListB,SqListC)
{i=0;j=0;k=0;m=0;/*i指示A中元素原来的位置,m为移动后的位置*/
while(i<(*A).length&&j{if(B.elem[j]elseif(B.elem[j]>C.elem[k])k++;
else{
same=B.elem[j];/*找到了相同元素same*/
while(B.elem[j]==same)j++;
while(C.elem[k]==same)k++;/*j和k后移到新的元素*/
while(i<(*A).length&&(*A).elem[i](*A).elem[m++]=(*A).elem[i++];/*需保留的元素移动到新位置*/
while(i<(*A).length&&(*A).elem[i]==same)i++;/*跳过相同的元素*/
}
}/*while*/
while(i<(*A).length)
(*A).elem[m++]=(*A).elem[i++];/*A的剩余元素重新存储*/
(*A).length=m;
}/*SqList_Intersect_Delete*/
11.设L为单链表的头结点地址,其数据结点的数据都是正整数且无相同的,试设计利用直接插入的原则把该链表整理成数据递增的有序单链表的算法.【算法分析】本题明确指出单链表带头结点,其结点数据是正整数且不相同,要求利用直接插入原则把链表整理成递增有序链表。
这就要求从第二结点开始,将各结点依次插入到有序链表中。
【算法源代码】voidInsertSort(LinkListla)
{if(la->next!
=NULL)/*链表不为空表*/
{p=la->next->next;/*p指向第一结点的后继*/
la->next->next=NULL;
/*直接插入原则认为第一元素有序,然后从第二元素起依次插入*/
while(p!
=NULL)
{r=p->next;/*暂存p的后继*/
q=la;
while(q->next!
=NULL&&q->next->datadata)q=q->next;/*查找插入位置*/
p->next=q->next;/*将p结点链入链表*/
q->next=p;
p=r;
}
12.设有一个双向循环链表,每个结点中除有prior,data和next三个域外,还增设了一个访问频度域freq。
在链表被起用之前,频度域freq的值均初始化为零,而每当对链表进行一次LOCATE(L,X)的操作后,被访问的结点(元素值等于X的结点)中的频度域freq的值便增1,同时调整链表中结点之间的次序,使其按访问频度非递增的次序顺序排列,以便始终保持被频繁访问的结点总是靠近表头结点。
试编写符合上述要求的LOCATE操作的算法。
【算法分析】
1)在双向链表中查找数据值为x的结点,由指针p指向,若找不到,直接返回,否则执行第2步;
2)修改x结点的访问频度freq,并将结点从链表上摘下;
3)顺结点的前驱链查找该结点的位置,即找到一个结点的访问频度大于x结点的访问频度,由指针q指向;若q和p不是相邻结点,调整位置,把p插在q之后。
【算法源代码】DuLNode*Locate_DuList(DuLinkList*L,intx)
{p=(*L)->next;
while(p.data!
=x&&p!
=(*L))p=p->next;
if(p==(*L))returnNULL;/*没找到x结点*/
p->freq++;
p->pre->next=p->next;p->next->pre=p->pre;/*将x结点从链表上摘下*/
q=p->pre;
while(q->freq<=p->freq&&p!
=(*L))q=q->pre;/*查找插入位置*/
if(q!
=p->pre)/*将x结点插入*/
{q->next->pre=p;p->next=q->next;
q->next=p;p->pre=q;/*调整位置*/
}
returnp;
}/*Locate_DuList*/
13.已知三个带头结点的线性链表A、B和C中的结点均依元素值自小至大非递减排列(可能存在两个以上值相同的结点),编写算法对A表进行如下操作:
使操作后的链表A中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。
限定算法的时间复杂度为O(m+n+p),其中m、n和p分别为三个表的长度。
【算法分析】留下三个链表中公共数据,首先查找两表A和B中公共数据,再去C中找有无该数据。
要消除重复元素,应记住前驱,要求时间复杂度O(m+n+p),在查找每个链表时,指针不能回溯。
【算法源代码】LinkListCommon(LinkListA,LinkListB,LinkListC)
{pa=A->next;pb=B->next;pc=C->next;/*pa,pb和pc是工作指针*/
pre=A;
while(pa&&pb&&pc)/*当三表均不空时,查找共同元素*/
{while(pa&&pb)
if(pa->datadata)/*处理pa结点,后移指针*/
{u=pa;pa=pa->next;free(u);}
elseif(pa->data>pb->data)pb=pb->next;
elseif(pa&&pb)/*处理A和B表元素值相等的结点*/
{while(pc&&pc->datadata)pc=pc->next;
if(pc)
{if(pc->data>pa->data)/*处理pa结点,后移指针*/
{u=pa;pa=pa->next;free(u);}
else
{if(pre==A)/*结果表中第一个结点*/
{pre->next=pa;pre=pa;pa=pa->next}
elseif(pre->data==pa->data)/*重复结点不链入A表*/
{u=pa;pa=pa->next;free(u);}
else
{pre->next=pa;pre=pa;pa=pa->next;}/*将新结点链入A表*/
pb=pb->next;pc=pc->next;/*链表的工作指针后移*/
}
}
else
if(pa==NULL)pre->next=NULL;/*若A表已结束,置A表表尾*/
else/*处理原A表未到尾而B或C到尾的情况*/
{pre->next=NULL;/*置A表表尾标记*/
while(pa!
=NULL)/*删除原A表剩余元素。
*/
{u=pa;pa=pa->next;free(u);}
}
}
14.设head为一单链表的头指针,单链表的每个结点由一个整数域data和指针域next组成,整数在单链表中是无序的。
编一函数,将head链中结点分成一个奇数链和一个偶数链,分别由p,q指向,每个链中的数据按由小到大排列。
程序中不得使用malloc申请空间。
【算法分析】本题要求将一个链表分解成两个链表,两个链表都要有序,两链表建立过程中不得使用malloc申请空间,这就是要利用原链表空间,随着原链表的分解,新建链表随之排序。
【算法源代码】discreat(LinkListp,LinkListq,LinkListhead)
{p=NULL;q=NULL;/*p和q链表初始化为空表*/
s=head;
while(s!
=NULL)
{r=s->next;/*暂存s的后继*/
if(s->data%2==0)/*处理偶数*/
if(p==NULL){p=s;p->next=NULL;}/*第一个偶数结点*/
else{pre=p;
if(pre->data>s->data)
{s->next=pre;p=s;}/*插入当前最小值结点*/
else
{while(pre->next!
=NULL)
if(pre->next->datadata)pre=pre->next;/*查找插入位置*/
s->next=pre->next;/*链入结点*/
pre->next=s;}
}
else/*处理奇数链
if(q==NULL){q=s;q->next=NULL;}/*第一奇数结点*/
else
{pre=q;
if(pre->data>s->data){s->next=pre;q=s;}/*修改头指针*/
else
{while(pre->next!
=NULL)/*查找插入位置*/
if(pre->next->datadata)pre=pre->next;
s->next=pre->next;/*链入结点*/
pre->next=s;}
}/*结束奇数链结点*/
s=r;/*s指向新的待排序结点*/
}
}
3.5 程序设计题
1.设表达式以字符形式已存入数组E[n]中,‘#’为表达式的结束符,试写出判断表达式中括号(‘(’和‘)’)是否配对的C语言描述算法:
EXYX(E);(注:
算法中可调用栈操作的基本算法。
)
【算法分析】判断表达式中括号是否匹配,可通过栈,简单说是左括号时进栈,右括号时退栈。
退栈时,若栈顶元素是左括号,则新读入的右括号与栈顶左括号就可消去。
如此下去,输入表达式结束时,栈为空则正确,否则括号不匹配。
【算法源代码】intEXYX(charE[]){
/*E[]存放字符串表达式,以‘#’结束*/
chars[30];/*s是一维数组,容量足够大,用作存放括号的栈*/
inttop=0,i;/*top用作栈顶指针*/
s[top]='#';/*‘#’先入栈,用于和表达式结束符号‘#’匹配*/
i=0;/*字符数组E的工作指针*/
while(E[i]!
='#')/*逐字符处理字符表达式的数组*/
switch(E[i])
{case'(':
s[++top]='(';i++;break;
case')':
if(s[top]=='('){top--;i++;break;}
else{printf("括号不配对");exit(0);}
case'#':
if(s[top]=='#'){printf("括号配对\n");return
(1);}
else{printf("括号不配对\n");return(0);}/*括号不配对*/
default:
i++;/*读入其它字符,不作处理*/
}
}/*算法结束*/
2.假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾结点,但不设头指针,请写出相应的入队列和出队列算法。
【算法分析】
根据队列的先进先出的性质,队列的入队操作在队尾进行,出队操作在队头进行。
而题目所采用的数据结构是只设一个尾指针的循环链表。
我们可以根据循环链表的特点找到头指针。
【算法源代码1】
voidEnQueue(LinkListrear,ElemTypex)
/*rear是带头结点的循环链队列的尾指针,本算法将元素x插入到队尾*/
{s=(LinkList)malloc(sizeof(LNode));/*申请结点空间*/
s->data=x;s->next=rear->next;/*将s结点链入队尾*/
rear->next=s;rear=s;/*rear指向新队尾*/
}
【算法源代码2】
voidDeQueue(LinkListrear)
/*rear是带头结点的循环链队列的尾指针,本算法执行出队操作,操作成功输出队头元素;否则给出出错信息*/
{if(rear->next==rear){printf("队空\n");exit(0);}
s=rear->next->next;/*s指向队头元素*/
rear->next->next=s->next;/*队头元素出队*/
printf("出队元素是:
%d",s->data);
if(s==rear)rear=rear->next;/*空队列*/
free(s);
}
3.设整数序列a1,a2,…,an,给出求解最大值的递归程序。