2.4典型例题
例1顺序表La和Lb的结点的数据元素是整数,La和Lb中的元素非递减有序,线性空间足够大。试编写一个高效算法,将Lb中的元素合并到La中,使新的La的元素仍非递减有序。高效是指最大限度地避免移动元素。
解顺序表的插入的时间复杂度为O(n),平均移动近一半的元素。顺序表La和Lb合并时,若从第一个元素开始,一定会造成元素后移,这不符合本题“高效算法”的要求。另外,题中“线性空间足够大”也暗示出另外的合并方式,即应从顺序表的最后一个元素开始比较,较大者放在最终位置。算法如下:
void unionsqlist(SqList La,SqList &Lb){
int m,n,k,i,j;
m=La.length;n=Lb.length;
k=m+n-1;//La中最后一个元素的位置
i=m-1;j=n-1;//i,j为工作指针
while(i>=0&&j>=0){
if(La.elem[i]>=Lb.elem[j])La.elem[k--]=La.elem[i--];
else La.elem[k--]=Lb.elem[j--];
}
while(j>=0)La.elem[k--]=Lb.elem[j--];
La.length=m+n;
}
例2设L为单链表的头结点指针,单链表的每个结点由一个整数域data和指针域next组成,整数在单链表中是无序的。设计算法,将链表中的结点分成一个奇数链和一个偶数链,分别由P、Q指向,每个链表中的数据按由小到大的顺序排列,算法中不得申请新的结点空间。
解本题要求将一个链表分解成两个链表,两个链表都要有序,两个链表的建立过程中不得申请新的空间,因此原链表无头结点,且新链表要利用原链表的空间,随着原链表的分解,新建链表随之排序。算法如下:
void discreate(LinkList &L,LinkList &P,LinkList &Q){//将链表L分解成两个有序链表P和Q
LNode p,q,s,r,*pre;
p=NULL;Q=NULL;
s=L;
while(s){
r=s->next;
if(s->data mod 2==0){
if(!P){P=s;P->next=NULL;}
else{
pre=P;
if(pre->data>s->data){s->next=pre;P=s;}
else{
while(pre->next){
if(pre->next->datadata)pre=pre->next;
s->next=pre->next;
pre-next=s;
}
}
}
else{
if(!Q){Q=s;Q->next=NULL;}
else{
pre=Q;
if(pre->data>s->data){s->next=pre;Q=s;}
else{
while(pre->next){
if(pre->next->datadata)pre=pre->next;
s->next=pre->next;
pre-next=s;
}
}
}
s=r;
}
例3假设有一个带有头结点的单循环链表,其结点含有三个域prior、data、next。其中data为数据域;prior为指针域,它的值为空指针;next为指针域,它指向后继结点。设计算法,将此表改成双循环链表。
解将具有两个指针域的单循环链表改造成双循环链表,关键是给每个结点均置上指向前驱的指针,而且每个结点的前驱指针置且仅置一次。算法如下:
void setdoublelinklist(LinkList &L){
while(L->next->pre==NULL){
L->next->pre=L;
L=L->next;
}
}
例4在一个递增有序的链表中,有数据相同的元素存在,编写算法删去数据相同的结点,使链表不再有重复的数据元素。
解在一个递增有序的链表中,删去数据相同的结点,要知道被删除结点的前驱结点。算法如下:
void deletesame(LinkList &L){
LNode pre,p,*q;
pre=L->next;//pre指向p所指结点的前驱结点
p=pre->next;//p是工作指针,设表中至少有一个结点
while(p)
if(p->data==pre->data){
q=p;
p=p->next;
delete q;
}
else{
pre->next=p;
pre=p;
p=p->next;
}
pre->next=p;
}