if(p->element[i]==x){
/*找到了一个要删除的元素*/
while((p->element[j]==x)&&(i!
=j)){
/*从后往前找不会被删除的元素,当i,j相等时退出循环,count记录从后往前找的过程中遇到了多少个要删的元素*/
j?
?
;
count++;
}
if(i==j){
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
p->n=p->n?
count?
1;
return;
}
else{
p->element[i]=p->element[j];
count++;
j?
?
;
}
}
i++;
}
p->n=p->n?
count;
if(p->element[i]==x)p?
>n?
?
;
}
代价分析
该算法访问顺序表中每个元素各一次,时间代价为O(n)。
?
讨论
这个算法使用了一点技巧使得在中间删除元素时,避免了最后一串元素的移动。
但是,它破坏了原来线性表中元素之间的顺序关系。
如果需要保持原来的顺序应该怎样做?
这里提供一种可行的思路:
从前向后遍历表,如果元素不是x,则继续向后;如果元素是x,则寻找其后第一个不是x的元素,将这两个元素互换。
具体算法请读者自己实现。
6.写一算法,在带头结点的单链表llist中,p所指结点前面插入值为x的新结点,并返回插入成功与否的标志。
【答】
数据结构
采用2.1.3节中单链表定义。
思想:
由于在单链表中,只有指向后继结点的指针,所以只有首先找到p所指结点的前驱结点,然后才能完成插入。
而找p所指结点的前驱结点,只能从单链表的第一个结点开始,使用与locate_link类似的方式进行搜索。
算法:
intinsertPre_link(LinkListllist,PNodep,DataTypex){
/*在llist带头结点的单链表中,p所指结点前面插入值为x的新结点*/
PNodep1;
if(llist==NULL)return0;
p1=llist;
while(p1!
=NULL&&p1->link!
=p)p1=p1->link;/*寻找p所指结点的前驱结点*/
if(p1=NULL)return0;
PNodeq=(PNode)malloc(sizeof(structNode));/*申请新结点*/
if(q=NULL){printf(“Outofspace!
!
!
\n”);return0;}
q->info=x;
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
q->link=p1->link;
p1->link=q;
return1;
}
7.写一算法,在带头结点的单链表llist中,删除p所指的结点,并返回删除成功与否的标志。
【答】
数据结构
采用2.1.3节中单链表定义。
思想:
由于在单链表中,只有指向后继结点的指针,所以只有首先找到p所指结点的前驱结点,然后才能完成删除。
而找p所指结点的前驱结点,只能从单链表的第一个结点开始,使用与locate_link类似的方式进行搜索。
intdeleteP_link(LinkListllist,PNodep){
/*在llist带头结点的单链表中,删除p所指的结点*/
PNodep1;
if(llist==NULL)returnNull;
p1=llist;
while(p1!
=NULL&&p1->link!
=p)p1=p1->link;/*寻找p所指结点的前驱结点*/
if(p1=NULL)return0;
p1->link=p->link;
free(p);/*删除结点p*/
return1;
}
8.已知list是指向无头结点的单链表的指针变量,写出删除该链表下标为i的(第i+1个)结点的算法。
【答】
数据结构
采用2.1.3节中单链表定义。
思路
依次遍历表中的每一个结点并计数,到第i+1个结点时实行删除。
由于链表是无头结点的,所以在删除第一个结点时要特别注意。
算法
intdeleteindex_link_nohead(LinkList*pllist,inti){
/*删除单链表中下标为i的结点。
删除成功返回TRUE,否则返回FALSE。
*/
intj;
PNodep,q;
if((*pllist)==NULL||i<0)returnFALSE;
if(i==0){
/*如果需要删除第一个结点*/
q=(*pllist);
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
(*pllist)=(*pllist)->link;
free(q);
returnTRUE;
}
p=(*pllist);
j=0;
while(p->link!
=NULL&&j
1){/*寻找下标为i?
1的结点的地址*/
p=p->link;
j++;
}
*/
此元素不存在/*if(p->link==NULL)returnFALSE;
q=p->link;
p->link=q->link;
free(q);
returnTRUE;
}
代价分析
该算法访问单链表中前面i个结点,时间代价为O(i),最大不超过O(n)。
?
讨论
如果函数参数不是LinkList*类型而是LinkList类型,在删除i=0的结点时,程序不能正确实现,其原因请读者思考(考虑C语言的参数传递方式)。
如果单链表带表头结点,重写本题的算法。
比较这两个算法,是否能体会到表头结点的作用。
9.已知list是指向无头结点的单链表的指针变量,写出删除该链表中从下标为i的(第i+1个)结点开始的连续k个结点的算法。
【答】
数据结构
采用2.1.3节单链表定义。
思路
这道题与上题相似,只需要增加计数即可。
要注意的是应该判断给出的i和k是否合理,是不是会超出链表长度。
算法
intdel_link_nohead(LinkList*pllist,inti,intk){
/*删除单链表中从下标i开始的k个结点。
删除成功返回TRUE,否则返回FALSE。
*/
intj;
PNodep,q;
if((*pllist)==NULL||i<0||k<=0)returnFALSE;
if(i==0){
/*如果需要删除从第一个结点开始的k个结点*/
for(j=0;j=NULL;j++){
q=(*pllist);
(*pllist)=(*pllist)->link;
free(q);
}
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
returnTRUE;
}
p=(*pllist);
j=0;
while(p->link!
=NULL&&j
1){
/*寻找下标为i?
1的结点的地址*/
p=p->link;
j++;
}
*/
个结点不存在第iif(p->link==NULL)returnFALSE;
/*for(j=0;jlink!
=NULL;j++){
q=p->link;
p->link=q->link;
free(q);
}
returnTRUE;
}
代价分析
该算法访问单链表中前面i+k个结点,时间代价为O(i+k),最大不超过O(n)。
13.请设计一个算法,求出循环表中结点的个数。
【答】
数据结构
采用不带头结点的循环链表。
structNode;
typedefstructNode*PNode;
structNode{
DataTypeinfo;
PNodelink;
};
typedefstructNode*LinkList;
思路
遍历整个循环链表,同时计数即可。
?
错误算法
intcount(LinkListclist){
intcount;
PNodep,q;
p=clist;
q=p->link;
if(clist==NULL)return0;/*如果是空表*/
count=1;
while(q!
=p){
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
q=q->link;
count++;
}
returncount;
}
错误:
如果clist是一个空表,那么第5行的语句“q=p->link;”是非法的。
分析:
应把第6行语句(用下划线表示)提前1行或2行。
一定要放在语句“q=p->link;”之前。
缺点:
增加局部变量p。
分析:
这样做没有必要,因为p的初值置为clist,在程序中并没有对p做其他修改,所以程序中不需要引入p而直接使用clist即可。
算法
intcount(LinkListclist){
intcount;
PNodeq;
if(clist==NULL)return0;/*如果是空表*/
q=clist->link;
count=1;
while(q!
=clist){
q=q->link;
count++;
}
returncount;
}
代价分析
该算法访问循环链表中每个结点各一次,时间代价为O(n)。
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
4.栈与队列
1.写一个递归算法来把整数字符串转换为整数。
(例:
“43567”→43567。
)
【答】
思路
先递归调用本算法转换除去末位外剩余的字符串,结果乘以10。
再转换末位。
将两结果相加即为所求。
算法
intstringToInt1(char*s,intstart,intend){
/*把整数字符串s中从start到end的部分转换为整数*/
if(start>end)return?
1;
/*转换失败*/
if(start==end)returns[end]?
'0';/*只有一个字符,直接转换*/
returnstringToInt1(s,start,end?
1)*10+s[end]?
'0';/*先转换其他位,再转换末位*/
}
/*把整数字符串s转换为整数*/
intstringToInt(char*s){
inti=0;
*/
while(s[i]!
='\0')i++;/*计算字符串的长度
returnstringToInt1(s,0,i?
1);
}
代价分析
设n为字符串的长度。
该算法访问每个字符各一次,时间代价为O(n),计算字符串的长度的时间代价也为O(n)。
故总的时间代价为O(n)。
递归算法需要栈的支持,栈的大小与递归调用的深度成正比。
所以实际空间开销为O(n)。
2.编写一个算法,对于输入的十进制非负整数,将它的二进制表示打印出来。
【答】
数据结构
采用4.1.2节中栈的顺序表示。
思路
将输入的十进制数字反复除以2,直到它变为0。
将每次的数字模2的余数入栈。
栈中存放的就是所要求的二进制表示。
再将栈中的元素依次弹出打印即可。
算法
法拉兹·日·阿卜——学问是异常珍贵的东西,从任何源泉吸收都不可耻。
.
阅读使人充实,会谈使人敏捷,写作使人精确。
——培根
voidprint_bin(intdec_number){/*将十进制非负整数转化为二进制数打印出来*/
PSeqStackpastack;
inttemp=dec_number;
if(temp<0){
printf(Error!
\n);
return;
}
pastack=createEmptyStack_seq();
/*建立一个空栈*/
if(pastack==NULL)return;
while(temp>0){
push_seq(pastack,temp%2);
temp/=2;
}