数据结构考研复习题--第二章--线性表(带答案) 下载本文

第2章 线性表

一 选择题

1.下述哪一条是顺序存储结构的优点?( )【北方交通大学 2001 一、4(2分)】 A.存储密度大 B.插入运算方便 C.删除运算方便 D.可方便地用于各种逻辑结构的存储表示

2.下面关于线性表的叙述中,错误的是哪一个?( )【北方交通大学 2001 一、14(2分)】

A.线性表采用顺序存储,必须占用一片连续的存储单元。 B.线性表采用顺序存储,便于进行插入和删除操作。 C.线性表采用链接存储,不必占用一片连续的存储单元。 D.线性表采用链接存储,便于插入和删除操作。 3.线性表是具有n个( )的有限序列(n>0)。 【清华大学 1998 一、4(2分)】 A.表元素 B.字符 C.数据元素 D.数据项 E.信息项 4.若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用( )存储方式最节省时间。【哈尔滨工业大学 2001 二、1(2分)】

A.顺序表 B.双链表 C.带头结点的双循环链表 D.单循环链表 5.某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用( )存储方式最节省运算时间。【南开大学 2000 一、3】

A.单链表 B.仅有头指针的单循环链表 C.双链表 D.仅有尾指针的单循环链表

6.设一个链表最常用的操作是在末尾插入结点和删除尾结点,则选用( )最节省时间。

A. 单链表 B.单循环链表 C. 带尾指针的单循环链表 D.带头结点的双循环链表

【合肥工业大学 2000 一、1(2分)】

7.若某表最常用的操作是在最后一个结点之后插入一个结点或删除最后一个结点。则采用( )存储方式最节省运算时间。【北京理工大学 2000 一、1(2分)】

A.单链表 B.双链表 C.单循环链表 D.带头结点的双循环链表 8. 静态链表中指针表示的是( ). 【北京理工大学 2001 六、2(2分)】 A. 内存地址 B.数组下标 C.下一元素地址 D.左、右孩子地址 9. 链表不具有的特点是( ) 【福州大学 1998 一、8 (2分)】 A.插入、删除不需要移动元素 B.可随机访问任一元素 C.不必事先估计存储空间 D.所需空间与线性长度成正比 10. 下面的叙述不正确的是( )【南京理工大学 1996 一、10(2分)】 A.线性表在链式存储时,查找第i个元素的时间同i的值成正比 B. 线性表在链式存储时,查找第i个元素的时间同i的值无关

C. 线性表在顺序存储时,查找第i个元素的时间同i 的值成正比 D. 线性表在顺序存储时,查找第i个元素的时间同i的值无关

11. 线性表的表元存储方式有((1))和链接两种。试指出下列各表中使用的是何种存储方式:表1是((2))存储方式;表2是((3))存储方式;表3是((4))存储方式;表

4是((5))存储方式。表左的s指向起始表元。 表元编号 1 2 货号 618 205 数量 40 2 表元间联系 2 3 3 4 5 6 表元编号 1 2 3 4 5 6 表元编号 1 2 3 4 5 6 表元编号 1 2 3 4 5 6 103 501 781 910 货号 618 205 103 501 781 910 货号 618 205 103 501 781 910 货号 618 205 103 501 781 910 15 20 17 24 数量 40 2 15 20 17 24 数量 40 2 15 20 17 24 数量 40 2 15 20 17 24 4 5 6 0 表元间联系 5 1 4 2 6 3 表元间联系 5 1 4 0 6 3 表元间联系 1 1 4 0 6 3 2 0 6 3 1 5 5 2

s→

表1 s→

表2

表3

s→

表4

s→

供选择的答案: A.连续 B.单向链接 C.双向链接 D.不连接 E.循环链接 F.树状 G.网状 H.随机 I.顺序 J.顺序循环

【上海海运学院 1995 二、1(5分)】

12.(1) 静态链表既有顺序存储的优点,又有动态链表的优点。所以,它存取表中第i个元

素的时间与i无关。

(2) 静态链表中能容纳的元素个数的最大数在表定义时就确定了,以后不能增加。 (3) 静态链表与动态链表在元素的插入、删除上类似,不需做元素的移动。

以上错误的是( )【南京理工大学 2000 一、3(1.5分)】 A.(1),(2) B.(1) C.(1),(2),(3) D.(2) 13. 若长度为n的线性表采用顺序存储结构,在其第i个位置插入一个新元素的算法的时间复杂度为( )(1<=i<=n+1)。【北京航空航天大学 1999 一、1(2分)】

2

A. O(0) B. O(1) C. O(n) D. O(n)

14. 对于顺序存储的线性表,访问结点和增加、删除结点的时间复杂度为( )。

A.O(n) O(n) B. O(n) O(1) C. O(1) O(n) D. O(1) O(1)

【青岛大学 2000 五、1(2分)】

15.线性表( a1,a2,?,an)以链接方式存储时,访问第i位置元素的时间复杂性为( )

A.O(i) B.O(1) C.O(n) D.O(i-1)【中山大学 1999 一、2】

16.非空的循环单链表head的尾结点p↑满足( )。【武汉大学 2000 二、10】

A.p↑.link=head B.p↑.link=NIL C.p=NIL D.p= head 17.循环链表H的尾结点P的特点是( )。【中山大学 1998 二、2(2分)】 A.P^.NEXT:=H B.P^.NEXT:= H^.NEXT C.P:=H D.P:=H^.NEXT 18.在一个以 h 为头的单循环链中,p 指针指向链尾的条件是()【南京理工大学1998 一、15(2分)】

A. p^.next=h B. p^.next=NIL C. p^.next.^next=h D. p^.data=-1 19.完成在双循环链表结点p之后插入s的操作是( );【北方交通大学 1999 一、4(3分)】

A. p^.next:=s ; s^.priou:=p; p^.next^.priou:=s ; s^.next:=p^.next; B. p^.next^.priou:=s; p^.next:=s; s^.priou:=p; s^.next:=p^.next; C. s^.priou:=p; s^.next:=p^.next; p^.next:=s; p^.next^.priou:=s ; D. s^.priou:=p; s^.next:=p^.next; p^.next^.priou:=s ; p^.next:=s; 20.在双向循环链表中,在p指针所指向的结点前插入一个指针q所指向的新结点,其修改指针的操作是( )。【北京邮电大学 1998 二、2(2分)】

注:双向链表的结点结构为(llink,data,rlink)。 供选择的答案:

A. p↑.llink:=q; q↑.rlink:=p; p↑.llink↑.rlink:=q; q↑.llink:=q;

B. p↑.llink:=q; p↑.llink↑.rlink:=q ; q↑.rlink:= p; q↑.llink:=p↑.llink;

C. q↑.rlink:=p; q↑.llink:=p↑.llink; p↑.llink↑.rlink:=q; p↑.llink:=q;

D. q↑.llink:=p↑.llink;q↑.rlink:=p; p↑.llink:=q;p↑.llink:=q;(编者按:原题如此)

21.在非空双向循环链表中q所指的结点前插入一个由p所指的链结点的过程依次为:

rlink(p) ← q; llink(p) ← llink(q); llink(q) ← p; ( )

A.rlink(q) ← p B.rlink(llink(q)) ← p C.rlink(llink(p)) ← p D.rlink(rlink(p)) ← p

【北京航空航天大学 2000 一、1(2分)】

22. 双向链表中有两个指针域,llink和rlink,分别指回前驱及后继,设p指向链表中的一个结点,q指向一待插入结点,现要求在p前插入q,则正确的插入为( )【南京理工大学1996 一、1(2分)】

A. p^.llink:=q; q^.rlink:=p; p^.llink^.rlink:=q; q^.llink:=p^.llink;

B. q^.llink:=p^.llink; p^.llink^.rlink:=q; q^.rlink:=p; p^.llink:=q^.rlink;

C. q^.rlink:=p; p^.rlink:=q; p^.llink^.rlink:=q; q^.rlink:=p;

D. p^.llink^.rlink:=q; q^.rlink:=p; q^.llink:=p^.llink; p^.llink:=q; 23.在双向链表指针p的结点前插入一个指针q的结点操作是( )。【青岛大学 2000 五、

2(2分)】

A. p->Llink=q;q->Rlink=p;p->Llink->Rlink=q;q->Llink=q;

B. p->Llink=q;p->Llink->Rlink=q;q->Rlink=p;q->Llink=p->Llink; C. q->Rlink=p;q->Llink=p->Llink;p->Llink->Rlink=q;p->Llink=q; D. q->Llink=p->Llink;q->Rlink=q;p->Llink=q;p->Llink=q;

24.在单链表指针为p的结点之后插入指针为s的结点,正确的操作是:( )。

A.p->next=s;s->next=p->next; B. s->next=p->next;p->next=s; C.p->next=s;p->next=s->next; D. p->next=s->next;p->next=s;

【青岛大学 2001 五、3(2分)】

25.对于一个头指针为head的带头结点的单链表,判定该表为空表的条件是( )

A.head==NULL B.head→next==NULL C.head→next==head D.head!=NULL

【北京工商大学 2001 一、5(3分)】

26. 在双向链表存储结构中,删除p所指的结点时须修改指针( )。

A. (p^.llink)^.rlink:=p^.rlink (p^.rlink)^.llink:=p^.llink; B. p^.llink:=(p^.llink)^.llink (p^.llink)^.rlink:=p;

C. (p^.rlink)^.llink:=p p^.rlink:=(p^.rlink)^.rlink D. p^.rlink:=(p^.llink)^.llink p^.llink:=(p^.rlink)^.rlink;

【西安电子科技大学 1998 一、1(2分)】

27. 双向链表中有两个指针域,llink和rlink分别指向前趋及后继,设p指向链表中的一个结点,现要求删去p所指结点,则正确的删除是( )(链中结点数大于2,p不是第一个结点)

A.p^.llink^.rlink:=p^.llink; p^.llink^.rlink:=p^.rlink; dispose(p); B.dispose(p); p^.llink^.rlink:=p^.llink; p^.llink^,rlink:=p^.rlink; C.p^.llink^.rlink:=p^.llink; dispose(p); p^.llink^.rlink:=p^.rlink; D.以上A,B,C都不对。 【南京理工大学 1997 一、1(2分)】

二、判断

1. 链表中的头结点仅起到标识的作用。( )【南京航空航天大学 1997 一、1(1分)】 2. 顺序存储结构的主要缺点是不利于插入或删除操作。( )【南京航空航天大学1997 一、2(1分)】

3.线性表采用链表存储时,结点和结点内部的存储空间可以是不连续的。( )

【北京邮电大学 1998 一、2(2分)】

4.顺序存储方式插入和删除时效率太低,因此它不如链式存储方式好。( )

【北京邮电大学 2002 一、2(1分)】

5. 对任何数据结构链式存储结构一定优于顺序存储结构。( )【南京航空航天大学 1997 一、3(1分)】

6.顺序存储方式只能用于存储线性结构。( )

【中科院软件所 1999 六、1-2(2分)】【上海海运学院 1997 一、1(1分)】

7.集合与线性表的区别在于是否按关键字排序。( )【大连海事大学 2001 一、5 ( 1分)】

8. 所谓静态链表就是一直不发生变化的链表。( )【合肥工业大学 2000 二、1(1分)】 9. 线性表的特点是每个元素都有一个前驱和一个后继。( )【合肥工业大学2001 二、1(1分)】

10. 取线性表的第i个元素的时间同i的大小有关. ( )【南京理工大学 1997 二、9(2

分)】

11. 循环链表不是线性表. ( )【南京理工大学 1998 二、1(2分)】

12. 线性表只能用顺序存储结构实现。( )【青岛大学 2001 四、2(1分)】 13. 线性表就是顺序存储的表。( )【青岛大学 2002 一、1(1分)】 14.为了很方便的插入和删除数据,可以使用双向链表存放数据。( )

【上海海运学院 1995 一、1(1分)】 【上海海运学院 1997 一、2(1分)】

15. 顺序存储方式的优点是存储密度大,且插入、删除运算效率高。( ) 【上海海运学院 1996 一、1(1分)】 【上海海运学院 1999 一、1(1分)】 16. 链表是采用链式存储结构的线性表,进行插入、删除操作时,在链表中比在顺序存储结构中效率高。 ( ) 【上海海运学院 1998 一、2(1分)】

三、填空

1.当线性表的元素总数基本稳定,且很少进行插入和删除操作,但要求以最快的速度存取线性表中的元素时,应采用_______存储结构。【北方交通大学 2001 二、4】

2.线性表L=(a1,a2,?,an)用数组表示,假定删除表中任一元素的概率相同,则删除一个元素平均需要移动元素的个数是________。【北方交通大学 2001 二、9】

3.设单链表的结点结构为(data,next),next为指针域,已知指针px指向单链表中data为x的结点,指针py指向data为y的新结点 , 若将结点y插入结点x之后,则需要执行以下语句:_______; ______;【华中理工大学 2000 一、4(2分)】

4.在一个长度为n的顺序表中第i个元素(1<=i<=n)之前插入一个元素时,需向后移动________个元素。

【北京工商大学 2001 二、4(4分)】

5.在单链表中设置头结点的作用是________。【哈尔滨工业大学 2000 二、1(1分)】 6.对于一个具有n个结点的单链表,在已知的结点*p后插入一个新结点的时间复杂度为________,在给定值为x的结点后插入一个新结点的时间复杂度为________。【哈尔滨工业大学 2001 一、1(2分)】

7.根据线性表的链式存储结构中每一个结点包含的指针个数,将线性链表分成________和_______;而又根据指针的连接方式,链表又可分成________和________。【西安电子科技大学1998 二、4(3分)】

8. 在双向循环链表中,向p所指的结点之后插入指针f所指的结点,其操作是_______、_______、_______、________。【中国矿业大学 2000 一、1(3分)】 9. 在双向链表结构中,若要求在p 指针所指的结点之前插入指针为s 所指的结点,则需执行下列语句:

s^ .next:=p; s^ .prior:= ________;p^ .prior:=s;________:=s; 【福州大学 1998 二、7 (2分)】

10.链接存储的特点是利用________来表示数据元素之间的逻辑关系。【中山大学 1998 一、1 (1分)】

11.顺序存储结构是通过________表示元素之间的关系的;链式存储结构是通过________表示元素之间的关系的。【北京理工大学 2001 七、2 (2分)】

12. 对于双向链表,在两个结点之间插入一个新结点需修改的指针共 ______个,单链表为_______个。

【南京理工大学 2000 二、2 (3分)】

13. 循环单链表的最大优点是:________。【福州大学 1998 二、3 (2分)】

14. 已知指针p指向单链表L中的某结点,则删除其后继结点的语句是:________

【合肥工业大学 1999 三、2 (2分)】

15. 带头结点的双循环链表L中只有一个元素结点的条件是:________

【合肥工业大学 1999 三、3 2000 三、2(2分)】

16. 在单链表L中,指针p所指结点有后继结点的条件是:__ 【合肥工业大学 2001 三、3 (2分)】

