第2章 线性表.docx

上传人:b****5 文档编号:5609739 上传时间:2022-12-29 格式:DOCX 页数:12 大小:22.84KB
下载 相关 举报
第2章 线性表.docx_第1页
第1页 / 共12页
第2章 线性表.docx_第2页
第2页 / 共12页
第2章 线性表.docx_第3页
第3页 / 共12页
第2章 线性表.docx_第4页
第4页 / 共12页
第2章 线性表.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

第2章 线性表.docx

《第2章 线性表.docx》由会员分享,可在线阅读,更多相关《第2章 线性表.docx(12页珍藏版)》请在冰豆网上搜索。

第2章 线性表.docx

第2章线性表

第2章线性表

 

一、基础知识题

2.1试述头指针、头结点、元素结点、首元结点的区别,说明头指针和头结点的作

【解答】指向链表第一个结点(或为头结点或为首元结点)的指针称为头指针。

“头指针”具有标识一个链表的作用,所以经常用头指针代表链表的名字,如链表L既是指链表的名字是L,也是指链表的第一个结点的地址存储在指针变量L中,头指针为“NULL”则表示一个空表。

有时,我们在整个线性链表的第一个元素结点之前加入一个结点,称为头结点,它的数据域可以不存储任何信息(也可以做监视哨或存放线性表的长度等附加信息),指针域中存放的是第一个数据结点的地址,空表时为空。

“头结点”的加入,使插入和删除等操作方便统一。

元素结点即是数据结点,至少包括元素自身信息和其后继元素的地址两个域。

首元结点是指链表中第一个数据元素的结点;为了操作方便,通常在链表的首元结点之前附设一个结点,称为头结点。

 

2.2分析顺序存储结构和链式存储结构的优缺点,说明何时应该利用何种结构。

【解答】①从空间上来看,当线性表的长度变化较大,难以估计其规模时,选用动态的链表作为存储结构比较合适,但链表除了需要设置数据域外,还要额外设置指针域,因此当线性表长度变化不大,易于事先确定规模时,为了节约存储空间,宜采用顺序存储结构。

②从时间上看,顺序表具有按元素序号随机访问的特点,在顺序表中按序号访问数据元素的时间复杂度为O

(1);而链表中按序号访问的时间复杂度为O(n)。

所以如果经常按序号访问数据元素,使用顺序表优于链表。

在顺序表中做插入删除操作时,平均移动大约表中一半的元素,因此n较大时顺序表的插入和删除效率低。

在链表中作插入、删除,虽然也要找插入位置,但操作主要是比较操作。

从这个角度考虑显然链表优于顺序表。

总之,两种存储结构各有长短,选择那一种存储结构,由实际问题中的主要因素决定。

 

2.3分析在顺序存储结构下插入和删除结点时平均需要移动多少个结点。

【解答】平均移动表中大约一半的结点,插入操作平均移动个结点,删除操作平均移动个结点。

具体移动的次数取决于表长和插入、删除的结点的位置。

 

2.4为什么在单循环链表中常使用尾指针,若只设头指针,插入元素的时间复杂度如何?

【解答】单循环链表中无论设置尾指针还是头指针都可以遍历表中任一个结点。

设置尾指针时,若在表尾进行插入元素或删除第一元素,操作可在O

(1)时间内完成;若只设置头指针,表尾进行插入或删除操作,需要遍历整个链表,时间复杂度为O(n)。

 

2.5在单链表、双链表、单循环链表中,若知道指针p指向某结点,能否删除该结点,时间复杂度如何?

【解答:

】以上三种链表中,若知道指针p指向某结点,都能删除该结点。

双链表删除p所指向的结点的时间复杂度为O

(1),而单链表和单循环链表上删除p所指向的结点的时间复杂度均为O(n)。

 

2.6下面算法的功能是什么?

LinkedListUnknown(LinkedListla)

{LNode*q,*p;

if(la&&la->next)

{q=la;la=la->next;p=la;

while(p->next)p=p->next;

p->next=q;q->next=null;

}

returnla;

}

【解答】将首元结点删除并插入到表尾(设链表长度大于1)。

 

2.7选择题:

在循环双链表的*p结点之后插入*s结点的操作是()

la、p->next=s;s->prior=p;p->next->prior=s;s->next=p->next;

B、p->next=s;p->next->prior=s;s->prior=p;s->next=p->next;

lc、s->prior=p;s->next=p->next;p->next:

=s;p->next->prior=s;

D、s->prior=p;s>next=p>next;p>next->prior=s;p->next=s;

【解答】D

 

2.8选择题:

若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用()存储方式最节省时间。

la.顺序表B.双链表lc.带头结点的双循环链表D.单循环链表

【解答】la

 

二、算法设计题

2.9设ha和hb分别是两个带头结点的非递减有序单链表的头指针,试设计算法,将这两个有序链表合并成一个非递增有序的单链表。

要求使用原链表空间,表中无重复数据。

【题目分析】因为两链表已按元素值非递减次序排列,将其合并时,均从第一个结点起进行比较,将小的链入链表中,同时后移链表工作指针,若遇值相同的元素,则删除之。

该问题要求结果链表按元素值非递增次序排列,故在合并的同时,将链表结点逆置。

LinkedListUnion(LinkedListha,hb)

∥ha,hb分别是带头结点的两个单链表的头指针,链表中的元素值按递增序排列

∥本算法将两链表合并成一个按元素值递减次序排列的单链表,并删除重复元素

{pa=ha->next;∥pa是链表ha的工作指针

pb=hb->next;∥pb是链表hb的工作指针

ha->next=null;∥ha作结果链表的头指针,先将结果链表初始化为空

while(pa!

=null&&pb!

=null)∥当两链表均不为空时作

{while(pa->next&&pa->data==pa->next->data)

{u=pa->next;pa->next=u->next;free(u)}∥删除pa链表中的重复元素

while(pb->next&&pb->data==pb->next->data)

{u=pb->next;pb->next=u->next;free(u)}∥删除pb链表中的重复元素

if(pa->datadata)

{r=pa->next;∥将pa的后继结点暂存于r

pa->next=ha->next;∥将pa结点链于结果表中,同时逆置

ha->next=pa;

pa=r;∥恢复pa为当前待比较结点

}

elseif(pb->datadata)

{r=pb->next;∥将pb的后继结点暂存于r

pb->next=ha->next;∥将pb结点链于结果表中,同时逆置

ha->next=pb;

pb=r;∥恢复pb为当前待比较结点

}

else{u=pb;pb=pb->next;free(u)}∥删除链表pb和pa中的重复元素

}//while(pa!

=null&&pb!

=null)

if(pa)pb=pa;∥避免再对pa写下面的while语句

while(pb!

=null)∥将尚未到尾的表逆置到结果表中

{r=pb->next;pb->next=ha->next;ha->next=pb;pb=r;}

returnha

}∥算法Union结束

 

2.10             设la是一个双向循环链表,其表中元素递增有序。

试写一算法插入元素x,使表中元素依然递增有序。

【问题分析】双向链表的插入与单链表类似,但需修改双向指针。

DLinkedListDInsert(DLinkedListla,ElemTypex)

∥在递增有序的双向循环链表la中插入元素x,使表中元素依然递增有序

{p=la->next;∥p指向第一元素

la->data=MaxElemType;∥MaxElemType是和x同类型的机器最大值,用做监视哨

while(p->data

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1