p=p->next ;
s=(DLNode*)malloc(sizeof(DLNode));∥申请结点空间
s->data=x;
s->prior=p->prior;s->next=p;∥将插入结点链入链表
p->prior->next=s;p->prior=s;
}
2.11 设p指向头指针为la的单链表中某结点,试编写算法,删除结点*p的直接前驱结点。
【题目分析】设*p是单链表中某结点,删除结点*p的直接前驱结点,要找到*p的前驱结点的前驱*pre。
进行如下操作:
u=pre->next;pre->next=u->next;free(u);
LinkedListLinkedListDel(LinkedListla,LNode*p)
{∥删除单链表la上的结点*p的直接前驱结点,假定*p存在
pre=la;
if(pre-next==p)
printf(“*p是链表第一结点,无前驱\n”) ;exit(0) ;}
while(pre->next->next!
=p)
pre=pre->next;
u=pre->next;pre->next=u->next;free(u);
return(la);
}
2.12 设计一算法,将一个用循环链表表示的稀疏多项式分解成两个多项式,使这两个多项式各自仅有奇次幂或偶次幂项,并要求利用原链表中的结点空间来构造这两个链表。
【题目分析】设循环链表表示的多项式的结点结构为:
typedefstructnode
{intpower;∥幂
floatcoef;∥系数
ElemTypeother;∥其他信息
structnode*next;∥指向后继的指针
}PNode,*PolyLinkedList;
则可以从第一个结点开始,根据结点的幂是奇数或偶数而将其插入到奇次幂或偶次幂项的链表中。
假定用原链表保存偶次幂,要为奇次幂的链表生成一个表头,为了保持链表中结点的原来顺序,用一个指针指向奇次幂链表的表尾,注意链表分解时不能“断链”。
voidPolyDis(PolyLinkedListpoly)
∥将poly表示的多项式链表分解为各含奇次幂或偶次幂项的两个循环链表
{PolyLinkedListpoly2=(PolyLinkedList)malloc(sizeof(PNode));
∥poly2表示只含奇次幂的多项式
r2=poly2;∥r2是只含奇次幂的多项式链表的尾指针
r1=poly;∥r1是只含偶次幂的多项式链表当前结点的前驱结点的指针
p=poly->next;∥链表带头结点,p指向第一个元素
while(p!
=poly)
if(p->power%2)∥处理奇次幂
{r=p->next;∥暂存后继
r2->next=p;∥结点链入奇次幂链表
r2=p;∥尾指针后移
p=r;∥恢复当前待处理结点
}
else∥处理偶次幂
{r1->next=p;r1=p;p=p->next;}
}
r->next=poly2;r1->next=poly;∥构成循环链表
}∥PolyDis
2.13 以带头结点的双向链表表示的线性表L=(a1,a2,…,an),试写一时间复杂度为O(n)的算法,将L改造为L=(a1,a3,…,an,…,a4,a2)。
【题目分析】分析结果链表,易见链表中位置是奇数的结点保持原顺序,而位置是偶数的结点移到奇数结点之后,且以与原来相反的顺序存放。
因此,可从链表第一个结点开始处理,位置是奇数的结点保留不动,位置是偶数的结点插入到链表尾部,并用一指针指向链表尾,以便对偶数结点“尾插入”。
DLinkedListDInvert(DLinkedListL)
∥将双向循环链表L位置是偶数的结点逆置插入到链表尾部
{p=L->next;∥p指向第一元素
Q=p->prior;∥Q指向最后一个元素
pre=L ;∥pre指向链表中位置为奇数的结点的前驱
r=L ;∥r指向链表中偶数结点的尾结点
i=0 ;∥i记录结点序号
while(p !
=Q)∥寻找插入位置
{i++ ;
if(i%2)∥处理序号为奇数的结点
{p->prior=pre ;pre->next=p ;pre=p;p=p->next;}
else∥处理序号为偶数的结点
{u=p ;∥记住当前结点
p=p->next ;∥p指向下个待处理结点
u->prior=r->prior; ∥以下4个语句将结点插入链表尾
u->next=r;
r->prior->next=u;
r->prior=u;
r=u;∥指向新的表尾
}
}
2.14 设单向链表的头指针为head,试设计算法,将链表按递增的顺序就地排序。
【题目分析】本题中的“就地排序”,可理解为不另辟空间,这里利用直接插入原则把链表整理成递增有序链表。
LinkedListLinkListInsertSort(LinkedListhead)
∥head是带头结点的单链表,本算法利用直接插入原则将链表整理成递增的有序链表
{if(head->next!
=null)∥链表不为空表
{p=head->next->next;∥p指向第一结点的后继
head->next->next=null;
∥直接插入原则认为第一元素有序,然后从第二元素起依次插入
while(p!
=null)
{r=p->next;∥暂存p的后继
q=head;
while(q->next&&q->next->datadata)
q=q->next;∥查找插入位置
p->next=q->next;∥将p结点链入链表
q->next=p;
p=r;
}
}}
2.15 已知递增有序的三个单链表分别代表集合A,B和C,设计算法实现A=A∪(B∩C),并使结果链表仍保持递增。
要求算法的时间复杂度为O(|A|+|B|+|C|)。
其中,|A|为集合A的元素个数。
【题目分析】本题首先求B和C的交集,即求B和C中共有元素,再与A求并集,同时删除重复元素,以保持结果A递增。
LinkedListunion(LinkedListA,B,C)
∥A、B和C均是带头结点的递增有序的单链表,本算法实现A=A∪(B∩C)
∥使结果表A保持递增有序
{pa=A->next;pb=B->next;pc=C->next;∥设置三个工作指针
pre=A;∥pre指向结果链表中当前待合并结点的前驱
A->data=MaxElemType;∥同类型元素最大值,起监视哨作用
while(pa||pb&&pc)
{while(pb&&pc)
if(pb->datadata)pb=pb->next;
elseif(pb->data>pc->data)pc=pc->next;
elsebreak;∥B表和C表有公共元素
if(pb&&pc)
{while(pa&&pa->datadata)∥先将A中小于B,C公共元素部分链入
{pre->next=pa;pre=pa;pa=pa->next;}
if(pre->data!
=pb->data)
{pre->next=pb;pre=pb;pb=pb->next;pc=pc->next;}
else{pb=pb->next;pc=pc->next;}
∥若A中已有B,C公共元素,则不再存入结果表
}
}∥while(pa||pb&&pc)
if(pa)pre->next=pa;
elsepre->next=null;∥当B,C无公共元素,将A中剩余链入
}∥算法Union结束
2.16 顺序表la与lb非递减有序,顺序表空间足够大。
试设计一种高效算法,将lb中元素合到la中,使新的la的元素仍保持非递减有序。
高效指最大限度地避免移动元素。
【题目分析】顺序存储结构的线性表的插入,其时间复杂度为O(n),平均移动近一半的元素。
线性表la和lb合并时,若从第一个元素开始,一定会造成元素后移,这不符合本题“高效算法”的要求。
应从线性表的最后一个元素开始比较,大者放到最终位置上。
设两线性表的长度各为m和n,则结果表的最后一个元素应在m+n位置上。
这样从后向前,直到第一个元素为止。
SeqListUnion(SeqListla,SeqListlb)
∥la和lb是顺序存储的非递减有序线性表,本算法将lb合并到la中,元素仍非递减有序
{m=la.last;n=lb.last;∥m,n分别为线性表la和lb的长度
k=m+n-1;∥k为结果线性表的工作指针(下标)
i=m-1;j=n-1;∥i,j分别为线性表la和lb的工作指针(下标)
while(i>=0&&j>=0)
if(la.data[i]>=lb.data[j])la.data[k--]=la.data[i--];
elsela.data[k--]=lb.data[j--];
while(j>=0)la.data[k--]=lb.data[j--];
la.last=m+n;
returnla;
}
【算法讨论】算法中数据移动是主要操作。
在最佳情况下(lb的最小元素大于la的最大元素),仅将lb的n个元素移(拷贝)到la中,时间复杂度为O(n),最差情况,la的所有元素都要移动,时间复杂度为O(m+n)。
因数据合并到la中,所以在退出第一个while循环后,只需要一个while循环,处理lb中剩余元素。
第二个循环只有在lb有剩余元素时才执行,而在la有剩余元素时不执行。
本算法“最大限度的避免移动元素”,是“一种高效算法”。
2.17 已知非空线性链表由head指出,试写一算法,将链表中数据域值最小的那个结点移到链表的最前面。
要求:
不得额外申请新的链结点。
【题目分析】本题要求将链表中数据域值最小的结点移到链表的最前面。
首先要查找最小值结点。
将其移到链表最前面,实质上是将该结点从链表上摘下(不是删除并回收空间),再插入到链表的最前面。
LinkedListDelinsert(LinkedListhead)
∥本算法将非空线性链表head中数据域值最小的那个结点移到链表的最前面
{p=head->next;∥p是链表的工作指针
pre=head;∥pre指向链表中数据域最小值结点的前驱
q=p;∥q指向数据域最小值结点,初始假定是第一结点
while(p->next)
{if(p->next->datadata){pre=p;q=p->next;}∥找到新的最小值结点
p=p->next;
}
if(q!
=head->next)∥若最小值是第一元素结点,则不需再操作
{pre->next=q->next;∥将最小值结点从链表上摘下
q->next=head->next;∥将q结点插到链表最前面
head->next=q;
}
}∥Delinsert
2.18设la是带头结点的非循环双向链表的指针,其结点中除有prior,data和next外,还有一访问频度域freq,其值在链表初始使用时为0。
当在链表中进行ListLocate(la,x)运算时,若查找失败,则在表尾插入值为x的结点;若查找成功,值为x的结点的freq值增1,并要求链表按freq域值非增(递减)的顺序排列,且最近访问的结点排在频度相同的结点的后面,使频繁访问的结点总是靠近表头。
试编写符合上述要求的ListLocate(la,x)运算的算法,返回找到结点的指针。
【题目分析】首先在双向链表中查找数据值为x的结点,查到后,将结点从链表上摘下,然后再顺结点的前驱链查找该结点的位置。
DLinkListListLocate(DLinkedListL,ElemTypex)
∥L是带头结点的按访问频度递减的双向链表,本算法先查找数据x
∥查找成功时结点的访问频度域增1,最后将该结点按频度递减插入链表中
{DLinkListp=L->next,q;∥p为L表的工作指针,q为p的前驱,用于查找插入位置
while(p&&p->data!
=x)p=p->next;∥查找值为x的结点
if(!
p){printf(“不存在所查结点\n”);exit(0);}
else{p->freq++;∥令元素值为x的结点的freq域加1
p->next->prior=p->prior;∥将p结点从链表上摘下
p->prior->next=p->next;
q=p->prior;∥以下查找p结点的插入位置
while(q!
=L&&q->freqfreq)q=q->prior;
p->next=q->next;q->next->prior=p;∥将p结点插入
p->prior=q;q->next=p;
}
return(p);∥返回值为x的结点的指针
}∥算法结束
2.19 三个带头结点的线性链表la、lb和lc中的结点均依元素值自小至大非递减排列(可能存在两个以上值相同的结点),编写算法对la表进行如下操作:
使操作后的la中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。
限定算法的时间复杂度为O(m+n+p),其中m、n和p分别为三个表的长度。
【题目分析】留下三个链表中公共数据,首先查找两表la和B中公共数据,再去lc中找有无该数据。
要消除重复元素,应记住前驱,要求时间复杂度O(m+n+p),在查找每个链表时,指针不能回溯。
LinkedListlcommon(LinkedListla,lb,lc)
∥本算法使la表留下la、lb和lc三个非递减有序表共同结点,无重复元素
{pa=la->next;pb=lb->next;pc=lc->next;
∥pa,pb和pc分别是la,lb和lc三个表的工作指针
pre=la;
la->data=MaxElemType∥pre是la表当前结点的前驱结点的指针,头结点作监视哨
while(pa&&pb&&pc)∥当la,lb和lc表均不空时,查找三表共同元素
{while(pa&&pa->data==pre->data)
{u=pa;pa=pa->next;free(u);}//删la中相同元素
while(pb&&pc)
if(pb->datadata)pb=pb->next;∥结点元素值小时,后移指针
elseif(pb->data>pc->data)pc=pc->next;
elsebreak;∥处理lb和lc表元素值相等的结点
if(pb&&pc)
{while(pa&&pa->datadata){u=pa;pa=pa->next;free(u);}
if(pa&&pa->data>pc->data){pb=pb->next;pc=pc->next;}
elseif(pa&&pa->data==pc->data)∥pc,p