17.带头结点的双循环链表L为空表的条件是:________。 【北京理工大学 2000 二、1 (2分)】 【青岛大学 2002 三、1 (2分)】 18. 在单链表p结点之后插入s结点的操作是:_______。【青岛大学 2002 三、2(2分)】 19. 请在下列算法的横线上填入适当的语句。【清华大学 1994 五 (15分)】

FUNCTION inclusion(ha,hb:linklisttp):boolean;

{以ha和hb为头指针的单链表分别表示有序表A和B,本算法判别表A是否包含在表B内,若是,则返回“true”,否则返回“false”}

BEGIN

pa:=ha^.next; pb:=hb^.next; (1) ; WHILE(2) DO

IF pa^.data=pb^.data THEN(3) ELSE(4) ; (5) END;

20.完善算法:已知单链表结点类型为:

TYPE ptr=^node; node=RECORD

data:integer; next:ptr

END;

过程create建立以head为头指针的单链表。 PROCEDURE create ( (1) ); VAR p,q:ptr; k:integer; BEGIN

new(head); q:=head;read(k);

WHILE k>0 DO BEGIN

(2); (3); (4); (5); read(k) END;

q^.next:=NIL;

END;【北京师范大学 1999 三】 21. 已给如下关于单链表的类型说明: TYPE

list=^node ;

node=RECORD

data: integer; next: list; END;

以下程序采用链表合并的方法,将两个已排序的单链表合并成一个链表而不改变其排序性(升序),这里两链表的头指针分别为p和q.

PROCEDURE mergelink(VAR p,q:list):

VAR h,r: list; BEGIN

(1)______

h^.next:= NIL; r:=h;

WHILE((p<>NIL) AND (q<>NIL)) DO IF (p^.data<=q^.data)

THEN BEGIN (2)___; r:=p; p:=p^.next; END ELSE BEGIN (3)____; r:=q; q:=q^.next; END; IF (p=NIL) THEN r^.next:=q; (4)__;

p:=h^.next; dispose(h);

END;【厦门大学 2000 三、2 (8分)】

22.假设链表p和链表q中的结点值都是整数,且按结点值的递增次序链接起来的带表头结点的环形链表。各链表的表头结点的值为max,且链表中其他结点的值都小于max,在程序中取max为9999。在各个链表中,每个结点的值各不相同,但链表p和链表q可能有值相同的结点(表头结点除外)。下面的程序将链表q合并到链表p中,使得合并后的链表是按结点值递增次序链接起来的带表头结点的环形链表,且链表中各个结点的值各不相同。请在划线处填上适当内容,每个框只填一个语句或一个表达式,链表的结点类型如下

TYPE nodeptr=^nodetype; nodetype=RECORD

data:integer; link:nodeptr;

END;

CONST max=9999;

PROCEDURE merge(VAR p:nodeptr;q:nodeptr); VAR r,s: nodeptr; BEGIN r:=p;

WHILE (A)___ DO BEGIN

WHILE r^.link^.dataq^.link^.data THEN BEGIN s:=(C)_; (D)_:=s^.link; s^.link:=(E)_; (F)_ _:=s; (G)_; END

ELSE BEGIN (H)__; s:=q^.link; (I)__; dispose(s) END END;

dispose(q)

END;【复旦大学 1997 五(18分)】

23.PROC ins__linklist(la:linkisttp; i:integer; b:elemtp);

{la为指向带头结点的单链表的头指针,本算法在表中第i个元素之前插入元素b} p:=(1) ; j:=(2) ;{指针初始化,j为计数器}

WHILE (p<>NIL) AND ((3) ) DO [p:=(4) ; j:=j+1;] {寻找第 i-1 个结点}

IF (p=NIL) OR ((5) )

THEN error (‘No this position’)

ELSE [new(s) ; s↑.data:=b; s↑.next:=p↑.next; p↑.next:=s;] ENDP;{ins-linklist}【燕山大学 1998 四、1(15分)】 24. 已知双链表中结点的类型定义为:

TYPE dpointer=^list;

list=RECORD

data:integer; left,right:dpointer; END;

如下过程将在双链表第i个结点(i>=0)之后插入一个元素为x的结点,请在答案栏给出题目中______处应填入的语句或表达式,使之可以实现上述功能。 PROCEDURE insert(VAR head:dpointer;i,x:integer); VAR s,p:dpointer; j: integer; BEGIN

new(s); s^.data:=x;

IF(i=0)THEN BEGIN s^.right:=head; (1)___ head:=s END{如果i=0,则将s结点插入到表头后返回}

ELSE BEGIN p:=head; (2)_______;{在双链表中查找第i个结点,由p所指向}

WHILE ((p<>NIL) AND (jNIL THEN

IF (p^.right=NIL)

THEN BEGIN p^.right:=s; s^.right:=NIL; (4) __END

ELSE BEGIN s^.right:=p^.right; (5) _;p^.right:=s; (6) END ELSE writeln(‘can not find node!’) END

END;【厦门大学 2002 二 (12分)】

25.阅读以下算法,填充空格,使其成为完整的算法。其功能是在一个非递减的顺序存储线性表中,删除所有值相等的多余元素。 CONST maxlen=30

TYPE sqlisttp=RECORD

elem:ARRAY[1..maxlen] OF integer; last:0..maxlen END;

PROC exam21(VAR L:sqlisttp); j:=1; i:=2;

WHILE (1)______ DO

[ IF L.elem[i]<>L.elem[j] THEN [ (2)_______; (3)______]; i:=i+1 ] (4) ________;

ENDP;【同济大学 2000 二、1 (10分)】

26.在本题的程序中,函数过程Create_link_list(n)建立一个具有n个结点的环形链表;程序过程 josephus(n,i,m)对由Create_link_list(n)所建立的具有n个结点的环形链表按一定的次序逐个输出并删除链表中的所有结点,参数 n(n>0)指明环形链表的结点个数,参数 i(1<=i<=n)指明起始结点,参数 m (m>0)是步长,指明从起始结点或前次被删除并输出的结点之后的第m个结点作为本次被输出并删除的结点。例如,对于下图中具有6个结点的环形链表,在调用 josephus(6,3,2)后,将输出 5,1,3,6,4,2 请在横线处填上适当内容,

每空只填一个语句。

TYPE nodeptr=^nodetype; nodetype=RECORD

data: intrger; link: nodeptr

END; VAR n,i,m: integer;

FUNCTION Create_link_list(n: integer): nodeptr; VAR head,p,q: nodeptr; i:integer; BEGIN head := NIL; IF n>0 THEN

BEGIN new(head); p: =head; FOR i:=1 TO n-1 DO

BEGIN p^.data:=i; new(q); (A)____; (B)____ END; p^.data:=n; (C)___; END;

Creat_link_list:=head

END;

PROCEDURE josephus(n,i,m:integer); VAR p,q:nodeptr; j:integer; BEGIN p:=Creat_link_list(n);

WHILE i>1 DO BEGIN p:=p^.link; i:=i-1 END; (D)___ ;

WHILE j

FOR i:=1 TO m-1 DO p:=p^.link;

(E)___; write(q^.data:8); (F)__ ;

dispose(q); j:=j+1

END

END;【复旦大学 1997 四(12分)】

27.对于给定的线性链表head , 下面的程序过程实现了按结点值非降次序输出链表中的所有结点,在每次输出一个结点时,就把刚输出的结点从链表中删去。请在划线处填上适当的内容,使之成为一个完整的程序过程,每个空框只填一个语句。

TYPE nodeptr =^ nodetype;

nodetype = RECORD

data : integer;link : nodeptr

END;

VAR head : nodeptr;

PROCEDURE sort_output_delete (head : nodeptr); VAR p,q,r,s: nodeptr;

BEGIN WHILE head <> NIL DO

BEGIN p:= NIL ;q:= head;r:= q ;s:=q^.link ; WHILE s <> NIL DO

BEGIN IF s^.data < q^.data THEN BEGIN (1)__; (2)___ END ;

r:= s ; (3)___

END;

write(q^.data : 5) ;

IF p=NIL THEN (4)___ ELSE (5)____ ;

dispose (q) ;

END;

writeln END;【复旦大学 1996 七(20分) 1995 一(12分)与本题相似】 28.下面函数的功能是在一个按访问频度不增有序的,带头结点的双向链环上检索关键值为x的结点,对该结点访问频度计数,并维护该链环有序。若未找到,则插入该结点。所有结点的频度域初值在建表时都为零。请将程序中四处空缺补写完整。 TYPE

link=^node node=RECORD

key:char; freq:integer; pre,next:link; END;

VAR l:link;

FUNCTION loc(l:link;x:char):link; VAR p,q:link; BEGIN

p:=l^.next; (1)_;

WHILE p^.key<>x DO p:=p^.next;

IF p=l THEN [ new(q); q^.key:=x; q^.freq:=0 ]

ELSE {找到}

[ p^.freq:=p^.freq+1; q:=p; (2)______; WHILE q^.freq>p^.pre^.freq DO p:=p^.pre; IF p<>q THEN [ (3)______ ] ];

IF (4)_ THEN [q^.next:=p, q^.pre;=p^.pre; p^.pre^.next:=q; p^.pre:=q] return(q);

END;【北京工业大学 1999 五 (12分)】

29.循环链表a和b的结点值为字母,其中a表非递减有序,下面的程序欲构造一个递增有序的循环链表c,其中结点的值为同时在a,b两链表中出现的字母,且c中字母不重复,请补上程序中空缺的部分,并估计算法的时间复杂度。(设a,b的结点数分别为m,n) TYPE

link=^node; node=RECORD

key:char; next:link END;

PROC jj(a,b:link; VAR c:link); VAR p,q,r,s:link; BEGIN

new(c);c^.next:=c; q:=a; p:=a^.next; WHILE p<>a DO [(1)___;

WHILE p^.key=p^.next^.key DO [q:=p; p=p^.next];{跳过相同字母} r:=b^.next ; (2)_____;

WHILE r^.key <>p^.key DO r:=r^.next; IF r<>b THEN

[ s:=p; q^.next:=p^.next; (3) ; s^.next:=c^.next; c^.next:=s; c:=s ] ELSE [ q:=p; p:=p^.next ] ]; c:=c^.next;

END;

算法时间复杂度为O(4)___ 【北京工业大学 2000 四 (15分)】

30. 以下程序的功能是实现带附加头结点的单链表数据结点逆序连接,请填空完善之。 void reverse(pointer h)

/* h为附加头结点指针;类型pointer同算法设计第3题*/ { pointer p,q;

p=h->next; h->next=NULL; while((1)________)

{q=p; p=p->next; q->next=h->next; h->next=(2)________; } }【西南交通大学 2000 一、9】

31. 下面是用c语言编写的对不带头结点的单链表进行就地逆置的算法,该算法用L返回逆置后的链表的头指针,试在空缺处填入适当的语句。

void reverse(linklist &L){

p=null;q=L; while(q!=null)

{ (1) ; q->next=p;p=q;(2)___ ; }

(3)_____;

}【北京理工大学 2001 九、1 (6分)】

32.下面程序段是逆转单向循环链表的方法,p0 是原链表头指针,逆转后链表头指针仍为p0。

(可以根据需要增加标识符) p:= p0; q0:=NIL;

WHILE (1)________ DO

BEGIN (2)________; (3)________;(4)______;(5)________ END; p^.next:= q0; p0 ^.next:=p; p0:=p;【中国人民大学 2000 二、1(4分)】 33.一个无头结点的线性链表(不循环)有两个域。数据域data,指针域 next,链首head,下面算法用read(num)读入数据,当num小于0时,输入结束。建立一个数据以递增序组成的链表。

PROC insert( head, x);

{在链首为head的表中按递增序插入x} new(r);r^.data:=x; IF head=NIL

THEN[ head:=(1) _____; r^.next:= (2)________ ] ELSE IF (3)___ THEN [r^ .next:=head; head:=r]

ELSE [p:=head;

WHILE (4)___ AND (p^.next≠NIL ) DO[q:=p;

(5)___ ];

IF (6)___ THEN [ q^ .next:=(7)___; r^.next:=

(8)____; ]

ELSE [p^.next:=(9)____; r^.next:=

(10)___; ]

]

ENDP;

PROC creat(head);

head:= (11)______; read(num); WHILE num>0 DO

[ insert(head,num); read(num) ] ENDP;【南京理工大学 1999 三、4(11分)】

34. 一元稀疏多项式以循环单链表按降幂排列,结点有三个域,系数域coef ,指数域exp和指针域 next;现对链表求一阶导数 ,链表的头指针为ha,头结点的exp域为 –1。

derivative(ha)

{ q=ha ; pa=ha->next; while( (1)_______)

{ if ( (2)____) { ( (3)__); free(pa); pa= ( (4) _); }

else{ pa->coef ( (5) ___); pa->exp( (6)___); q=( (7) __);} pa=( (8)________); }

} 【南京理工大学 2000 三、3(10分)】

35.下面是删除单链表L中最大元素所在结点的类PASCAL语言算法,请在横线填上内容,完成其功能。

TYPE pointer =↑node; node=RECORD

data:integer; next: pointer END;

PROCEDURE delmax (L:pointer); VAR p,q,r:pointer; m:integer; BEGIN

r:=L; p:=L↑.next; IF p<>NIL THEN

[ m:=p↑.data; (1)________; p:=p↑.next;

WHILE p<>NIL DO

[ IF (2)________THEN [ (3)________ ; m:=p↑.data; ]

(4)________; p:=p↑.next; ]

q:=r↑.next; (5)______; dispose(q);

]

END;【北京科技大学 1998 二】

36.对单链表中元素按插入方法排序的C语言描述算法如下,其中L为链表头结点指针。请填充算法中标出的空白处,完成其功能。 typedef struct node

{int data; struct node *next; }linknode,*link; void Insertsort(link L) { link p,q,r,u;

p=L->next; (1)______; while((2)________) { r=L; q=L->next;

while((3)________&& q->data<=p->data) {r=q; q=q->next;} u=p->next; (4)______; (5)______; p=u; }

}【北京科技大学 2001 二 (10分)】

37.下面是一个求两个集合A和B之差C=A-B的程序,即当且仅当e是A的一个元素,但不是B中的一个元素时,e才是C中的一个元素。集合用有序链表实现,初始时,A,B集合中的元素按递增排列,C为空;操作完成后A,B保持不变,C中元素按递增排列。下面的函数append(last,e)是把值为e的新结点链接在由指针last指向的结点的后面,并返回新结点的地址;函数difference(A,B)实现集合运算A-B,并返回表示结果集合C的链表的首结点的地址。在执行A-B运算之前,用于表示结果集合的链表首先增加一个附加的表头结点,以便新结点的添加,当A-B运算执行完毕,再删除并释放表示结果集合的链表的表头结点。 程序(a)(编者略去这个PASCAL程序) 程序(b)

typedef struct node{ int element; struct node *link; }NODE; NODE *A,*B,*C;

NODE *append (NODE *last,int e)

{ last->link=(NODE*) malloc (sizeof(NODE));

last->link->element=e; return(last->link); }

NODE *difference(NODE *A,NODE *B) {NODE *C,*last;

C=last=(NODE*) malloc (sizeof(NODE)); while (1)___

if (A->elementelement) { last=append(last,A->element); A=A->link; }

else if (2) ___ { A=A->link; B=B->link; } ELSE (3) ___ ; while (4) __

{ last=append(last,A->element); A=A->link; }

(5) ___; last=C; C=C->link; free (last); return (C); }

/*call form:C=difference(A,B);*/【上海大学 2000 一、4 (10分)】

四 应用题

1.线性表有两种存储结构:一是顺序表,二是链表。试问:

(1)如果有 n个线性表同时并存,并且在处理过程中各表的长度会动态变化,线性表的总数也会自动地改变。在此情况下,应选用哪种存储结构? 为什么?

(2)若线性表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取线性表中的元素,那么应采用哪种存储结构?为什么?【西安电子科技大学 1999软件 二、1 (5分)】

2.线性表的顺序存储结构具有三个弱点:其一,在作插入或删除操作时,需移动大量元素;其二,由于难以估计,必须预先分配较大的空间,往往使存储空间不能得到充分利用;其三,表的容量难以扩充。线性表的链式存储结构是否一定都能够克服上述三个弱点,试讨论之。【重庆大学 2000 二、5】 3.若较频繁地对一个线性表进行插入和删除操作,该线性表宜采用何种存储结构?为什么?

【北京航空航天大学 1998 一、2(4分)】

4.线性结构包括______、______、_______和_______。线性表的存储结构分成______和______。请用类PASCAL语言描述这两种结构。【华北计算机系统工程研究所1999一、2(10分)】

5.线性表(a1,a2,?,an)用顺序映射表示时,ai和ai+1(1<=i

【东南大学 1996 一、1 (5分)】

6. 说明在线性表的链式存储结构中,头指针与头结点之间的根本区别;头结点与首元结点的关系。

【厦门大学 2000 五、1 (14%/3分)】

7. 试述头结点,首元结点,头指针这三个概念的区别. 【武汉交通科技大学 1996 二、2 (3分)】【西安电子科技大学2001计应用 二、1(5分)】 8. 已知有如下定义的静态链表: TYPE component=RECORD

data:elemtp; next:0..maxsize END

VAR stalist:ARRAY[0..maxsize] OF component;

以及三个指针:av指向头结点,p指向当前结点,pre指向前驱结点,现要求修改静态链表中next域中的内容,使得该静态链表有双向链表的功能,从当前结点p既能往后查找,也能往前查找:

(1) 定义next域中的内容。(用老的next域中的值表示); (2) 如何得到当前结点p的前驱(pre)的前驱,给出计算式;

(3) 如何得到p的后继,给出计算式;【中科院计算所 2000 四(10分)】 9. 在单链表和双向链表中,能否从当前结点出发访问到任何一个结点?

【西安电子科技大学1999计应用一、1 (5分)】

10. 如何通过改链的方法,把一个单向链表变成一个与原来链接方向相反的单向链表?

【中国人民大学 2001 二、4 (2分)】

11. 下面是一算法的核心部分,试说明该算法的功能。

pre:=L↑.next;

{L是一单链表,结点有数据域 data和指针域 next} IF pre<>NIL THEN

WHILE pre↑.next<>NIL DO

BEGIN p:=pre↑.next; IF p↑.data>=pre↑.data THEN pre:=p ELSE

return(false) END;

return(true); 【燕山大学 2000 七、1 (7分)】

12. 设单链表结点指针域为next,试写出删除链表中指针p所指结点的直接后继的C语言语句。

【北京科技大学 2000 一、3】

13. 设单链表中某指针p所指结点(即p结点)的数据域为data,链指针域为next,请写出在p结点之前插入s结点的操作(PASCAL语句)。【北京科技大学 1999 一、2 (2分)】 14. 有线性表(a1,a2,?,an),采用单链表存储,头指针为H,每个结点中存放线性表中一个元素,现查找某个元素值等于X的结点。分别写出下面三种情况的查找语句。要求时间尽量少。

(1)线性表中元素无序。(2)线性表中元素按递增有序。 (3)线性表中元素按递减有序。

【北京邮电大学 1994 七 (7分)】

15.设pa,pb分别指向两个带头结点的有序(从小到大)单链表。仔细阅读如下的程序,并回答问题:

(1) 程序的功能。(2) s1,s2中值的含义。(3) pa,pb中值的含义。 PROCEDURE exam(pa,pb) BEGIN

p1:=pa↑.next; p2:=pb↑.next; pa↑.next:=∧; s1:=0; s2:=0; WHILE p1≠∧ AND p2≠∧ DO

[ CASE p1↑.data

dispose(p) ];

p1↑.data>p2↑.data: p2:=p2↑.next;

p1↑.data=p2↑.data: [p:=p1; p1:=p1↑.next; p↑.next:= pa↑.next;

pa↑.next:= p; p2:= p2↑.next;s1:=s1+1; ];

END ];

WHILE p1≠∧ DO [ p:=p1; p1:=p1↑.next; dispose(p); s2:=s2+1 ] END;【南京航空航天大学 1995 十 (9分)】

16.写出下图双链表中对换值为23和15的两个结点相互位置时修改指针的有关语句。

结点结构为:(llink,data,rlink) 【北京邮电大学 1992 三、4 (25/4分)】

p

10231530

17.按照下列题目中的算法功能说明,将算法描述片段中的错误改正过来。

(1) (4分)下面的算法描述片段用于在双链表中删除指针变量p所指的结点:

p^.rlink←p^.llink^.rlink; p^.llink←p.^rlink^.llink dispose(p);

(2) (6分)下面的算法描述片段用于在双链表中指针变量p所指结点后插入一个新结点:

new(q);

q^.llink←p; p^.rlink←q;

q^.rlink←p^.rlink;

q←p^.rlink^.llink; 【山东大学 1999 八(10分)】

18.已知L是一个数据类型linkedlist的单循环链表,pa和pb是指向L中结点的指针。简述下列程序段的功能。【山东科技大学 2001 一、2 (5分)】

TYPE linkedlist=↑node; node=RECORD

data:datatype; next:linkedlist

END;

PROC Mp(pa,pb:linkedlist); PROC subp(s,q: linkedlist); p:=s;

WHILE p↑.next<>q DO p:=p↑.next; p↑.next:=s ENDP;

subp(pa,pb); subp(pb,pa); ENDP;

19.设双向循环链表中结点的数据域、前驱和后继指针域分别为data,pre和next,试写出在指针p 所指结点之前插入一s结点的C语言描述语句。【北京科技大学 2001 一、3 (2分)】

20.本题给出一个子程序的框图,如图2,试填空完善此算法框图。该子程序用来寻找第一个均出现在三个整数单向链表f1,f2,f3中的相同整数。假定在调用该子程序前,这三个整数链表已按从小到大的次序排序,单向链表的形式如下图1的例子所示。

注:在图2的框图中:found和exit均为布尔型的变量,可取值为true和false。val是整型变量,用来存放第一个均出现在f1,f2,f3中的相同整数。若f1,f2和f3中无相同的整数,found 的值为false,否则found的值为true。f1↑.link表示访问f1所指结点的link域。

【哈尔滨工业大学 1999 三 (15分)】

21. 一线性表存储在带头结点的双向循环链表中,L为头指针。如下算法:

(1)说明该算法的功能。(2)在空缺处填写相应的语句。 void unknown (BNODETP *L)

{ ?

p=L->next; q=p->next; r=q->next; while (q!=L)

{ while (p!=L) && (p->data>q->data) p=p->prior;

q->prior->next=r;(1) ______; q->next=p->next;q->prior=p;

(2) ______;(3) ______; q=r;p=q->prior; (4) ______; }

} 【北京理工大学 1999 第二部分 数据结构 [7] (8分)】

五、算法设计题

1. 假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两

个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。

【北京大学 1998 三、1 (5分)】 类似本题的另外叙述有:

(1)设有两个无头结点的单链表,头指针分别为ha,hb,链中有数据域data,链域next,两链表的数据都按递增序存放,现要求将hb表归到ha表中,且归并后ha仍递增序,归并中ha表中已有的数据若hb中也有,则hb中的数据不归并到ha中,hb的链表在算法中不允许破坏。【南京理工大学1997 四、3(15分)】

PROCEDURE merge(ha,hb); (2)已知头指针分别为la和lb 的带头结点的单链表中,结点按元素值非递减有序排列。写出将la 和 lb两链表归并成一个结点按元素值非递减有序排列的单链表(其头指针为 lc),并计算算法的时间复杂度。【燕山大学 1998 五 (20分)】

2. 图(编者略)中带头结点且头指针为ha和hb的两线性表A和B 分别表示两个集合。两表中的元素皆为递增有序。请写一算法求A和B的并集AUB。要求该并集中的元素仍保持递增有序。且要利用A和B的原有结点空间。【北京邮电大学 1992 二 (15分)】

类似本题的另外叙述有: (1) 已知递增有序的两个单链表A,B分别存储了一个集合。设计算法实现求两个集合的并集的运算A:=A∪B【合肥工业大学 1999 五、1(8分)】

(2)已知两个链表A和B分别表示两个集合,其元素递增排列。编一函数,求A与B的交集,并存放于A链表中。【南京航空航天大学 2001 六(10分)】 (3)设有两个从小到大排序的带头结点的有序链表。试编写求这两个链表交运算的算法(即L1∩L2)。要求结果链表仍是从小到大排序,但无重复元素。【南京航空航天大学 1996 十一(10分)】

(4)己知两个线性表A ,B均以带头结点的单链表作存储结构,且表中元素按值递增有序排列。设计算法求出A与B的交集C,要求C另开辟存储空间,要求C同样以元素值的递增序的单链表形式存贮。

【西北大学 2000 五 ( 8分)】

(5)已知递增有序的单链表A,B和C分别存储了一个集合,设计算法实现A:=A∪(B∩C),并使求解结构A仍保持递增。要求算法的时间复杂度为O(|A|+|B|+|C|)。其中,|A|为集合A的元素个数。

【合肥工业大学 2000 五、1(8分)】

3. 知L1、L2分别为两循环单链表的头结点指针,m,n分别为L1、L2表中数据结点个数。要求设计一算法,用最快速度将两表合并成一个带头结点的循环单链表。【东北大学1996 二 (12分)】

类似本题的另外叙述有:

(1)试用类Pascal语言编写过程PROC join(VAR la:link; lb:link) 实现连接线性表la和lb(lb在后)的算法,要求其时间复杂度为0(1), 占用辅助空间尽量小。描述所用结构。

【北京工业大学 1997 一、1 (8分)】

(2)设有两个链表,ha为单向链表,hb为单向循环链表。编写算法,将两个链表合并成一个单向链表,要求算法所需时间与链表长度无关。【南京航空航天大学 1997 四(8分)】 4. 顺序结构线性表LA与LB的结点关键字为整数。LA与LB的元素按非递减有序,线性表空间足够大。试用类PASCAL语言给出一种高效算法,将LB中元素合到LA中,使新的LA的元素仍保持非递减有序。高效指最大限度的避免移动元素。【北京工业大学 1997 一、2 (12分)】

5. 已知不带头结点的线性链表list,链表中结点构造为(data、link),其中data为数据域,link为指针域。请写一算法,将该链表按结点数据域的值的大小从小到大重新链接。要求链接过程中不得使用除该链表以外的任何链结点空间。【北京航空航天大学 1998 五(15分)】

6. 设L为单链表的头结点地址,其数据结点的数据都是正整数且无相同的,试设计利用直接插入的原则把该链表整理成数据递增的有序单链表的算法。【东北大学 1996 六 (14分)】 类似本题的另外叙述有:

(1)设一单向链表的头指针为head,链表的记录中包含着整数类型的key域,试设计算法,将此链表的记录按照key递增的次序进行就地排序.【中科院计算所 1999 五、1(10分)】 7. 设 Listhead为一单链表的头指针,单链表的每个结点由一个整数域DATA和指针域NEXT组成,整数在单链表中是无序的。编一PASCAL过程,将 Listhead链中结点分成一个奇数链和一个偶数链,分别由P,Q指向,每个链中的数据按由小到大排列。程序中不得使用 NEW过程申请空间。【山东大学1993六( 15分)】 类似本题的另外叙述有:

(1)设计算法将一个带头结点的单链表A分解为两个具有相同结构的链表B、C,其中B表的结点为A表中值小于零的结点,而C表的结点为A表中值大于零的结点(链表A的元素类型为整型,要求B、C表利用A表的结点)。【北京理工大学 2000 四、2(4分)】

(2) 设L为一单链表的头指针,单链表的每个结点由一个整数域 data和指针域NEXT组成,整数在单链表中是无序的。设计算法,将链表中结点分成一个奇数链和一个偶数链,分别由P,Q指向,每个链中的数据按由小到大排列,算法中不得申请新的结点空间。【青岛海洋大学 1999 三(12分)】

(3) 将一个带头结点的单链表A分解为两个带头结点的单链表A和B,使得A表中含有原表中序号为奇数的元素,而B表中含有原表中序号为偶数的元素,且保持其相对顺序不变。

1) 写出其类型定义: 2) 写出算法。【山东大学 1998 九 (9分)】 【山东工业大学 2000 九(9分)】 8. 已知线性表(a1 a2 a3 ?an)按顺序存于内存,每个元素都是整数,试设计用最少时间把所有值为负数的元素移到全部正数值元素前边的算法:例:(x,-x,-x,x,x,-x ?x)变为(-x,-x,-x?x,x,x)。

【东北大学 1998 二 (15分)】

类似本题的另外叙述有:

(1)设有一元素为整数的线性表L=(a1,a2,a3,?,an),存放在一维数组A[N]中,设计一个算法,以表中an作为参考元素,将该表分为左、右两部分,其中左半部分每个元素小于等于an,右半部分每个元素都大于an, an位于分界位置上(要求结果仍存放在A[N]中)。【北京理工大学 1999 八(6分)】

(2)顺序存储的线性表A,其数据元素为整型,试编写一算法,将A拆成B和C两个表,使A中元素值大于等于0的元素放入B,小于0的放入C中.. 要求:

1)表B和C另外设置存储空间;

2)表B和C不另外设置,而利用A的空间.【山东大学 2001 九、1 (12分)】

(3)知线性表(a1, a2,a3,?,an)按顺序存储,且每个元素都是整数均不相同,设计把所有奇数移到所有偶数前边的算法。(要求时间最少,辅助空间最少)【东北大学 1997 三 (15分)】

(4) 编写函数将一整数序列中所有负数移到所有正数之前,要求时间复杂度为O(n)

【南京航空航天大学 2001 八(10分)】

(5) 已知一个由n( 设n=1000)个整数组成的线性表,试设计该线性表的一种存储结构,并用标准pascal语言描述算法,实现将n个元素中所有大于等于19的整数放在所有小于19的整数之后。要求算法的时间复杂度为O(n),空间复杂度O(1)。【西安交通大学 1996 六(11分)】

9. 试编写在带头结点的单链表中删除(一个)最小值结点的(高效)算法。void delete(Linklist &L)

【北京理工大学 2001 九、3 (8分)】

10. 已知非空线性链表由list指出,链结点的构造为(data,link).请写一算法,将链表中数据域值最小的那个链结点移到链表的最前面。要求:不得额外申请新的链结点。【北京航空航天大学 2001 四(10分)】

11. 已知p指向双向循环链表中的一个结点,其结点结构为data、llink、rlink三个域,写出算法change(p),交换p所指向的结点和它的前缀结点的顺序。【首都经贸大学 1997 二、2(15分)】

12. 线性表(a1,a2,a3,?,an)中元素递增有序且按顺序存储于计算机内。要求设计一算法完成:

(1) 用最少时间在表中查找数值为x的元素。 (2) 若找到将其与后继元素位置相交换。

(3) 若找不到将其插入表中并使表中元素仍递增有序。【东北大学 1996 三 ( 12分)】 13. 设单链表的表头指针为h,结点结构由data和next两个域构成,其中data域为字符型。写出算法dc(h,n),判断该链表的前n个字符是否中心对称。例如 xyx, xyyx都是中心对称。【首都经贸大学1998三、9(15分)】

14. 已知两个单链表A和B,其头指针分别为heada和headb,编写一个过程从单链表A中删除自第i个元素起的共len个元素,然后将单链表A插入到单链表B的第j个元素之前。

【中国矿业大学 2000 三(10分)】

类似本题的另外叙述有:

(1)h1、h2为两个链表的表头指针,结点结构为data和link两个域组成。写出算法inde(h1,h2,i,j,l),将链表h1从第i个结点起的l个结点删除,并插入到h2表的第j个结点之前。

【首都经贸大学 1998 三、10(20分)】

15. 设线性表存于A[1..size]的前num各分量中,且递增有序。请设计一个算法,将x插

入到线性表的适当位置上,以保持线性表的有序性,并在设计前说明设计思想,最后说明所设计算法的时间复杂度。

【西安电子科技大学 1999计应用 1997 二 (10分)】

类似本题的另外叙述有:

(1) 试编制在线性表L={12,13,21,24,28,30,42,}中插入数据元素26的程序。(要求该程序用turbo Pascal语言编制并能在计算机上运行,结点类型为链式结构)【大连海事大学 1996 二、1 (16分)】

16. 假设一个单循环链表,其结点含有三个域pre、data、link。其中data为数据域;pre为指针域,它的值为空指针(NIL);link为指针域,它指向后继结点。请设计算法,将此表改成双向循环链表。

【西安电子科技大学 1999软件 五(10分)】

17. 已知递增有序的单链表A,B分别存储了一个集合,请设计算法以求出两个集合A和B 的差集A-B(即仅由在A中出现而不在B中出现的元素所构成的集合),并以同样的形式存储,同时返回该集合的元素个数。

【西安电子科技大学2000计应用1997 二 (10分)】

18. 已知一个单链表中每个结点存放一个整数,并且结点数不少于2,请设计算法以判断该链表中第二项起的每个元素值是否等于其序号的平方减去其前驱的值,若满足则返回ture,否则返回false.

【西安电子科技大学2000软件1997 二(10分)】

19.两个整数序列A=a1,a2,a3,?,am和B=b1,b2,b3,?,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的子序列。【东北大学 1999 二 (10分)】

20.L1与L2分别为两单链表头结点地址指针,且两表中数据结点的数据域均为一个字母。设计把L1中与L2中数据相同的连续结点顺序完全倒置的算法。【东北大学 1997 四 (15分)】

L1acabdad?例:

L2abd?

类似本题的另外叙述有:

(1) 知L为链表的头结点地址,表中共有m(m>3)个结点,从表中第i个结点(1

L ama1a1

【东北大学 1998 三 (15分)】

21. 请写一个算法将顺序存储结构的线性表(a1...an)逆置为(an...a1)。【大连海事大学1996八(6分)】

类似本题的另外叙述有:

(1) 设有一带头结点的单链表,编程将链表颠倒过来.要求不用另外的数组或结点完成.

【南京航空航天大学 1999 八 (10分)】

(2) 设有一个带头结点的单向链表,数据项递减有序。写一算法,重新排列链表,使数据项递增有序,要求算法时间复杂度为O(n)。(注:用程序实现)【南京航空航天大学 1997 七 (12分)】

(3) 试编写求倒排循环链表元素的算法。【南京航空航天大学 1995 十二 (10分)】

(4) 请设计算法将不带头结点的单链表就地逆置。【北方交通大学 2001 三 (12分)】 (5) 试编写算法 ,将不设表头结点的、不循环的单向链表就地逆转。【北方交通大学1997五(10分)】

(6) 有一个单链表L(至少有1个结点),其头结点指针为head,编写一个过程将L逆置,即最后一个结点变成第一个结点,原来倒数第二个结点变成第二个结点,如此等等。【燕山大学 2001 四、2(8分)】

22.设有一个由正整数组成的无序(向后)单链表,编写完成下列功能的算法:

(1)找出最小值结点,且打印该数值;

(2)若该数值是奇数,则将其与直接后继结点的数值交换; (3)若该数值是偶数,则将其直接后继结点删除。【东北大学 2000 二 (15分)】

23.已知L为没有头结点的的单链表中第一个结点的指针,每个结点数据域存放一个字符,该字符可能是英文字母字符或数字字符或其它字符,编写算法构造三个以带头结点的单循环链表表示的线性表,使每个表中只含同一类字符。(要求用最少的时间和最少的空间)【东北大学 2002 三(15分)】

24.在一个递增有序的线性表中,有数值相同的元素存在。若存储方式为单链表,设计算法去掉数值相同的元素,使表中不再有重复的元素。例如:(7,10,10,21,30,42,42,42,51,70)将变作(7,10,21,30,42,51,70),分析算法的时间复杂度。【北京工业大学 1996 三 (15分)】

25.在输入数据无序的情况下,建立一个数据值为整型的递增有序的顺序存储线性表L,且要求当输入相同数据值时,线性表中不能存在数据值相同的数据元素,试写出其算法。 顺序存储结构的线性表描述为:

CONST maxlen={线性表可能达到的最大长度}; TYPE sqlisttp=RECORD

elem:array[1..maxlen] of integer; last :0..maxlen

END;

VAR L: sqlisttp;【同济大学 1998 二 (12分 )】

26.设有一个正整数序列组成的有序单链表(按递增次序有序,且允许有相等的整数存在),试编写能实现下列功能的算法 :(要求用最少的时间和最小的空间)

(1)确定在序列中比正整数x大的数有几个(相同的数只计算一次,如序列{20,20,17,16,15,15,11,10,8,7,7,5,4}中比10大的数有5个);

(2) 在单链表将比正整数x小的数按递减次序排列; (3) 将正整数(比)x大的偶数从单链表中删除。【东北大学 2001 二 (17分)】

27. 编写一个算法来交换单链表中指针P所指结点与其后继结点,HEAD是该链表的头指针,P指向该链表中某一结点。【吉林大学 2001 二、1 (7分)】 类似本题的另外叙述有:

(1) 已知非空线性链表第一个结点由List指出,请写一算法,交换p所指的结点与其下一个结点在链表中的位置(设p指向的不是链表最后那个结点)。【北京航空航天大学 1999 五 (10分)】

(2) 已知任意单链表如图所示(编者略去图)。Head为表头指针,指向表的第一个元素,p为指向表中任意结点的指针,试设计一个算法,将p指向的结点和其后面结点交换位置(可采用任何高级语言描述算法)。

【山东大学 1992 二 ( 12分)】

28.设键盘输入n个英语单词,输入格式为n, w1, w2, ?,wn,其中n表示随后输入英语单词

个数,试编一程序,建立一个单向链表,实现:(10分)

(1)如果单词重复出现,则只在链表上保留一个。(单考生做)。

(2)除满足(1)的要求外。链表结点还应有一个计数域,记录该单词重复出现的次数,然后输出出现次数最多的前k(k<=n)个单词(统考生做)。【南京航空航天大学 1998 九 (10分)】

29.已知一双向循还链表,从第二个结点至表尾递增有序,(设a1

30. 已知长度为n的线性表A采用顺序存储结构,请写一时间复杂度为0(n)、空间复杂度为0(1)的算法,该算法删除线性表中所有值为item的数据元素。(O(1)表示算法的辅助空间为常量)。

【北京航空航天大学 2000 五(10分)】

31.设民航公司有一个自动预订飞机票的系统,该系统中有一张用双重链表示的乘客表,表中结点按乘客姓氏的字母序相链。例如,下面是张某个时刻的乘客表。试为该系统写出一个当任一乘客要订票时修改乘客表的算法。

序号 data Llink Rlink 1 Liu 6 5 2 Chan 4 9 3 Wang 5 7 4 Bao 0 2 5 Mai 1 3 6 Dong 8 1 7 Xi 3 0 8 Deng 9 6

9 Cuang 2 8

【北方交通大学 2000 六(17分)】

32.设有一头指针为L的带有表头结点的非循环双向链表,其每个结点中除有pred(前驱指针),data(数据)和next(后继指针)域外,还有一个访问频度域freq。在链表被起用前,其值均初始化为零。每当在链表中进行一次Locate(L,x)运算时,令元素值为x的结点中freq域的值增1,并使此链表中结点保持按访问频度非增(递减)的顺序排列,同时最近访问的结点排在频度相同的结点的最后,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的Locate(L,x)运算的算法,该运算为函数过程,返回找到结点的地址,类型为指针型。【清华大学 1997 二 (10分)】

33.给定(已生成)一个带表头结点的单链表,设head为头指针,结点的结构为(data,next),data为整型元素,next为指针,试写出算法:按递增次序输出单链表中各结点的数据元素,并释放结点所占的存储空间。(要求;不允许使用数组作辅助空间)【华中理工大学 2000 八、2 (13分)】

34.已知三个带头结点的线性链表A、B和C中的结点均依元素值自小至大非递减排列(可能存在两个以上值相同的结点),编写算法对A表进行如下操作:使操作后的链表A中仅留下三个表中均包含的数据元素的结点,且没有值相同的结点,并释放所有无用结点。限定算法的时间复杂度为O(m+n+p),其中m、n和p分别为三个表的长度。【清华大学 1995 一 (15分)】

第2章 线性表

一.选择题 1.A 11.4B 2.B 11.5C 3.C 4.A 5.D 6.D 7.D 12.B 13.C 25.B 14.C 26.A 15.C 27.D 5.× 8.C 9.B 10.B,C 16.A 17.A 18.A 11.1I 11.2I 11.3E 19.D 20.C 21.B 22.D 23.C 24.B 二.判断题 1. × 2.√ 13. × 14. √ 3. √ 15.× 4.× 16. √ 6. × 7. × 8.× 9.× 10.× 11.× 12.× 部分答案解释如下。 1、 头结点并不“仅起”标识作用,并且使操作统一。另外,头结点数据域可写入链表长度,

或作监视哨。

4.两种存储结构各有优缺点,应根据实际情况选用,不能笼统说哪一个好。 7.集合中元素无逻辑关系。

9.非空线性表第一个元素无前驱,最后一个元素无后继。 13.线性表是逻辑结构,可以顺序存储,也可链式存储。

三.填空题

1.顺序 2.(n-1)/2 3.py->next=px->next; px->next=py 4 .n-i+1

5.主要是使插入和删除等操作统一,在第一个元素之前插入元素和删除第一个结点不必另作判断。另外,不论链表是否为空,链表指针不变。 6.O(1),O(n) 7.单链表,多重链表,(动态)链表,静态链表 8.f->next=p->next; f->prior=p; p->next->prior=f; p->next=f; 9.p^.prior s^.prior^.next

10. 指针 11.物理上相邻 指针 12.4 2 13.从任一结点出发都可访问到链表中每一个元素。

14.u=p->next; p->next=u->next; free(u); 15.L->next->next==L 16.p->next!=null

17.L->next==L && L->prior==L 18.s->next=p->next;p->next=s; 19.(1) IF pa=NIL THEN return(true);

(2) pb<>NIL AND pa^.data>=pb^.data (3) return(inclusion(pa,pb)); (4) pb:=pb^.next; (5) return(false); 非递归算法:

(1)pre:=pb; (2) pa<>NIL AND pb<>NIL AND pb^.data>=pa^.data (3)pa:=pa^.next; pb:=pb->next;

(4)pb:=pre^.next;pre:=pb;pa:=pa^.next;(5)IF pa=NIL THEN return(true) ELSE

return(false);

[注]:本题是在链表上求模式匹配问题。非递归算法中用指针pre指向主串中开始结点(初始时为第一元素结点)。若主串与子串对应数据相等,两串工作指针pa和pb后移;否则,主串工作指针从pre的下一结点开始(这时pre又指向新的开始结点),子串工作指针从子串第一元素开始,比较一直继续到循环条件失败。若pa为空,则匹配成功,返回true,否则,返回false。

20.A.VAR head:ptr B. new(p) C. p^.data:=k D. q^.next:=p E. q:=p(带头结点) 21.(1)new(h);∥生成头结点,以便于操作。

(2)r^.next:=p; (3) r^.next:=q; (4) IF (q=NIL) THEN r^.next:=p; 22.A: r^.link^.data<>max AND q^.link^.data<>max

B: r:=r^.link C: q^.link D: q^.link E: r^.link F: r^.link G: r:=s(或r:= r^.link) H: r:=r^.link I: q^.link:=s^.link 23.(1)la (2)0 (3)j

(2)j:=1;

(3)p:=p^.right ∥工作指针后移 (4)s^.left:=p

(5)p^.right^.left:=s; ∥p后继的前驱是s (6)s^.left:=p;

25.(1)i<=L.last ∥L.last 为元素个数 (2)j:=j+1 ∥有值不相等的元素

(3)L.elem[j]:=L.elem[i] ∥元素前移 (4)L.last:=j ∥元素个数

26.(A)p^.link:=q;∥拉上链,前驱指向后继 (B)p:=q;∥新的前驱

(C)p^.link:=head;∥形成循环链表 (D)j:=0; ∥计数器,记被删结点 (E)q:=p^.link∥记下被删结点 (F)p^.link=q^.link ∥删除结点

27. (1)p:=r;∥r指向工作指针s的前驱,p指向最小值的前驱。 (2)q:=s;∥q指向最小值结点,s是工作指针

(3)s:=s^.link∥工作指针后移

(4)head:=head^.next;∥第一个结点值最小;

(5)p^link:=q^.link;∥跨过被删结点(即删除一结点) 28.(1) l^.key:=x;∥头结点l这时起监视哨作用 (2) l^.freq:=p^.freq ∥头结点起监视哨作用

(3) q->pre->next=q->next; q->next->pre=q->pre; ∥先将q结点从链表上摘下

q^.next:=p; q^.pre:=p^.pre; p^.pre->next:=q; p^.pre:=q; ∥结点q插入

结点p前

(4) q^.freq=0 ∥链表中无值为x的结点,将新建结点插入到链表最后(头结点

前)。

29.(1)a^.key:=’@’∥a的头结点用作监视哨,取不同于a链表中其它数据域的值 (2)b^.key:=p^.key∥b的头结点起监视哨作用

(3)p:=p^.next∥找到a,b表中共同字母,a表指针后移 (4)0(m*n)

30. C 部分:(1)p!=null ∥链表未到尾就一直作

(2)q ∥将当前结点作为头结点后的第一元素结点插入

31. (1)L=L->next; ∥暂存后继 (2)q=L; ∥待逆置结点 (3)L=p; ∥头指针仍为L

32. (1) p^.next<>p0 (2)r:= p^.next (3) p^.next:= q0; (4) q0:= p; (5) p:=r

33. (1)r (2)NIL (3)x=x; (7)r (8)p (9)r (10)NIL (11)NIL 34.(1)pa!=ha ∥或pa->exp!=-1

(2)pa->exp==0 ∥若指数为0,即本项为常数项 (3)q->next=pa->next ∥删常数项 (4)q->next ∥取下一元素 (5)=pa->coef*pa->exp

(6)-- ∥指数项减1

(7)pa ∥前驱后移,或q->next (8)pa->next ∥取下一元素 35.(1)q:=p; ∥q是工作指针p的前驱

(2)p^.data>m ∥p是工作指针

(3)r:=q; ∥r 记最大值的前驱, (4)q:=p; ∥或q:=q^.next;

(5)r^.next:=q^.next; ∥或r^.next:=r^.next^.next 删最大值结点 36.(1)L->next=null ∥置空链表,然后将原链表结点逐个插入到有序表中 (2)p!=null ∥当链表尚未到尾,p为工作指针

(3)q!=null ∥查p结点在链表中的插入位置,这时q是工作指针。 (4)p->next=r->next ∥将p结点链入链表中

(5)r->next=p ∥r是q的前驱,u是下个待插入结点的指针。 37.程序(a) PASCAL部分(编者略)

程序(b) C部分

(1)(A!=null && B!=null) ∥两均未空时循环

(2)A->element==B->element ∥两表中相等元素不作结果元素 (3)B=B->link ∥向后移动B表指针

(4)A!=null ∥将A 表剩余部分放入结果表中 (5)last->link=null ∥置链表尾

四、 应用题 1.(1)选链式存储结构。它可动态申请内存空间,不受表长度(即表中元素个数)的

影响,插入、删

除时间复杂度为O(1)。

(2)选顺序存储结构。顺序表可以随机存取,时间复杂度为O(1)。

2.链式存储结构一般说克服了顺序存储结构的三个弱点。首先,插入、删除不需移动

元素,只修改指针,时间复杂度为O(1);其次,不需要预先分配空间,可根据需要动态申请空间;其三,表容量只受可用内存空间的限制。其缺点是因为指针增加了空间开销,当空间不允许时,就不能克服顺序存储的缺点。

3.采用链式存储结构,它根据实际需要申请内存空间,而当不需要时又可将不用结点空间返还给系统。在链式存储结构中插入和删除操作不需要移动元素。

4.线性表 栈 队列 串 顺序存储结构和链式存储结构。 顺序存储结构的定义是:

CONST maxlen=线性表可能达到的最大长度; TYPE sqlisttp=RECORD

elem:ARRAY[1..maxlen] OF ElemType; last:0..maxlen; END; 链式存储结构的定义是: TYPE pointer=↑nodetype; nodetype=RECORD

data:ElemType; next:pointer; END;

linklisttp=pointer; 5.顺序映射时,ai与ai+1的物理位置相邻;链表表示时ai与ai+1的物理位置不要求相邻。 6.在线性表的链式存储结构中,头指针指链表的指针,若链表有头结点则是链表的头结点的指针,头指针具有标识作用,故常用头指针冠以链表的名字。头结点是为了操作的统一、方便而设立的,放在第一元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等),有头结点后,对在第一元素结点前插入结点和删除第一结点,其操作与对其它结点的操作统一了。而且无论链表是否为空,头指针均不为空。首元结点也就是第一元素结点,它是头结点后边的第一个结点。

7.见上题6。

8.(1)将next域变为两个域: pre和next,其值域均为0..maxsize。初始化时,头结点(下标为0的元素)其next域值为1,其pre域值为n(设n是元素个数,且n

9. 在单链表中不能从当前结点(若当前结点不是第一结点)出发访问到任何一个结点,链表只能从头指针开始,访问到链表中每个结点。在双链表中求前驱和后继都容易,从当前结点向前到第一结点,向后到最后结点,可以访问到任何一个结点。

10.本题是链表的逆置问题。设该链表带头结点,将头结点摘下,并将其指针域置空。然后从第一元素结点开始,直到最后一个结点为止,依次前插入头结点的后面,则实现了链表的逆置。 11.该算法的功能是判断链表L是否是非递减有序,若是则返回“true”;否则返回“false“。pre指向当前结点,p指向pre的后继。

12.q=p->next; p->next=q->next; free(q);

13. 设单链表的头结点的头指针为head,且pre=head; while(pre->next!=p) pre=pre->next; s->next=p; pre->next=s;

14.设单链表带头结点,工作指针p初始化为p=H->next;

(1) while(p!=null && p->data!=X) p=p->next;

if(p= =null) return(null);∥查找失败 else return(p);∥查找成功

(2) while(p!=null && p->datanext;

if(p==null || p->data>X) return(null);∥查找失败 else return(p);

(3) while(p!=null && p->data>X) p=p->next;

if(p==null || p->data

15.本程序段功能是将pa和pb链表中的值相同的结点保留在pa链表中(pa中与pb中不同结点删除),pa是结果链表的头指针。链表中结点值与从前逆序。S1记结果链表中结点个数(即pa与pb中相等的元素个数)。S2记原pa链表中删除的结点个数。 16.设 q:=p^.llink; 则

q^.rlink:=p^.rlink; p^.rlink^.llink:=q; p^.llink:=q^.llink;

q^.llink^.rlink:=p; p^.rlink:=q; q^.llink:=p 17.(1)前两个语句改为:

p.llink^.rlink<- p^.rlink; p^.rlink^.llink<- p^.llink;

(2)后三个语句序列应改为:

q^.rlink<- p^.rlink;∥以下三句的顺序不能变

p^.rlink^.llink<- q; p^.rlink<- q;

18.mp是一个过程,其内嵌套有过程subp。

subp(s,q)的作用是构造从s到q的循环链表。

subp(pa,pb)调用结果是将pa到pb的前驱构造为循环链表。

subp(pb,pa)调用结果是将pb到pa的前驱(指在L链表中,并非刚构造的pa循环

链表中)构造为循环链表。

总之,两次调用将L循环链表分解为两个。第一个循环链表包含从pa到pb的前驱,L中除刚构造的pa到pb前驱外的结点形成第二个循环链表。 19.在指针p所指结点前插入结点s的语句如下:

s->pre=p->pre; s->next=p; p->pre->next=s; p->pre=s;

20.(A) f1<>NIL并且f2<>NIL

(B) f1↑.data < f2↑.data (C) f2↑.data

(E) f1<- f1↑.link 或f2=f2↑.link;

21. 1)本算法功能是将双向循环链表结点的数据域按值自小到大排序,成为非递减(可能包括数据域值相等的结点)有序双向循环链表。

2)(1)r->prior=q->prior;∥将q结点摘下,以便插入到适当位置。

(2)p->next->prior=q;∥(2)(3)将q结点插入 (3)p->next=q;

(4)r=r->next;或r=q->next;∥后移指针,再将新结点插入到适当位置。

五、 算法设计题

1.[题目分析]因为两链表已按元素值递增次序排列,将其合并时,均从第一个结点起进行比较,将小的链入链表中,同时后移链表工作指针。该问题要求结果链表按元素值递减次序排列。故在合并的同时,将链表结点逆置。 LinkedList Union(LinkedList la,lb)

∥la,lb分别是带头结点的两个单链表的头指针,链表中的元素值按递增序排列,本算法

将两链表合并成一个按元素值递减次序排列的单链表。

{ pa=la->next; pb=lb->next;∥pa,pb分别是链表la和lb的工作指针

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

while(pa!=null && pb!=null) ∥当两链表均不为空时作 if(pa->data<=pb->data)

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

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

la->next=pa;

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

else

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

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

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

while(pa!=null) ∥将la表的剩余部分链入结果表,并逆置。 {r=pa->next; pa->next=la->next; la->next=pa; pa=r; } while(pb!=null)

{r=pb->next; pb->next=la->next; la->next=pb; pb=r; } }∥算法Union结束。

[算法讨论]上面两链表均不为空的表达式也可简写为while(pa&&pb),两递增有序表合并成递减有序表时,上述算法是边合并边逆置。也可先合并完,再作链表逆置。后者不如前者优化。算法中最后两个while语句,不可能执行两个,只能二者取一,即哪个表尚未到尾,就将其逆置到结果表中,即将剩余结点依次前插入到结果表的头结点后面。 与本题类似的其它题解答如下: (1)[问题分析]与上题类似,不同之处在于:一是链表无头结点,为处理方便,给加上头结点,处理结束再删除之;二是数据相同的结点,不合并到结果链表中;三是hb链表不能被破坏,即将hb的结点合并到结果链表时,要生成新结点。 LinkedList Union(LinkedList ha, hb)

∥ha和hb是两个无头结点的数据域值递增有序的单链表,本算法将hb中并不出现在ha

中的数据合并到ha中,合并中不能破坏hb链表。 {LinkedList la;

la=(LinkedList)malloc(sizeof(LNode)); la->next=ha;∥申请头结点,以便操作。 pa=ha; ∥pa是ha链表的工作指针

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

pre=la; ∥pre指向当前待合并结点的前驱。

while(pa&&pb)

if(pa->datadata)∥处理ha中数据 {pre->next=pa;pre=pa;pa=pa->next;}

else if(pa->data>pb->data)∥处理hb中数据。

{r=(LinkedList)malloc(sizeof(LNode));∥申请空间 r->data=pb->data; pre->next=r;

pre=r;∥将新结点链入结果链表。

pb=pb->next;∥hb链表中工作指针后移。 }

else∥处理pa->data=pb->data; {pre->next=pa; pre=pa;

pa=pa->next;∥两结点数据相等时,只将ha的数据链入。 pb=pb->next;∥不要hb的相等数据 }

if(pa!=null)pre->next=pa;∥将两链表中剩余部分链入结果链表。 else pre->next=pb;

free(la);∥释放头结点.ha,hb指针未被破坏。 }∥算法nion结束。

(2)本题与上面两题类似,要求结果指针为lc,其核心语句段如下: pa=la->next;pb=hb->next;

lc=(LinkedList )malloc(sizeof(LNode)); pc=lc;∥pc是结果链表中当前结点的前驱 while(pa&&pb)

if(pa->datadata)

{pc->next=pa;pc=pa;pa=pa->next;} else {pc->next=pb;pc=pb;pb=pb->next;} if(pa)pc->next=pa; else pc->next=pb;

free(la);free(lb);∥释放原来两链表的头结点。 算法时间复杂度为O(m+n),其中m和n分别为链表la和lb的长度。

2.[题目分析]本组题有6个,本质上都是链表的合并操作,合并中有各种条件。与前组题不同的是,叙述上是用线性表代表集合,而操作则是求集合的并、交、差(A∪B,A∩B,A-B)等。

本题与上面1.(2)基本相同,不同之处1.(2)中链表是“非递减有序”,(可能包含相等元素),本题是元素“递增有序”(不准有相同元素)。因此两表中合并时,如有元素值相等元素,则应删掉一个。

LinkedList Union(LinkedList ha,hb)

∥线性表A和B代表两个集合,以链式存储结构存储,元素递增有序。ha和hb分别

是其链表的头指针。本算法求A和B的并集A∪B,仍用线性表表示,结果链表元素也是递增有序。

{ pa=ha->next;pb=hb->next;∥设工作指针pa和pb。 pc=ha;∥pc为结果链表当前结点的前驱指针。 while(pa&&pb)

if(pa->datadata)

{pc->next=pa;pc=pa;pa=pa->next;}

else if(pa->data>pb->data)

{pc->next=pb;pc=pb;pb=pb->next;} else∥处理pa->data=pb->data.

{pc->next=pa;pc=pa;pa=pa->next; u=pb;pb=pb->next;free(u);}

if(pa) pc->next=pa;∥ 若ha表未空,则链入结果表。 else pc->next=pb;∥若hb表未空,则链入结果表。 free(hb); ∥释放hb头结点 return(ha);

}∥算法Union结束。

与本题类似的其它几个题解答如下: (1) 解答完全同上2。

(2) 本题是求交集,即只有同时出现在两集合中的元素才出现在结果表中。其核心语句段如下:

pa=la->next;pb=lb->next;∥设工作指针pa和pb; pc=la;∥结果表中当前合并结点的前驱的指针。 while(pa&&pb)

if(pa->data==pb->data)∥交集并入结果表中。 { pc->next=pa;pc=pa;pa=pa->next;

u=pb;pb=pb->next;free(u);}

else if(pa->datadata) {u=pa;pa=pa->next;free(u);}

else {u=pb; pb=pb->next; free(u);}

while(pa){ u=pa; pa=pa->next; free(u);}∥ 释放结点空间 while(pb) {u=pb; pb=pb->next; free(u);}∥释放结点空间 pc->next=null;∥置链表尾标记。

free(lb); ∥注: 本算法中也可对B表不作释放空间的处理

(3)本题基本与(2)相同,但要求无重复元素,故在算法中,待合并结点数据要与其前驱比较,只有在与前驱数据不同时才并入链表。其核心语句段如下。 pa=L1->next;pb=L2->next;∥pa、pb是两链表的工作指针。 pc=L1;∥L1作结果链表的头指针。 while(pa&&pb)

if(pa->datadata) {u=pa;pa=pa->next;free(u);}∥删除L1表多余元素 else if (pa->data>pb->data) pb=pb->next; ∥pb指针后移 else ∥处理交集元素

{if(pc==L1) {pc->next=pa;pc=pa;pa=pa->next;} ∥处理第一个相等的元

素。

else if(pc->data==pa->data){ u=pa;pa=pa->next;free(u);} ∥重复元

素不进入L1表。

else{ pc->next=pa;pc=pa;pa=pa->next;} ∥交集元素并入结果表。

} ∥while

while(pa) {u=pa;pa=pa->next;free(u);} ∥ 删L1表剩余元素

pc->next=null; ∥置结果链表尾。

注: 本算法中对L2表未作释放空间的处理。

(4) 本题与上面(3)算法相同,只是结果表要另辟空间。

(5) [题目分析]本题首先求B和C的交集,即求B和C中共有元素,再与A求并集,同时删除重复元素,以保持结果A递增。

LinkedList union(LinkedList A,B,C)

∥A,B和C均是带头结点的递增有序的单链表,本算法实现A= A∪(B∩C),使求解结构保持递增有序。

{pa=A->next;pb=B->next;pc=C->next;∥设置三个工作指针。 pre=A; ∥pre指向结果链表中当前待合并结点的前驱。

if(pa->datadata||pa->datadata)∥A中第一个元素为结果表的第一元素。

{pre->next=pa;pre=pa;pa=pa->next;}

else{while(pb&&pc) ∥找B表和C表中第一个公共元素。 if(pb->datadata)pb=pb->next;

else if(pb->data>pc->data)pc=pc->next;

else break; ∥找到B表和C表的共同元素就退出while循环。 if(pb&&pc)∥ 因共同元素而非B表或C表空而退出上面while循环。 if(pa->data>pb->data)∥A表当前元素值大于B表和C表的公共元素,先将B

表元素链入。

{pre->next=pb;pre=pb;pb=pb->next;pc=pc->next;}∥ B,C公共元素为结果

表第一元素。

}∥结束了结果表中第一元素的确定 while(pa&&pb&&pc) {while(pb&&pc)

if(pb->datadata) pb=pb->next;

else if(pb->data>pc->data) pc=pc->next; else break; ∥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; ∥当B,C无公共元素(即一个表已空),将A中剩余链入。 }∥算法Union结束

[算法讨论]本算法先找结果链表的第一个元素,这是因为题目要求结果表要递增有序(即删除重复元素)。这就要求当前待合并到结果表的元素要与其前驱比较。由于初始pre=A(头结点的头指针),这时的data域无意义,不能与后继比较元素大小,因此就需要确定第一个元素。当然,不要这样作,而直接进入下面循环也可以,但在链入结点时,必须先判断pre是否等于A,这占用了过多的时间。因此先将第一结点链入是可取的。 算法中的第二个问题是要求时间复杂度为O(|A|+|B|+|C|)。这就要求各个表的工作指针只能后移(即不能每次都从头指针开始查找)。本算法满足这一要求。 最后一个问题是,当B,C有一表为空(即B和C已无公共元素时),要将A的剩余部分

链入结果表。

3.[题目分析]循环单链表L1和L2数据结点个数分别为m和n ,将二者合成一个循环单链表时,需要将一个循环链表的结点(从第一元素结点到最后一个结点)插入到另一循环链表的第一元素结点前即可。题目要求“用最快速度将两表合并“,因此应找结点个数少的链表查其尾结点。

LinkedList Union(LinkedList L1,L2;int m,n)

∥L1和L2分别是两循环单链表的头结点的指针,m和n分别是L1和L2的长度。 ∥本算法用最快速度将L1和L2合并成一个循环单链表。 {if(m<0||n<0) {printf(“表长输入错误\\n”);exit(0);}

if(m

while(p->next!=L1) p=p->next;∥查最后一个元素结点。

p->next=L2->next;∥将L1循环单链表的元素结点插入到L2的第一元素结点前。

L2->next=L1->next;

free(L1);∥释放无用头结点。 }

}∥处理完m

else∥ 下面处理L2长度小于等于L1的情况 {if(n==0)return(L1);∥L2为空表。 else{p=L2;

while(p->next!=L2) p=p->next;∥查最后元素结点。

p->next=L1->next;∥将L2的元素结点插入到L1循环单链表的第一元素结点前。

L1->next=L2->next;

free(L2);∥释放无用头结点。

}

}∥算法结束。

类似本题叙述的其它题解答如下:

(1)[题目分析]本题将线性表la和lb连接,要求时间复杂度为O(1),且占用辅助空间尽量小。应该使用只设尾指针的单循环链表。

LinkedList Union(LinkedList la,lb)

∥la和lb是两个无头结点的循环单链表的尾指针,本算法将lb接在la后,成为一

个单循环链表。

{ q=la->next; ∥q指向la的第一个元素结点。

la->next=lb->next; ∥将lb的最后元素结点接到lb的第一元素。

lb->next=q; ∥将lb指向la的第一元素结点,实现了lb接在la后。 return(lb); ∥返回结果单循环链表的尾指针lb。 }∥算法结束。

[算法讨论]若循环单链表带有头结点,则相应算法片段如下: q=lb->next; ∥q指向lb的头结点;

lb->next=la->next; ∥lb的后继结点为la的头结点。

la->next=q->next; ∥la的后继结点为lb的第一元素结点。

free(q); ∥释放lb的头结点

return(lb); ∥返回结果单循环链表的尾指针lb。 (2)[题目分析]本题要求将单向链表ha和单向循环链表hb合并成一个单向链表,要求算法所需时间与链表长度无关,只有使用带尾指针的循环单链表,这样最容易找到链表的首、尾结点,将该结点序列插入到单向链表第一元素之前即可。 其核心算法片段如下(设两链表均有头结点)

q=hb->next; ∥单向循环链表的表头指针

hb->next=ha->next; ∥将循环单链表最后元素结点接在ha第一元素前。

ha->next=q->next; ∥将指向原单链表第一元素的指针指向循环单链表第一结

free(q); ∥释放循环链表头结点。 若两链表均不带头结点,则算法片段如下:

q=hb->next; ∥q指向hb首元结点。

hb->next=ha; ∥hb尾结点的后继是ha第一元素结点。 ha=q; ∥头指针指向hb的首元结点。

4.[题目分析]顺序存储结构的线性表的插入,其时间复杂度为O(n),平均移动近一半的元素。线性表LA和LB合并时,若从第一个元素开始,一定会造成元素后移,这不符合本题“高效算法”的要求。另外,题中叙述“线性表空间足够大”也暗示出另外合并方式,即应从线性表的最后一个元素开始比较,大者放到最终位置上。设两线性表的长度各为m和n ,则结果表的最后一个元素应在m+n位置上。这样从后向前,直到第一个元素为止。

PROC Union(VAR LA:SeqList;LB:SeqList)

∥LA和LB是顺序存储的非递减有序线性表,本算法将LB合并到LA中,元素仍非递减有序。

m:=LA.last;n:=LB.last;∥m,n分别为线性表LA和LB的长度。 k:=m+n; ∥k为结果线性表的工作指针(下标)。

i:=m;j:=n; ∥i,j分别为线性表LA和LB的工作指针(下标)。 WHILE(i>0)AND(j>0)DO

IF LA.elem[i]>=LB.elem[j]

THEN[LA.elem[k]:=LA.elem[i];k:=k-1;i:=i-1;] ELSE[LA.elem[k]:=LB.elem[j];k:=k-1;j:=j-1;]

WHILE(j>0) DO [LA.elem[k]:=LB.elem[j];k:=k-1;j:=j-1;] LA.last:=m+n;

ENDP;

[算法讨论]算法中数据移动是主要操作。在最佳情况下(LB的最小元素大于LA的最大元素),仅将LB的n个元素移(拷贝)到LA中,时间复杂度为O(n),最差情况,LA的所有元素都要移动,时间复杂度为O(m+n)。因数据合并到LA中,所以在退出第一个WHILE循环后,只需要一个WHILE循环,处理LB中剩余元素。第二个循环只有在LB有剩余元素时才执行,而在LA有剩余元素时不执行。本算法利用了题目中“线性表空间足够大”的条件,“最大限度的避免移动元素”,是“一种高效算法”。

5.[题目分析]本题实质上是一个排序问题,要求“不得使用除该链表结点以外的任何链结点空间”。链表上的排序采用直接插入排序比较方便,即首先假定第一个结点有序,然后,从第二个结点开始,依次插入到前面有序链表中,最终达到整个链表有序。 LinkedList LinkListSort(LinkedList list)

∥list是不带头结点的线性链表,链表结点构造为data和link两个域,data是数据

域,link是指针域。本算法将该链表按结点数据域的值的大小,从小到大重新链接。 {p=list->link; ∥p是工作指针,指向待排序的当前元素。

list->link=null;∥假定第一个元素有序,即链表中现只有一个结点。 while(p!=null)

{r=p->link; ∥r是p的后继。 q=list;

if(q->data>p->data)∥处理待排序结点p比第一个元素结点小的情况。 {p->link=list;

list=p;∥链表指针指向最小元素。 }

else∥查找元素值最小的结点。

{while(q->link!=null&&q->link->datadata)q=q->link; p->link=q->link;∥将当前排序结点链入有序链表中。 q->link=p; }

p=r;∥p指向下个待排序结点。 } }

[算法讨论]算法时间复杂度的分析与用顺序存储结构时的情况相同。但顺序存储结构将第i(i>1)个元素插入到前面第1至第i-1个元素的有序表时,是将第i个元素先与第i-1个元素比较。而在链表最佳情况均是和第一元素比较。两种存储结构下最佳和最差情况的比较次数相同,在链表情况下,不移动元素,而是修改结点指针。

另一说明是,本题中线性链表list不带头结点,而且要求“不得使用除该链表以外的任何链结点空间“,所以处理复杂,需要考虑当前结点元素值比有序链表第一结点的元素值还小的情况,这时要修改链表指针list。如果list是头结点的指针,则相应处理要简单些,其算法片段如下:

p=list->link;∥p指向第一元素结点。 list->link=null;∥有序链表初始化为空 while(p!=null)

{r=p->link;∥保存后继 q=list;

while(q->link!=null && q->link->datadata)q=q->link; p->link=q->link; q->link=p;

q=r; }

6.[题目分析]本题明确指出单链表带头结点,其结点数据是正整数且不相同,要求利用直接插入原则把链表整理成递增有序链表。这就要求从第二结点开释,将各结点依次插入到有序链表中。

LinkedList LinkListInsertSort(LinkedList la)

∥la是带头结点的单链表,其数据域是正整数。本算法利用直接插入原则将链表整理成递增的有序链表。

{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; }

与本题有类似叙述的题的解答:

(1)本题也是链表排序问题,虽没象上题那样明确要求“利用直接插入的原则”来排序,仍可用上述算法求解,这里不再赘述。

7.[题目分析]本题要求将一个链表分解成两个链表,两个链表都要有序,两链表建立过程中不得使用NEW过程申请空间,这就是要利用原链表空间,随着原链表的分解,新建链表随之排序。

PROC discreat(VAR listhead,P,Q:linkedlist)

∥listhead是单链表的头指针,链表中每个结点由一个整数域DATA和指针域NEXT组成。本算法将链表listhead分解成奇数链表和偶数链表,分解由P和Q指向,且P和Q链表是有序的。

P:=NIL;Q:=NIL;∥P和Q链表初始化为空表。 s:=listhead; WHILE(s<>NIL)DO

[r:=s^.NEXT; ∥暂存s的后继。 IF s^.DATA DIV 2=0 ∥处理偶数。

THEN IF P=NIL THEN[P:=s;P^.NEXT:=NIL;] ∥第一个偶数链结点。

ELSE[pre:=P;

IF pre^.DATA>s^.DATA THEN[s^.NEXT:=pre;P:=s;∥插入当前最小值结

点修改头指针]

ELSE[WHILE pre^.NEXT<>NIL DO

IF pre^.NEXT^.DATA

s^.NEXT:=pre^.NEXT; ∥链入此结点。 pre^.NEXT:=s; ]

]

ELSE∥处理奇数链。

IF Q=NIL THEN[Q:=s;Q^.NEXT:=NIL;] ∥第一奇数链结点。 ELSE[pre:=Q;

IF pre^.DATA>s^.DATA THEN[s^.NEXT:=pre; Q:=s; ]∥修改头指针。

ELSE[WHILE pre^.NEXT<>NIL DO ∥查找插入位置。

IF pre^.NEXT^.DATA

]∥结束奇数链结点

s:=r;∥s指向新的待排序结点。 ]∥结束“WHILE(s<>NIL)DO” ENDP;∥结束整个算法。 [算法讨论]由于算法要求“不得使用NEW过程申请空间,也没明确指出链表具有头结点,所以上述算法复杂些,它可能需要在第一个结点前插入新结点,即链表的头指针会发生变化。如有头结点,算法不必单独处理在第一个结点前插入结点情况,算法会规范统一,下面的(1)是处理带头结点的例子。算法中偶数链上结点是靠数据整除2等于0(DATA DIV 2=0)判断的。

类似本题的其它题解答如下:

(1)[题目分析]本题基本类似于上面第7题,不同之处有二。一是带头结点,二是分解后的两个链表,一个是数据值小于0,另一个是数据值大于0。由于没明确要求用类PASCAL书写算法,故用C书写如下。

void DisCreat1(LinkedList A)

∥A是带头结点的单链表,链表中结点的数据类型为整型。本算法将A分解成两个单链表B和C,B中结点的数据小于零,C中结点的数据大于零。 {B=A;

C=(LinkedList )malloc(sizeof(LNode));∥为C申请结点空间。 C->next=null ∥C初始化为空表。 p=A->next; ∥p为工作指针。 B->next=null; ∥B表初始化。 while(p!=null)

{r=p->next; ∥暂存p的后继。

if (p->data<0)∥小于0的放入B表。

{p->next=B->next; B->next=p; }∥将小于0的结点链入B表。 else {p->next=C->next; C->next=p; } p=r;∥p指向新的待处理结点。 }

}∥算法结束。

[算法讨论]因为本题并未要求链表中结点的数据值有序,所以算法中采取最简单方式:将新结点前插到头结点后面(即第一元素之前)。 (2) 本题同上面第7题,除个别叙述不同外,本质上完全相同,故不再另作解答。

(3)[题目分析]本题中的链表有头结点,分解成表A和表B,均带头结点。分解后的A表含有原表中序号为奇数的元素,B表含有原A表中序号为偶数的元素。由于要求分解后两表中元素结点的相对顺序不变,故采用在链表尾插入比较方便,这使用一指向表尾的指针即可方便实现。

void DisCreat3(LinkedList A)

∥A是带头结点的单链表,本算法将其分解成两个带头结点的单链表,A表中含原表中序号为奇数

∥的结点,B表中含原表中序号为偶数的结点。链表中结点的相对顺序同原链表。 {i=0;∥i记链表中结点的序号。

B=(LinkedList)malloc(sizeof(LNode);∥创建B表表头。 B->next=null; ∥B表的初始化。

LinkedList ra,rb;∥ra和rb将分别指向将创建的A表和B表的尾结点。

ra=A;rb=B;

p=A->next; ∥p为链表工作指针,指向待分解的结点。 A->next=null; ∥置空新的A表 while(p!=null)

{r=p->next; ∥暂存p的后继。 i++;

if(i%2==0) ∥处理原序号为偶数的链表结点。

{p->next=rb->next;∥在B表尾插入新结点; rb->next=p; rb=p;∥rb指向新的尾结点; }

else∥处理原序号为奇数的结点。

{p->next=ra->next; ra->next=p; ra=p; }

p=r; ∥将p恢复为指向新的待处理结点。 }∥算法结束

8.[题目分析]题目要求重排n个元素且以顺序存储结构存储的线性表,使得所有值为负数的元素移到正数元素的前面。这可采用快速排序的思想来实现,只是提出暂存的第一个元素(枢轴)并不作为以后的比较标准,比较的标准是元素是否为负数。 int Rearrange(SeqList a; int n)

∥a是具有n个元素的线性表,以顺序存储结构存储,线性表的元素是整数。本算法重

排线性表a,

∥使所有值为负数的元素移到所有值为正数的数的前面。 {i=0; j=n-1; ∥ i,j为工作指针(下标),初始指向线性表a的第1个和第n个元

素。

t=a[0]; ∥暂存枢轴元素。 while(i

{while(i=0) j--; ∥ 若当前元素为大于等于零,则指针前移。 if(i

while(i

a[i]=t; ∥将原第一元素放到最终位置。 }

[算法讨论] 本算法时间复杂度为O(n)。算法只是按题目要求把正负数分开,如要求统计负数和大于等于零的个数,则最后以t来定。如t为负数,则0至i共i+1个负数,n-1-i个正数(包括零)。另外,题目并未提及零的问题,笔者将零放到正数一边。对此问题的扩充是若元素包含正数、负数和零,并要求按负数、零、正数的顺序重排线性表,统计负数、零、正数的个数。请读者利用上面解题思想自行解答。 类似本题的选了5 个题,其解答如下:

(1)与上面第8题不同的是,这里要求以an为参考元素,将线性表分成左右两部分。左半部分的元素都小于等于an,右半部分的元素都大于an,an位于分界位置上。其算法主要片段语句如下:

i=1;j=n;

t=a[n]; ∥暂存参考元素。 while(i

{while(i

while(it) j--; ∥当前元素大于参考元素时指针前移。 if(i

a[i]=t; ∥参考元素置于分界位置。

(2) [题目分析]本题要求将线性表A分成B和C两个表,表B和表C不另占空间,而是利用表A的空间,其算法与第8题相同。这里仅把表B和表C另设空间的算法解答如下: void Rearrange2(int A[],B[],C[])

∥线性表A有n个整型元素,顺序存储。本算法将A拆成B和C 两个表,B中存放

大于

∥等于零的元素,C中存放小于零的元素。

{i=0; ∥i,j,k是工作指针,分别指向A、B和C表的当前元素。 j=k=-1; ∥j,k初始化为-1。 while(i

{if(A[i]<0) C[++k]=A[i++]; ∥将小于零的元素放入C表。 else B[++j]=A[i++]; ∥将大于零的元素放入B表。

[算法讨论]本题用一维数组存储线性表,结果线性表B和C中分别有j+1和k+1个元素。若采用教材中的线性表,则元素的表示作相应改变,例如A.elem[i],而最后B和C表应置上表的长度,如B.length=j和C.length=k。

(3) 本题与第8题本质上相同,第8题要求分开正数和负数,这里要求分开奇数和偶数,判别方式是a[i]%2==0,满足时为偶数,反之为奇数。 (4) 本题与第8题相同,只是叙述不同。

(5) 本题与第8题基本相同,不同之处在于这里的分界元素是整数19(链表中并不要

求一定有19)。本题要求用标准pascal描述算法,如下所示。 TYPE arr=ARRAY[1..1000] OF integer; VAR a:arr;

PROCEDURE Rearrange5(VAR a:arr);

∥a是n(设n=1000)个整数组成的线性表,用一维数组存储。本算法将n个元素

中所有大于等于19的整数放在所有小于19的整数之后。

VAR i,j,t:integer; BEGIN

i:=1;j:=n;t:=a[1] ;∥i,j指示顺序表的首尾元素的下标,t暂存分界元素 WHILE(i

WHILE (i=19) DO j:=j-1;

IF(i

IF(i

[算法讨论] 分界元素t放入a[i],而不论它的值如何。算法中只用了一个t中间变量,符合空间复杂度O(1)的要求。算法也满足时间复杂度O(n)的要求。

9.[题目分析] 本题要求在单链表中删除最小值结点。单链表中删除结点,为使结点删除后不出现“断链”,应知道被删结点的前驱。而“最小值结点”是在遍历整个链表后才能知道。所以算法应首先遍历链表,求得最小值结点及其前驱。遍历结束后再执行删除操作。

LinkedList Delete(LinkedList L)

∥L是带头结点的单链表,本算法删除其最小值结点。

{p=L->next; ∥p为工作指针。指向待处理的结点。假定链表非空。 pre=L; ∥pre指向最小值结点的前驱。

q=p; ∥q指向最小值结点,初始假定第一元素结点是最小值结点。 while(p->next!=null)

{if(p->next->datadata){pre=p;q=p->next;} ∥查最小值结点 p=p->next; ∥指针后移。 }

pre->next=q->next;∥从链表上删除最小值结点 free(q); ∥释放最小值结点空间 }∥结束算法delete。

[算法讨论] 算法中函数头是按本教材类C描述语言书写的。原题中void delete(linklist &L),是按C++的“引用”来写的,目的是实现变量的“传址”,克服了C语言函数传递只是“值传递”的缺点。

10.[题目分析] 本题要求将链表中数据域值最小的结点移到链表的最前面。首先要查找最小值结点。将其移到链表最前面,实质上是将该结点从链表上摘下(不是删除并回收空间),再插入到链表的最前面。

LinkedList delinsert(LinkedList list)

∥list是非空线性链表,链结点结构是(data,link),data是数据域,link是链域。

∥本算法将链表中数据域值最小的那个结点移到链表的最前面。 {p=list->link;∥p是链表的工作指针

pre=list; ∥pre指向链表中数据域最小值结点的前驱。 q=p; ∥q指向数据域最小值结点,初始假定是第一结点 while (p->link!=null)

{if(p->link->datadata){pre=p;q=p->link;} ∥找到新的最小值结点; p=p->link; }

if (q!=list->link) ∥若最小值是第一元素结点,则不需再操作 {pre->link=q->link; ∥将最小值结点从链表上摘下; q->link= list->link;∥将q结点插到链表最前面。 list->link=q;

}

}∥算法结束

[算法讨论] 算法中假定list带有头结点,否则,插入操作变为q->link=list;list=q。 11.[题目分析] 知道双向循环链表中的一个结点,与前驱交换涉及到四个结点(p结点,前驱结点,前驱的前驱结点,后继结点)六条链。

void Exchange(LinkedList p)

∥p是双向循环链表中的一个结点,本算法将p所指结点与其前驱结点交换。 {q=p->llink;

q->llink->rlink=p; ∥p的前驱的前驱之后继为p p->llink=q->llink; ∥p的前驱指向其前驱的前驱。 q->rlink=p->rlink; ∥p的前驱的后继为p的后继。 q->llink=p; ∥p与其前驱交换

p->rlink->llink=q; ∥p的后继的前驱指向原p的前驱 p->rlink=q; ∥p的后继指向其原来的前驱 }∥算法exchange结束。

12.[题目分析] 顺序存储的线性表递增有序,可以顺序查找,也可折半查找。题目要求“用最少的时间在表中查找数值为x的元素”,这里应使用折半查找方法。

void SearchExchangeInsert(ElemType a[];ElemType x) ∥a是具有n个元素的递增有序线性表,顺序存储。本算法在表中查找数值为x的元素,如查到则与其后继交换位置;如查不到,则插入表中,且使表仍递增有序。

{ low=0;high=n-1; ∥low和high指向线性表下界和上界的下标 while(low<=high)

{mid=(low+high)/2; ∥找中间位置

if(a[mid]==x) break; ∥找到x,退出while循环。 else if (a[mid]

if(a[mid]==x && mid!=n)∥ 若最后一个元素与x相等,则不存在与其后继交换的

操作。

{t=a[mid];a[mid]=a[mid+1];a[mid+1]=t;} ∥ 数值x与其后继元素位置交换。 if(low>high) ∥查找失败,插入数据元素x

{for(i=n-1;i>high;i--) a[i+1]=a[i];∥后移元素。 a[i+1]=x;∥插入x。 } ∥结束插入 }∥结束本算法。

[算法讨论] 首先是线性表的描述。算法中使用一维数组a表示线性表,未使用包含数据元素的一维数组和指示线性表长度的结构体。若使用结构体,对元素的引用应使用a.elem[i]。另外元素类型就假定是ElemType,未指明具体类型。其次,C中一维数组下标从0开始,若说有n个元素的一维数组,其最后一个元素的下标应是n-1。第三,本算法可以写成三个函数,查找函数,交换后继函数与插入函数。写成三个函数显得逻辑清晰,易读。 13.[题目分析] 判断链表中数据是否中心对称,通常使用栈。将链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两元素比较,若相等,则将链表中下一元素与栈中再弹出元素比较,直至链表到尾。这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中一元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。 int dc(LinkedList h,int n)

∥ h是带头结点的n个元素单链表,链表中结点的数据域是字符。本算法判断链表是

否是中心对称。

{char s[]; int i=1;∥i记结点个数, s字符栈

p=h->next;∥p是链表的工作指针,指向待处理的当前元素。 for(i=1;i<=n/2;i++)∥ 链表前一半元素进栈。 {s[i]=p->data;p=p->next;}

i--; ∥恢复最后的i值

if(n%2==1)p=p->next;} ∥若n是奇数,后移过中心结点。

while(p!=null && s[i]==p->data){i--;p=p->next;} ∥测试是否中心对称。 if(p==null)return(1);∥链表中心对称 else return(0); ∥链表不中心对称 }∥算法结束。

[算法讨论] 算法中先将“链表的前一半”元素(字符)进栈。当n为偶数时,前一半和后一半的个数相同;当n为奇数时,链表中心结点字符不必比较,移动链表指针到下一字符开始比较。比较过程中遇到不相等时,立即退出while循环,不再进行比较。

14.[题目分析] 在单链表中删除自第i个元素起的共len个元素,应从第1个元素起开始计数,记到第i个时开始数len个,然后将第i-1个元素的后继指针指向第i+len个结点,实现了在A链表中删除自第i个起的len个结点。这时应继续查到A的尾结点,得到删除元素后的A链表。再查B链表的第j个元素,将A链表插入之。插入和删除中应注意前驱后继关系,不能使链表“断链”。另外,算法中应判断i,len和j的合法性。 LinkedList DelInsert(LinkedList heada,headb,int i,j,len) ∥heada和headb均是带头结点的单链表。本算法删除heada链表中自第i个元素起的共len个元素,然后将单链表heada插入到headb的第j个元素之前。

{if(i<1 || len<1 || j<1){printf(“参数错误\\n”);exit(0);}∥参数错,退出算法。

p=heada;∥p为链表A的工作指针,初始化为A的头指针,查到第i个元素时,p指向第i-1个元素 k=0;∥计数

while(p!=null && knext;} if(p==null){printf(“给的%d太大\\n”,i);exit(0);} ∥i太大,退出算法 q=p->next;∥q为工作指针,初始指向A链表第一个被删结点。 k=0;

while(q!=null && knext;free(u);} ∥删除结点,后移

指针。

if(knext=q;∥A链表删除了len个元素。

if (heada->next!=null) ∥heada->next=null说明链表中结点均已删除,无需往B

表插入

{while(p->next!=null)p= p->next;∥找A的尾结点。 q=headb;∥q为链表B的工作指针。 k=0;∥计数

while(q!=null && k

{k++;q= q->next;} ∥查找成功时,q指向第j-1个结点 if(q==null){printf(“给的%d太大\\n”,j);exit(0);} p->next=q->next; ∥将A链表链入

q->next=heada->next; ∥A的第一元素结点链在B的第j-1个结点之后 }//if

free(heada);∥释放A表头结点。 }∥算法结束。

与本题类似的题的解答如下:

(1)本题与第14题基本相同,不同之处仅在于插入B链表第j个元素之前的,不是删除了len个元素的A链表,而是被删除的len个元素。按照上题,这len个元素结点中第一个结点的指针p->next,查找从第i个结点开始的第len个结点的算法修改为:

k=1;q=p->next; //q指向第一个被删除结点

while(q!=null && knext;} if(k

15.[题目分析] 在递增有序的顺序表中插入一个元素x,首先应查找待插入元素的位置。因顺序表元素递增有序,采用折半查找法比顺序查找效率要高。查到插入位置后,从此位置直到线性表尾依次向后移动一个元素位置,之后将元素x插入即可。 void Insert(ElemType A[],int size, ElemType x)

∥ A是有size个元素空间目前仅有num(num

{low=1;high=num; //题目要求下标从1开始

while(low<=high)∥对分查找元素x的插入位置。 {mid=(low+high)/2;

if(A[mid]==x){low=mid+1;break;}

else if(A[mid]>x)high=mid-1 ;else low=mid+1 ; }

for(i=num;i>=low;i--) A[i+1]=A[i];∥元素后移。 A[i+1]=x; ∥将元素x插入。 }算法结束。

[算法讨论] 算法中当查找失败(即线性表中无元素x)时,变量low在变量high的右面(low=high+1)。移动元素从low开始,直到num为止。特别注意不能写成for(i=low;i<=num;i++)A[i+1]=A[i],这是一些学生容易犯的错误。另外,题中未说明若表中已有值为x的元素时不再插入,故安排在A[mid]= =x时,用low(=mid+1)记住位置,以便后面统一处理。查找算法时间复杂度为O(logn),而插入时的移动操作时间复杂度为O(n),若用顺序查找,则查找的时间复杂度亦为O(n)。

类似本题的其它题的解答:

(1)[题目分析] 本题与上面15题类似,不同之处是给出具体元素值,且让编写turbo pascal程序,程序如下:

PROGRAM example(input,output); TYPE pointer=^node; node=RECORD

data:integer; next:pointer; END; VAR head,q:pointer;

PROCEDURE create(VAR la:pointer); VAR x:integer; p,q:pointer; BEGIN

new(la);la^.next:=NIL;{建立头结点。}

read(x);q:=la;{q用以指向表尾。} WHILE NOT EOF DO {建立链表} BEGIN new(p);p^.data:=x;p^.next:=q^.next;q^.next:=p;q:=p; read(x); END; END;

PROCEDURE insert(VAR la:pointer;s:pointer); VAR p,q:pointer;found:boolean; BEGIN

p:= la^.next;{p为工作指针。} q:=la;{q为p的前驱指针。} found:=false;

WHILE(p<>NIL)AND NOT found

IF(p^.data

s^.next:=p;{将s结点插入链表} q^.next:=s; END;

BEGIN {main} writeln(“请按顺序输入数据,建立链表”) create(head); writeln(“请输入插入数据”) new(q);

readln(q^.data); insert(head,q); END.{程序结束}

[程序讨论] 在建立链表时,输入数据依次为12,13,21,24,28,30,42,键入CTRL-Z,输入结束。“插入数据”输26即可。本题编写的是完整的pascal程序。

16.[题目分析] 将具有两个链域的单循环链表,改造成双向循环链表,关键是控制给每个结点均置上指向前驱的指针,而且每个结点的前驱指针置且仅置一次。 void StoDouble(LinkedList la)

∥la是结点含有pre,data,link三个域的单循环链表。其中data为数据域,pre为空指针域,link是指向后继的指针域。本算法将其改造成双向循环链表。 {while(la->link->pre==null)

{la->link->pre=la; ∥将结点la后继的pre指针指向la。 la=la->link; ∥la指针后移。 }

} ∥算法结束。

[算法讨论] 算法中没有设置变量记住单循环链表的起始结点,至少省去了一个指针变量。当算法结束时,la恢复到指向刚开始操作的结点,这是本算法的优点所在。

17.[题目分析] 求两个集合A和B的差集A-B,即在A中删除A和B中共有的元素。由于集合用单链表存储,问题变成删除链表中的结点问题。因此,要记住被删除结点的前驱,以便顺利删除被删结点。两链表均从第一元素结点开始,直到其中一个链表到尾为止。 void Difference(LinkedList A,B,*n)

∥A和B均是带头结点的递增有序的单链表,分别存储了一个集合,本算法求两集合的差集,存储于单链表A中,*n是结果集合中元素个数,调用时为0

{p=A->next; ∥p和q分别是链表A和B的工作指针。

q=B->next; pre=A; ∥pre为A中p所指结点的前驱结点的指针。 while(p!=null && q!=null)

if(p->datadata){pre=p;p=p->next;*n++;} ∥ A链表中当前结点指针后移。 else if(p->data>q->data)q=q->next; ∥B链表中当前结点指针后移。

else {pre->next=p->next; ∥处理A,B中元素值相同的结点,应删除。

u=p; p=p->next; free(u);} ∥删除结点

18.[题目分析] 本题要求对单链表结点的元素值进行运算,判断元素值是否等于其序号的平方减去其前驱的值。这里主要技术问题是结点的序号和前驱及后继指针的正确指向。 int Judge(LinkedList la)

∥la是结点的元素为整数的单链表。本算法判断从第二结点开始,每个元素值是否等于其序号的平方减去其前驱的值,如是返回true;否则,返回false。 {p=la->next->next;∥p是工作指针,初始指向链表的第二项。 pre=la->next; ∥pre是p所指结点的前驱指针。

i=2; ∥i是la链表中结点的序号,初始值为2。 while(p!=null)

if(p->data==i*i-pre->data){i++;pre=p;p=p->next;} ∥结点值间的关系符合题目要求

else break; ∥当前结点的值不等于其序号的平方减去前驱的值。

if(p!=null)return(false); ∥未查到表尾就结束了。 else return(true); ∥成功返回。 }∥算法结束。

[算法讨论]本题不设头结点也无影响。另外,算法中还可节省前驱指针pre,其算法片段如下:

p=la;∥假设无头结点,初始p指向第一元素结点。 i=2;

while(p->next!=null) ∥初始p->next指向第二项。 if(p->next->data= =i*i-p->data) {i++;p=p->next;}

if(p->next!=null)return(false);∥失败 else return(true); ∥成功 19.[题目分析] 本题实质上是一个模式匹配问题,这里匹配的元素是整数而不是字符。因两整数序列已存入两个链表中,操作从两链表的第一个结点开始,若对应数据相等,则后移指针;若对应数据不等,则A链表从上次开始比较结点的后继开始,B链表仍从第一结点开始比较,直到B链表到尾表示匹配成功。A链表到尾B链表未到尾表示失败。操作中应记住A链表每次的开始结点,以便下趟匹配时好从其后继开始。

int Pattern(LinkedList A,B)

∥A和B分别是数据域为整数的单链表,本算法判断B是否是A的子序列。如是,返回1;否则,返回0表示失败。

{p=A; ∥p为A链表的工作指针,本题假定A和B均无头结点。

pre=p; ∥pre记住每趟比较中A链表的开始结点。 q=B; ∥q是B链表的工作指针。 while(p && q)

if(p->data==q->data) {p=p->next;q=q->next;}

else{pre=pre->next;p=pre; ∥A链表新的开始比较结点。 q=B;} ∥q从B链表第一结点开始。 if(q==null)return(1); ∥B是A的子序列。 else return(0); ∥B不是A的子序列。 }∥算法结束。

20.[题目分析] 本题也是模式匹配问题,应先找出链表L2在链表L1中的出现,然后将L1中的L2倒置过来。设L2在L1中出现时第一个字母结点的前驱的指针为p,最后一个字母结点在L1中为q所指结点的前驱,则在保存p后继结点指针(s)的情况下,执行p->next=q。之后将s到q结点的前驱依次插入到p结点之后,实现了L2在L1中的倒置。 LinkedList PatternInvert(LinkedList L1,L2)

∥L1和L2均是带头结点的单链表,数据结点的数据域均为一个字符。本算法将L1中与L2中数据域相同的连续结点的顺序完全倒置过来。

{p=L1; ∥p是每趟匹配时L1中的起始结点前驱的指针。 q=L1->next; ∥q是L1中的工作指针。 s=L2->next; ∥s是L2中的工作指针。 while(p!=null && s!=null)

if(q->data==s->data){q=q->next;s=s->next;} ∥对应字母相等,指针后移。 else {p=p->next;q=p->next;s=L2->next;} ∥失配时,L1起始结点后移,L2从首结点开始。

if(s==null)∥匹配成功,这时p为L1中与L2中首字母结点相同数据域结点的前驱,q

为L1中与L2最后一个结点相同数据域结点的后继。

{r=p->next; ∥r为L1的工作指针,初始指向匹配的首字母结点。 p->next=q; ∥将p与q结点的链接。 while(r!=q); ∥逐结点倒置。 {s=r->next; ∥暂存r的后继。 r->next=p->next;∥将r所指结点倒置。 p->next=r;

r=s; ∥恢复r为当前结点。 } }

else printf(“L2并未在L1中出现”); } ∥算法结束。

[算法讨论] 本算法只讨论了L2在L1至多出现一次(可能没出现),没考虑在L1中多次出现的情况。若考虑多次出现,可在上面算法找到第一次出现后的q结点作L1中下次比较的第一字母结点,读者可自行完善之。

类似本题的另外叙述题的解答:

(1)[题目分析] 本题应先查找第i个结点,记下第i个结点的指针。然后从第i+1个结点起,直至第m(1

∥L是有m个结点的链表的头结点的指针。表中从第i(1

{if(i<1|| i>=m || m<4){printf(“%d,%d参数错误\\n”,i,m);exit(0);} p=L->next->next; ∥p是工作指针,初始指向第二结点(已假定i>1)。 pre=L->next; ∥pre是前驱结点指针,最终指向第i-1个结点。 j=1; ∥计数器

while(j

{j++;pre=p;p=p->next;}∥查找结束,p指向第i个结点。 q=p; ∥暂存第i个结点的指针。

p=p->next; ∥p指向第i+1个结点,准备逆置。

j+=2; ∥上面while循环结束时,j=i-1,现从第i+1结点开始逆置。

while(j<=m)

{r=p->next; ∥暂存p的后继结点。 p->next=pre->next;∥逆置p结点。 pre->next=p;

p=r; ∥p恢复为当前待逆置结点。 j++; ∥计数器增1。 }

q->next=pre->next;∥将原第i个结点的后继指针指向原第m个结点。

[算法讨论] 算法中未深入讨论i,m,j的合法性,因题目的条件是m>3且1next=pre->next,实现了从原第i个结点到原第m个结点的循环。最后pre->next正是指向原第m个结点,不可用p->next代替pre->next。

21.[题目分析] 顺序存储结构的线性表的逆置,只需一个变量辅助空间。算法核心是选择循环控制变量的初值和终值。

void SeqInvert(ElemType a[ ],int n)

∥a是具有n个元素用一维数组存储的线性表,本算法将其逆置。 {for(i=0;i<=(n-1)/2;i++)

{t=a[i];a[i]= a[n-1-i];a[n-1-i]=t;} }∥算法结束

[算法讨论] 算法中循环控制变量的初值和终值是关键。C中数组从下标0开始,第n个元素的下标是n-1。因为首尾对称交换,所以控制变量的终值是线性表长度的一半。当n为偶数,“一半”恰好是线性表长度的二分之一;若n是奇数,“一半”是小于n/2的最大整数,这时取大于1/2的最小整数的位置上的元素,恰是线性表中间位置的元素,不需要逆置。另外,由于pascal数组通常从下标1开始,所以,上下界处理上略有不同。这点请读者注意。

类似本题的其它题的解答: 这一组又选了6个题,都是单链表(包括单循环链表)的逆置。链表逆置的通常作法是:将工作指针指向第一个元素结点,将头结点的指针域置空。然后将链表各结点从第一结点开始直至最后一个结点,依次前插至头结点后,使最后插入的结点成为链表的第一结点,第一个插入的结点成为链表的最后结点。

(1)要求编程实现带头结点的单链表的逆置。首先建立一单链表,然后逆置。

typedef struct node

{int data;∥假定结点数据域为整型。 struct node *next; }node,*LinkedList; LinkedList creat( ) {LinkedList head,p int x;

head=(LinkedList)malloc(sizeof(node)); head->next=null; /*设置头结点*/ scanf(“%d”,&x);

while(x!=9999) /*约定输入9999时退出本函数*/ {p=(LinkedList)malloc(sizeof(node)); p->data=x;

p->next=head->next;/* 将新结点链入链表*/ head->next=p; scanf(“%d”,&x); }

return(head); }∥结束creat函数。

LinkedList invert1(LinkedList head) /*逆置单链表*/

{LinkedList p=head->next; /*p为工作指针*/ head->next=null; while(p!=null)

{r=p->next; /*暂存p的后继*/ p->next=head->next; head->next=p; p=r; }

return(head);

}/*结束invert1函数*/ main()

{LinkedList la; la=creat( ); /*生成单链表*/ la=invert1(la);/*逆置单链表*/ }

(2)本题要求将数据项递减有序的单链表重新排序,使数据项递增有序,要求算法复杂度为O(n)。虽没说要求将链表逆置,这只是叙述不同,本质上是将单链表逆置,现编写如下:

LinkedList invert2(LinkedList la)

∥la是带头结点且数据项递减有序的单链表,本算法将其排列成数据项递增有序的单链表。 {p=la->next; /*p为工作指针*/ la->next=null; while(p!=null)

{r=p->next; /*暂存p的后继。*/

p->next=la->next;/*将p结点前插入头结点后。*/ la->next=p;p=r; }

}∥结束算法

(3)本题要求倒排循环链表,与上面倒排单链表处理不同之处有二:一是初始化成循环链表而不是空链表;二是判断链表尾不用空指针而用是否是链表头指针。算法中语句片段如下:

p=la->next; ∥p为工作指针。

la->next=la; ∥初始化成空循环链表。 while(p!=la) ∥当p=la时循环结束。 {r=p->next; ∥暂存p的后继结点 p->next=la->next;∥逆置 la->next=p; p=r; }

(4)不带头结点的单链表逆置比较复杂,解决方法可以给加上头结点:

la=(LinkedList)malloc(sizeof(node)); la->next=L;

之后进行如上面(2)那样的逆置,最后再删去头结点: L=la->next; ∥L是不带头结点的链表的指针。 free(la); ∥释放头结点。

若不增加头结点,可用如下语句片段: p=L->next; ∥p为工作指针。

L->next=null;∥第一结点成为尾结点。 while(p!=null) {r=p->next;

p->next=L;∥将p结点插到L结点前面。

L=p; ∥L指向新的链表“第一”元素结点。 p=r; } (5)同(4),只是叙述有异。 (6)同(2),差别仅在于叙述不同。

22.[题目分析] 在无序的单链表上,查找最小值结点,要查遍整个链表,初始假定第一结点是最小值结点。当找到最小值结点后,判断数据域的值是否是奇数,若是,则“与其后继结点的值相交换”即仅仅交换数据域的值,用三个赋值语句即可交换。若与后继结点交换位置,则需交换指针,这时应知道最小值结点的前驱。至于删除后继结点,则通过修改最小值结点的指针域即可。

[算法设计]

void MiniValue(LinkedList la)

∥la是数据域为正整数且无序的单链表,本算法查找最小值结点且打印。若最小值结点的数值是奇数,则与后继结点值交换;否则,就删除其直接后继结点。

{p=la->next; ∥设la是头结点的头指针,p为工作指针。

pre=p; ∥pre指向最小值结点,初始假定首元结点值最小。 while(p->next!=null) ∥p->next是待比较的当前结点。 {if(p->next->datadata)pre=p->next;

p=p->next; ∥后移指针

}

printf(“最小值=%d\\n”,pre->data); if(pre->data%2!=0) ∥处理奇数

if(pre->next!=null)∥若该结点没有后继,则不必交换

{t= pre->data;pre->data=pre->next->data;pre->next->data=t;}∥交换完毕

else∥处理偶数情况

if(pre->next!=null)∥若最小值结点是最后一个结点,则无后继 {u=pre->next;pre->next=u->next;free(u);} ∥释放后继结点空间

23.[题目分析] 将一个结点数据域为字符的单链表,分解成含有字母字符、数字字符和其它字符的三个循环链表,首先要构造分别含有这三类字符的表头结点。然后从原链表第一个结点开始,根据结点数据域是字母字符、数字字符和其它字符而分别插入到三个链表之一的链表。注意不要因结点插入新建链表而使原链表断链。另外,题目并未要求链表有序,插入采用“前插法”,每次插入的结点均成为所插入链表的第一元素的结点即可。

void OneToThree(LinkedList L,la,ld,lo)

∥L是无头结点的单链表第一个结点的指针,链表中的数据域存放字符。本算法将链表L分解成含有英文字母字符、数字字符和其它字符的带头结点的三个循环链表。

{la=(LinkedList)malloc(sizeof(LNode)); ∥建立三个链表的头结点 ld=(LinkedList)malloc(sizeof(LNode));

lo=(LinkedList)malloc(sizeof(LNode));

la->next=la;ld->next=ld;lo->next=lo;∥置三个循环链表为空表 while(L!=null)∥分解原链表。

{r=L; L=L->next; ∥L指向待处理结点的后继

if(r->data>=‘a’&& r->data<=‘z’|| r->data>=‘A’&& r->data<=‘Z’) {r->next=la->next; la->next=r;} ∥处理字母字符。 else if(r->data>=‘0’&& r->data<=‘9’)

{r->next=ld->next;ld->next=r;} ∥处理数字字符 else {r->next=lo->next;lo->next=r;} ∥处理其它符号。

}∥结束while(L!=null)。

}∥算法结束

[算法讨论] 算法中对L链表中每个结点只处理一次,时间复杂度O(n),只增加了必须的三个表头结点,符合题目“用最少的时间和最少的空间”的要求。

24.[题目分析] 在递增有序的线性表中,删除数值相同的元素,要知道被删除元素结点的前驱结点。

LinkedList DelSame(LinkedList la)

∥la是递增有序的单链表,本算法去掉数值相同的元素,使表中不再有重复的元素。 {pre=la->next;∥pre是p所指向的前驱结点的指针。

p=pre->next;∥p是工作指针。设链表中至少有一个结点。 while(p!=null)

if(p->data==pre->data) ∥处理相同元素值的结点 {u=p;p=p->next;free(u);} ∥释放相同元素值的结点

else {pre->next=p;pre=p;p=p->next;} ∥处理前驱,后继元素值不同 pre->next=p;∥置链表尾。 }∥DelSame