当前位置: 首页 > news >正文

数据结构摘星题库800题笔记 第2章线性表

第2章线性表

§2.1 线性表的概念

考点 线性表的定义和特点

题组闯关

1.链式存储设计时,节点内的存储单元地址( )。
A. 一定连续
B. 一定不连续
C. 不一定连续
D. 部分连续,部分不连续

1.参考答案:A
解析:链式存储设计时,各个不同节点的存储空间可以不连续,但节点内的存储单元地址必须连续。

2.以下( )是一个线性表。
A. 由n个实数组成的集合
B. 由 100 个字符组成的序列
C. 所有整数组成的序列
D. 邻接表

2.参考答案:B
解析:线性表的定义:线性表是具有相同数据类型的 n(n≥0) 个数据元素的有限序列。选项 A 中实数不是有限序列,且不是相同数据类型。选项 C 中所有整数不是有限序列。选项 D 中图的邻接表存储方法是一种顺序分配和链式分配相结合的存储结构。

3.若线性表最常用的操作是存取第i个元素及其前驱和后继元素的值,为了提高效率,应采用( )的存储方式。
A. 单链表
B. 双向链表
C. 单循环链表
D. 顺序表

3.参考答案:D
解析:选项中只有顺序表可以随机存取,时间复杂度为 O(1)。其他都需要从头节点依次寻找,时间复杂度为 O(n)。

4.一个线性表最常用的操作是存取一指定序号的元素并在最后进行插入、删除操作,则利用( )的存储方式可以节省时间。
A. 顺序表
B. 双链表
C. 带头节点的双循环链表
D. 单循环链表

4.参考答案:A
解析:存取指定序号,顺序表可以随机存取,所需的时间比链表少,可以在 O(1) 的时间复杂度内完成。题目的插入、删除是在最后,所以顺序表和链表都行。所以综合来看,答案选择顺序表。

5.对于一个线性表既要求它能够进行较快速的插入和删除,又要求其存储结构能反映数据之间的逻辑关系,则应该用( )。
A. 顺序存储方式
B. 链式存储方式
C. 散列存储方式
D. 以上均可以

5.参考答案:B
解析:顺序存储插入和删除的时间复杂度是 O(n),链式存储的插入和删除的平均时间复杂度是 O(1),所以链式存储能够进行较快速的插入和删除。同时,链式存储的前后节点之间有明确的逻辑关系。散列存储查找的时间复杂度较低,但插入和删除的时间复杂度仍然高于链式存储。

6.某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用( )的存储方式最节省运算时间。
A. 单链表
B. 仅有头指针的单循环链表
C. 双链表
D. 仅有尾指针的单循环链表

6.参考答案:D
解析:带尾指针的单循环链表,就是单独有一个指针指向最后一个元素,这样最后一个元素和第一个元素的遍历时间复杂度都是 O(1)。
B 选项中查找到最后一个元素需要时间 O(n),比 D 慢。

7.线性表是具有n个( )的有限序列。
A. 表元素
B. 数据元素
C. 数据项
D. 信息项

7.参考答案:B
解析:数据元素是数据的基本单位,它是由若干数据项组成的。

8.下面关于线性表的叙述中,错误的是( )。
A. 线性表采用顺序存储,必须占用一片连续的存储单元
B. 线性表采用顺序存储,便于进行插入和删除操作
C. 线性表采用链接存储,不必占用一片连续的存储单元
D. 线性表采用链接存储,便于插入和删除操作

8.参考答案:B
解析:线性表采用顺序存储,便于进行查找操作,不便于进行插入和删除操作。

真题实战

1.下面关于线性表的叙述中,不正确的是( )。 【南京大学 2013 年】
Ⅰ. 线性表在链式存储时,查找第i个元素的时间同i的值成正比
Ⅱ. 线性表在链式存储时,查找第i个元素的时间同i的值无关
Ⅲ. 线性表在顺序存储时,查找第i个元素的时间同i的值成正比
Ⅳ. 线性表在顺序存储时,查找第i个元素的时间同i的值无关

A. Ⅰ、Ⅱ
B. Ⅱ、Ⅲ
C. Ⅲ、Ⅳ
D. Ⅰ、Ⅳ

参考答案:B
解析:线性表在顺序存储时可以随机存取,与 i 无关。链式存储时不能随机存取,查找时间与 i 成正比。

§2.2 线性表的分类

考点 1 顺序表

题组闯关

1.假设 8 行 10 列的二维数组a[1…8,1…10]分别以行序为主序和以列序为主序顺序存储时,其首地址相同,那么以行序为主序时元素a[3,5]的地址与以列序为主序时( )元素相同。注意:第一个元素为a[1,1]。
A. a[7,3]
B. a[8,3]
C. a[3,4]
D. 以上都不对

1.参考答案:D
解析:假设以行存储和以列存储的首地址都是 1,由于行和列的下标都是从 1 开始的,所以 a[3,5] 以行主序存储的地址表达式为:(i−1)×10+j,算出 a[3,5] 的地址为 (3−1)×10+5=25。如果是以列主序存储,那么其地址表达式为:i+(j−1)×8。将 A、B、C 选项分别代入表达式,得到的地址分别为:
A:7+2×8=23;
B:8+2×8=24;
C:3+3×8=27。
所以 A、B、C 都不对,选 D。如果存在正确选项的话应该是 a[4,4],(4−1)×8+4=25。

2.设长度为n的顺序存储线性表,在其中任何位置上插入或删除一个元素的概率相等,则删除一个元素时,平均需要移动( )个元素。
A. (n+1)/2
B. n/2
C. (n−1)/2
D. (n−2)/2

2.参考答案:C
解析:第一个元素删除时,其后面的 n−1 个元素都要前移;第二个元素删除时,其后面的 n−2 个元素需要移动。以此类推,所以总的移动次数为:n−1+n−2+...+1+0=n(n−1)/2,所以平均移动元素个数为:n(n−1)/2n=(n−1)/2。

3.向一个有 127 个元素的顺序表中插入一个新元素并保持原来的顺序不变,平均要移动( )个元素。
A. 8
B. 63.5
C. 63
D. 7

3.参考答案:B
解析:若是平均要移动的个数为 n/2,插入末尾,移动 0 个元素,插入表首移 n 个元素。

真题实战

1.已知一个三维数组A[1…15][0…9][−3…6]的每个元素占用 5 个存储单元,该数组总共需要的存储空间单元数为( )。 【北京邮电大学 2017 年】
A. 1500
B. 4050
C. 5600
D. 7500

1.参考答案:D
解析:总共需要的存储空间单元数为 15×10×10×5=7500。

2.若 6 行 5 列的数组以行序为主序顺序存储,基地址为 1000,每个元素占 2 个存储单元,则第 3 行第 4 列的元素(假定无第 0 行第 0 列)的地址是( )。 【中国科学院大学 2015 年】
A. 1040
B. 1042
C. 1026
D. 以上答案都不对

2.参考答案:C
解析:按行存储,第 1 行的基址是 1000,第 2 行的基址是 1010,第 3 行是 1020,所以第 3 行第 4 列的地址为:1020+2+2+2=1026。

3.二维数组A[0…7][0…9]中,每个元素占用 3 个存储单元,起始存储地址是 1000,则数组元素A[5][3]的存储地址是( )。 【北京邮电大学 2016 年】
A. 1126
B. 1141
C. 1156
D. 1159

3.参考答案:D
解析:此题考查数组矩阵的存储,注意题目并没有提及具体的存储形式,即按行还是按列。所以,按行存储时,该元素前面有 5 整行另加 3 个元素:(5×10+3)×3+1000=1159;按列存储时,该元素前面有 3 整列另加 5 个元素:(3×8+5)×3+1000=1087。

4.在长度为n的顺序表的第i个位置上插入一个元素(1≤i≤n+1),元素的移动次数为( )。 【中国海洋大学 2011 年】
A. n−i+1
B. n−i
C. i
D. i−1

4.参考答案:A
解析:在第 i 个位置插入元素时,该位置以及之后的所有元素都要往后移,一共 n−i+1 个元素。

5.对于长度为n的顺序表,假定删除表中任一元素的概率相同,则删除一个元素平均需要移动元素的个数是( )。 【广东工业大学 2013 年】
A. n
B. n/2
C. (n−1)/2
D. (n+1)/2

5.参考答案:C
解析:对长度为 n 的顺序线性表进行删除元素的操作,删除第 1 个元素移动元素的个数为 n−1。如果删除每一个元素的概率相同,则概率为 1/n,所以删除一个元素移动 (n−1)/2 个元素。

6.通常说顺序表具有随机存取特性,指的是( )。 【四川大学 2017 年】
A. 查找值为x的元素的时间与顺序表中元素个数n无关
B. 查找值为x的元素的时间与顺序表中元素个数n有关
C. 查找序号为i的元素的时间与顺序表中元素个数n无关
D. 查找序号为i的元素的时间与顺序表中元素个数n有关

6.参考答案:C
解析:随机存取是相对应于顺序存取而存在的概念。对于链表来说,它要读取它的第 n 个数据,那么只能从表头开始一个一个按顺序读取到第 n 个,因此称为顺序存取。对于顺序表来说,它要读取它的第 n 个数据,可以直接读出来,因此称为随机存取。

7.下述哪一条是顺序存储结构的优点( )。 【杭州电子科技大学 2018 年】
A. 存储密度大
B. 插入运算方便
C. 删除运算方便
D. 可方便地用于各种逻辑结构的存储表示

7.参考答案:A
解析:顺序存储利用物理的邻接关系表示数据元素之间的逻辑关系,因此没有必要设置指针域,所以其存储密度比链式存储大;B 和 C:插入运算和删除运算都需大量移动数据元素,并不方便;D 选项并不是顺序存储结构的优点。所以答案为 A。

8.假设顺序表中包含 5 个关键字{a,b,c,d,e},它们的查找概率分别为{0.25,0.3,0.2,0.1,0.15},为了使查找成功时的平均查找长度达到最小,则顺序表中数据元素的出现顺序是( )。 【北京工业大学 2017 年】
A. e,d,c,b,a
B. b,a,c,e,d
C. b,a,d,c,e
D. a,d,e,c,b

8.参考答案:B
解析:根据题干的 “使查找成功时的平均查找长度达到最小”,所以将经常需要查找(查找概率大)的数据放在顺序表中较前的位置。所以 b 的查找概率 0.3 最大,应放在顺序表最前位置。d 的查找概率 0.1 为最小,放在最后位置。

考点 2 单链表

题组闯关

1.下列选项中,( )是链表不具有的特点。
A. 插入和删除运算不需要移动元素
B. 所需要的存储空间与线性表的长度成正比
C. 不必事先估计存储空间大小
D. 可以随机访问表中的任意元素

1.参考答案:D
解析:链表不能随机存取。

2.单链表的存储密度( )。
A. 大于 1
B. 等于 1
C. 小于 1
D. 不能确定

2.参考答案:C
解析:存储密度 = 单链表数据项所占空间 / 节点所占空间。单链表节点所占空间包括数据项所占空间和存放后继节点地址的链域,所以,存储密度小于 1。

3.在一个长度为n(n>1)的单链表上,设有头和尾两个指针,执行( )操作与链表的长度有关。
A. 删除单链表中的第一个元素
B. 删除单链表中的最后一个元素
C. 在单链表第一个元素前插入一个新元素
D. 在单链表最后一个元素后插入一个新元素

3.参考答案:B
解析:A、C、D 选项查找要修改指针的节点的时间复杂度为 O(1),与链表的长度无关。B 查找的时间复杂度为 O(n),与链表的长度有关。

4.在一个单链表中,删除∗p节点(非尾节点)之后的一个节点的操作是( )。
A. p−>next=p
B. p−>next−>next=p−>next
C. p−>next−>next=p
D. p−>next=p−>next−>next

4.参考答案:D
解析:在单链表中删除 ∗p 节点之后的一个节点,仅需修改 ∗p 节点的指针域即可。

5.在一个单链表中,若要删除∗p节点的后继节点,则执行( )。
A. p−>next=p−>next−>next;
B. p−>next=p−>next−>next;free(p−>next);
C. p−>next=p−>next−>next;q=p−>next;free(q);
D. q=p−>next;p−>next=p−>next−>next;free(q);

5.参考答案:D
解析:本题主要考查删除单链表节点时指针的变化情况。删除节点时,首先记录所删节点的位置,即 q=p−>next,然后找 p 的新的后继。最后用 free 删除 q 指向的节点。完成删除。

6.将长度为n的单链表链接在长度为m的单链表之后的算法的时间复杂度为( )。
A. O(1)
B. O(n)
C. O(m)
D. O(m+n)

6.参考答案:C
解析:由于将长度为 n 的单链表链接在长度为 m 的单链表之后的操作,需要把长度为 m 的单链表遍历一遍,找到最后的一个节点,所以时间复杂度为 O(m)。

7.在一个具有n个节点的有序单链表中插入一个新节点并仍然有序的时间复杂度为( )。
A. O(1)
B. O(n)
C. O(n2)
D. O(log2​(n))

7.参考答案:B
解析:本题主要考查单链表插入节点时的时间复杂度。无论单链表是否有序,都只能顺序去找插入位置,所以有序单链表再插入节点仍保持有序的时间复杂度为 O(n)。另外延伸一下,若不是对链表进行插入,而是对顺序表(线性表的顺序存储表示)进行插入,则可以按照折半查找去寻找插入位置,此时时间复杂度为 O(log2​(n))。

真题实战

1.用单链表存储两个各有n个元素的有序表,若要将其归并成一个有序表,其最少的比较次数是( )。 【北京邮电大学 2017 年】
A. n−1
B. n
C. 2n−1
D. 2n

1.参考答案:B
解析:归并排序的基本思想是:归并排序是多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序的子表合并成一个有序的表,比较次数为 n。

2.用单链表方式存储队列(有头尾指针,非循环),在进行删除运算时( )。 【杭州电子科技大学 2018 年】
A. 仅修改头指针
B. 仅修改尾指针
C. 头、尾指针都须修改
D. 头、尾指针可能都要修改

2.参考答案:D
解析:链队列中删除元素一般仅修改队头指针,但只有一个元素时,出队后队空,此时还要修改队尾指针。

3.在单链表中,若需在p所指节点之后插入s所指节点,可执行语句( )。 【广东工业大学 2017 年】
A. s−>next=p;p−>next=s;
B. s−>next=p−>next;p=s;
C. s−>next=p−>next;p−>next=s;
D. p−>next=s;s−>next=p;

3.参考答案:C
解析:在一个单链表中插入一个元素,首先要生成一个指针 s 指向的节点,选项 C 中的第一条语句让插入位置之后的元素成为其后继节点,后一条语句使 s 成为 p 的后继节点,同时断开之前 s 的后继节点与 p 的联系。

4.h为不带头节点的单链表。在h的头上插入一个新节点t的语句是( )。【浙江大学 2010 年】
A. t−>next=h;h=t;
B. h=t;t−>next=h;
C. t−>next=h−>next;h=t;
D. h=t;t−>next=h−>next;

4.参考答案:A
解析:该题考查的是无头节点链表的头插法。首先,将 t 节点的下一个节点指向 h,然后将 t 改为新的头节点 h,头插法完成。选 A。

5.从一个具有n个节点的单链表中检索其值等于x的节点时,在检索成功的情况下,平均需比较的节点个数是( )。 【重庆大学 2013 年】
A. n/2
B. n
C. (n+1)/2
D. (n−1)/2

5.参考答案:C
解析:由于单链表只能进行单向顺序查找,以从第一个节点开始查找为例,查找第 m 个节点需要比较的节点数 f(m)=m,查找成功的最好情况是第一次就查找成功,只用比较 1 个节点,最坏情况则是最后才查找成功,需要比较 n 个节点。
所以一共有 n 种情况,平均下来需要比较的节点为 [1+2+3+...+(n−1)+n]/n=(n+1)/2。

6.在一个单链表中,已知q指向节点是p指向节点的前驱节点,若在q指向节点和p指向节点之间插入s指向节点,则需执行( )。 【上海海事大学 2016 年】
A. s−>next=p−>next;p−>next=s;
B. q−>next=s;s−>next=p;
C. p−>next=s−>next;s−>next=p;
D. p−>next=s;s−>next=q;

6.参考答案:B
解析:B 选项正确;A、C、D 选项中,都出现了 p−>next,在 p 前方插入元素时,p 的后继元素根本不会参与。所以 A、C、D 选项错误。

7.能正确完成删除单链表中p所指节点的后继的操作是( )。【电子科技大学 2010 年】
A. p=p−>next;
B. p−>next=p−>next−>next;
C. p−>next=p;
D. p=p−>next−>next;

7.参考答案:B
解析:本题考查单链表结构及指针的使用。删除 p 所指节点的后续节点,即把 p 所指节点的后续的后续节点的地址(p−>next−>next)赋值给 p 节点的 next 域。

8.已知头指针h指向一个带头节点的非空单循环链表,节点结构[data​next​],其中next是指向直接后继节点的指针,p是尾指针,q是临时指针。现要删除该链表的第一个元素,正确的语句序列是( )。 【全国统考 2021 年】
A. h−>next=h−>next−>next;q=h−>next;free(q);
B. q=h−>next;h−>next=h−>next−>next;free(q);
C. q=h−>next;h−>next=q−>next;if(p!=q)p=h;free(q);
D. q=h−>next;h−>next=q−>next;if(p==q)p=h;free(q);

8.参考答案:D
解析:本题考查单循环链表的删除。q=h−>next;h−>next=q−>next,q 指针指向待删除的第一个节点,头节点指向第二个节点,此时若尾指针 p 和 q 指针指向同一个位置的话,则我们需要修改尾指针 p,将其指向头节点(空单循环链表),否则,不需要修改,故 C 错。A 选项中,h−>next=h−>next−>next 修改了头节点的后继,q 指针指向的不是待删除的第一个节点,故 A 错。B 选项中,假设这个链表中只剩下最后一个节点(即尾指针 p 指向的节点),q=h−>next q 指针指向带删除的第一个节点(最后一个节点),则删除后,还需要修改 p 指针,故 B 错。故本题答案为 D。

9.单链表中访问当前节点的直接后继节点的时间复杂度为( )。 【广东工业大学 2019 年】
A. O(1)
B. O(n)
C. O(n^2)
D. O(logn)

9.参考答案:A
解析:访问后继节点只要一次间接寻址 p=p−>next,该步骤没有循环,时间复杂度是 O(1)。

10.已知两个长度分别为m和n的升序链表,若将它们合并为一个长度为m+n的降序链表,则最坏情况下的时间复杂度是( )。 【全国统考 2013 年】
A. O(n)
B. O(m×n)
C. O(min(m,n))
D. O(max(m,n))

10.参考答案:D
解析:最坏情况下的时间复杂度,即比较次数最多,为 n+m−1。选择的时候应该选择最大的那个数,所以答案是 D,为:O(max(m,n))。

考点 3 双链表

题组闯关

1.在双向链表存储结构中,删除p所指的节点时需要修改指针( )。
A. p−>next−>prior=p−>prior;p−>prior−>next=p−>next;
B. p−>next=p−>next−>next;p−>next−>prior=p;
C. p−>prior−>next=p;p−>prior=p−>prior−>prior;
D. p−>prior=p−>next−>next;p−>next=p−>prior−>prior;

1.参考答案:A
解析:删除 p 指向的节点,需要让 p 的后继节点的 prior 指向 p 的前驱节点,p 的前驱节点的 next 指针指向 p 的后继节点。

2.设双向链表中节点的结构为(prior,data,next),在双向链表中删除指针p所指的节点时需要修改指针( )。
A. p−>prior−>next=p−>next;p−>next−>prior=p−>prior;
B. p−>prior=p−>prior−>prior;p−>prior−>next=p;
C. p−>next−>prior=p;p−>next=p−>next−>next;
D. p−>next=p−>prior−>prior;p−>prior=p−>next−>next;

2.参考答案:A
解析:本题主要考查如何在双向链表中删除一个节点,与单链表上的插入和删除操作不同的是,在双向链表中插入和删除必须同时修改两个方向上的指针。

3.在一个双链表中,在∗p节点(非尾节点)之后插入一个节点∗s的操作是( )。
A. s−>prior=p;p−>next=s;p−>next−>prior=s;s−>next=p−>next;
B. s−>next=p−>next;p−>next−>prior=s;p−>next=s;s−>prior=p;
C. p−>next=s;s−>prior=p;s−>next=p−>next;p−>next−>prior=s;
D. p−>prior=s;s−>next=p;s−>next−>prior=p;p−>next=s−>next;

3.参考答案:B
解析:在 ∗p 节点之后插入一个节点 ∗s,相当于在 p 节点和 p−>next 之间插入节点。双链表插入算法的注意事项:插入新节点的核心步骤为修改四个指针,要保证在插入的过程中不能断开链表原来的链,否则链表就断链了。

4.在长度为n(n≥1)的非空双链表L中,删除p所指节点的前驱节点(非头节点)的时间复杂度为( )。
A. O(1)
B. O(n)
C. O(n^2)
D. O(nlog2​(n))

4.参考答案:A
解析:双链表的每一个节点均有两个指针,分别指向自己的前驱和后继,所以双链表的插入、删除操作的复杂度仅为 O(1)。

真题实战

1.设指针变量p指向双向链表中节点A,指针变量s指向被插入的节点X,则在节点A的后面插入节点X的操作序列为( )。 【暨南大学 2017 年】
A. p−>next=s;s−>prior=p;p−>next−>prior=s;s−>prior=p−>next;
B. s−>prior=p;s−>next=p−>next;p−>next=s;s−>next−>prior=s;
C. p−>prior=s;p−>next−>prior=s;s−>prior=p;s−>next=p−>prior;
D. s−>prior=p;s−>next=p−>next;p−>next=s;p−>next−>prior=s;

1.参考答案:B
解析:先修改待插入节点的前驱和后继,再修改原来两个节点中后一个节点的前驱以及前一个节点的后继,可以简单地记为前驱,后继,前驱,后继的修改顺序。

2.在链表中若经常要删除表中最后一个节点或在最后一个节点之后插入一个新节点,则宜采用( )存储方式。 【昆明理工大学 2018 年】
A. 顺序表
B. 用尾指针标识的循环单链表
C. 单链表
D. 双向链表

2.参考答案:D
解析:对于需要经常增删的操作,顺序表一律不适用。对于本题中需要经常操作表中最后一个节点,普通单链表需要遍历所有元素才能找到最末尾元素,C 选项,当需要删除最后一个元素时,需要找到倒数第二个元素,此时仍旧需要遍历所有元素,所以这里适合用双向链表。

3.在双向链表中向p所指的节点之前插入一个节点q的操作为( )。 【杭州电子科技大学 2016 年】
A. p−>prior=q;q−>next=p;p−>prior−>next=q;q−>prior=p−>prior;
B. q−>prior=p−>prior;p−>prior−>next=q;q−>next=p;p−>prior=q−>next;
C. q−>prior=p;p−>prior=q;q−>prior−>next=q;q−>next=p;
D. p−>prior−>next=q;q−>next=p;q−>prior=p−>prior;p−>prior=q;

3.参考答案:D
解析:p 节点的前驱节点指向的后继节点指向 q;q 的后继节点指向 p;q 的前驱节点指向 p 的前驱节点;p 的前驱节点更新为 q。

4.双向链表中每个节点的指针域的个数为( )。 【重庆理工大学 2014 年】
A. 0
B. 1
C. 2
D. 3

4.参考答案:C
解析:在双向链表中,每个节点两个指针域,一个指向前驱节点,另一个指向后继节点。

考点 4 循环链表

题组闯关

1.单循环链表的主要优点是( )。
A. 从表中任一节点出发都能扫描到整个链表
B. 不再需要头指针了
C. 在进行插入、删除操作时,能更好地保证链表不断开
D. 已知某个节点的位置后,能够容易找到它的直接前趋

1.参考答案:A
解析:A 选项,我们知道(非循环的)单链表只能找到它之后的节点,而循环链表因为是循环的,即使不是双向的,也能通过绕一圈的方式找到它前面的节点。所以 A 正确。
B 选项,对于(非循环的)链表而言,头节点可以不存在,但是存在头节点作用会更好,而对于循环链表,必须要有头节点,不然的话,循环链表最大的作用 —— 循环就没有作用了,所以 B 不正确。
C 选项,在进行节点删除操作后,原则上链表都是断开的,关键是靠删除算法来保证其不断开,与是否循环没有关系。所以 C 不正确。
D 选项,在单向循环链表中,已知某个节点的位置很难得到它的直接前驱节点,D 不正确。

2.带头节点的单循环链表head为空的判定条件是( )。
A. head==NULL
B. head−>next==NULL
C. head−>next==head
D. head!==NULL

2.参考答案:C
解析:带头节点的单循环链表判空条件为 head−>next==head,此为记忆性基础知识。

真题实战

1.设双向循环链表中节点的结构为(data,lLink,rLink),且不带头节点。若想在指针p所指节点之后插入指针s所指节点,则应执行下列哪个操作( )。 【中国科技大学 2013 年】
A. p−>rLink=s;s−>lLink=p;p−>rLink−>lLink=s;s−>rLink=p−>rLink;
B. p−>rLink=s;p−>rLink−>lLink=s;s−>lLink=p;s−>rLink=p−>rLink;
C. s−>lLink=p;s−>rLink=p−>rLink;p−>rLink=s;p−>rLink−>lLink=s;
D. s−>lLink=p;s−>rLink=p−>rLink;p−>rLink−>lLink=s;p−>rLink=s;

1.参考答案:D
解析:在指针 p 所指节点之后插入指针 s 所指节点,需要先修改指针 s,将 s 的 lLink 域指向 p,将 s 的 rLink 域指向 p 的下一个节点,p 的下一个节点的 lLink 域指向 s,p 的 rLink 域指向 s。

2.若线性表最常用的运算是删除第一个元素、在末尾插入新元素,则最适合的存储方式是( )。 【北京邮电大学 2018 年】
A. 顺序表
B. 带尾指针的单循环链表
C. 单链表
D. 带头指针的单循环链表

2.参考答案:B
解析:由于规定了插入运算是在表尾插入一个新元素,删除运算是指删除表头第一个元素。如果使用单向链表,且仅有头指针的单向循环链表,每次插入节点都要遍历整个链表,找到链尾,才能进行插入。如果采用顺序存储,每次删除表头元素时,都要移动 n−1 个元素。如果使用仅有尾指针的单向循环链表,插入新元素时,仅需移动尾指针就可以了,删除节点时,只需一步操作就可以定位到头节点,就可以进行删除,因为头节点是尾指针的下一个节点。

3.最不适合用作链式队列的链表是( )。 【杭州电子科技大学 2016 年】
A. 只带队首指针的非循环双链表
B. 只带队首指针的循环双链表
C. 只带队尾指针的循环双链表
D. 只带队尾指针的循环单链表

3.参考答案:A
解析:由于非循环双链表只带队首指针,可在执行入队操作时需要修改队尾节点的指针域,而查找队尾节点需要 O(n) 的时间。B、C、D 项均可在 O(1) 的时间内找到队首和队尾。

考点 5 静态链表

题组闯关

1.需要分配较大空间,插入和删除不需要移动元素的线性表,其存储结构是( )。
A. 单链表
B. 线性链表
C. 静态链表
D. 顺序存储结构

1.参考答案:C
解析:静态链表是使用一维数组实现的链式存储结构,需要分配较大空间,插入和删除需要修改指针,不需要移动元素。

2.线性表的静态链表存储结构与顺序存储结构相比优点是( )。
A. 所有的操作算法实现简单
B. 便于随机存储
C. 便于插入和删除
D. 便于利用零散的存储空间

2.参考答案:C
解析:本题主要考查线性表的静态链表的特点。

考点 6 广义表

题组闯关

1.一个非空广义表的表尾( )。
A. 只能是子表
B. 不能是子表
C. 只能是原子元素
D. 可以是原子元素或子表

1.参考答案:A
解析:根据表头、表尾的定义可知:任何一个非空广义表的表头是表中第一个元素,它可以是原子,也可以是子表,而其表尾必定是子表。

2.广义表A(a),则表尾为( )。
A. a
B. (())
C. 空表
D. (a)

2.参考答案:C
解析:广义表 A(a),则表尾为空表,这是基础概念。

3.已知广义表L=((x,y,z),a,(u,t,w)),从L表中取出原子t的运算是( )。
A. head[tail[tail[L]]]
B. tail[head[head[tail[L]]]]
C. head[tail[head[tail[tail[L]]]]]
D. head[head[tail[tail[L]]]]3.参考答案:C
解析:

tail[L]=(a,(u,t,w));tail[tail[L]]=((u,t,w));head[tail[tail[L]]]=(u,t,w)tail[head[tail[tail[L]]]]=(t,w)head[tail[head[tail[tail[L]]]]]=t

所以选择 C。

真题实战

1.对广义表L=((a,b),(c,d),(e,f))进行GetHead(GetHead(GetTail(GetTail(L))))操作的结果是( )。 【广东工业大学 2015 年】
A. (c)
B. c
C. (e)
D. e

1.参考答案:D
解析:

GetHead(GetHead(GetTail(GetTail(L))))
=GetHead(GetHead(GetTail(GetTail(((a,b),(c,d),(e,f))))))
=GetHead(GetHead(GetTail(((c,d),(e,f)))))=GetHead(GetHead((e,f)))
=GetHead((e,f))
=e

2.一个非空广义表的表尾( )。 【广东工业大学 2019 年】
A. 不可能是子表
B. 只能是子表
C. 只能是原子
D. 可以是子表或原子

2.参考答案:B
解析:非空广义表,除表头外,其余元素构成的表称为表尾,所以非空广义表尾一定是个表。

3.广义表((a,b),(),(c,d))的长度是( )。 【广东工业大学 2016 年】
A. 1
B. 4
C. 3
D. 2

3.参考答案:D
解析:(a,b) 为第一个长度,(),(c,d) 为第二个。

4.设广义表L=((a,b,c)),则L的长度和深度分别为( )。 【上海海事大学 2018 年】
A. 1 和 1
B. 1 和 3
C. 1 和 2
D. 2 和 3

4.参考答案:C
解析:本题考查广义表的基本概念。广义表的长度为表中最上层元素的个数,则广义表 L 中只有一个元素,即 “(a,b,c)”,长度为 1。广义表的深度为表中括号的最大层数,本题为 2。故 C 选项正确。

5.若广义表L满足Head(L)=Tail(L),则L为( )。 【扬州大学 2017 年】
A. ()
B. (())
C. ((),())
D. ((),(),())

5.参考答案:B
解析:(A)Head(A)、Tail(A) 无定义,长度为 0 的空表,对其不能做求表头和表尾的运算。(B)Head(A)=(),Tail(A)=()。(C)Head(A)=(),Tail(A)=(())。(D)Head(A)=(),Tail(A)=((),())。

§2.3 简答题

题组闯关

1.设计一个算法判断单链表中元素是否是递增的。

1.参考答案与解析

bool IsIncrease(LinkList * head){if(head == NULL || head -> next == NULL){return true;}else{for(p = head, q = head -> next; q != NULL; p = q, q = q -> next){if(p -> data > q -> data)return false;}return true;}
}

2.设计一个算法将所有奇数移到所有偶数之前。

2.参考答案与解析

void quick_move(int a[ ], int start, int end){int tmp;while(start < end){while(end >= 0 && a[end] % 2 == 0){end --;}while(start < end && a[start] % 2 != 0){start ++;}if(start < end){tmp = a[start];a[start] = a[end];a[end] = tmp;}}
}

3.描述以下三个概念的区别:头指针、头节点、表头节点。

3.参考答案与解析
头指针是指向链表中第一个节点(即表头节点)的指针;在表头节点之前附设的节点称为头节点;表头节点为链表中存储线性表中第一个数据元素的节点。若链表中附设头节点,则不管线性表是否为空,头指针均不为空,否则表示空表的链表的头指针为空。

4.设计一个最优的算法实现输出链表中倒数第k个节点,定义链表结构如下:

struct ListNode
{int value;ListNode * next;
};

4.参考答案与解析
最直接的想法是首先遍历一次链表得到链表的长度 n,倒数第 k 个节点就是正数第 n−k+1 个节点,然后再遍历链表到第 n−k+1 个节点即可。想法没有问题,只是不符合题目的 “最优” 要求,“最优” 是要求使时间、空间复杂度最小。最佳的方案是只遍历一次链表。思路是声明两个指针 p1 和 p2,首先让 p1 移动 k−1 步,然后 p1、p2 同时移动,直到 p1−>next 为空,p2 所指向的节点就是倒数第 k 个节点。

ListNode * FindKthToTail(ListNode * head, int k){ListNode * p1, p2 = head;for(int i = 0; i < k - 1; i++){p1 = p1 -> next;}while(p1 -> next){p1 = p1 -> next;p2 = p2 -> next;}return p2;
}

5.设计一个算法实现在单链表中删除值相同的多余节点的算法。

5.参考答案与解析

typedef struct node{int data;struct node * next;
}LinkList;void DelSameNum(LinkList * head){for(p = head; p != NULL; p = p -> next){s = p;for(q = p -> next; q != NULL; ){if(q -> data == p -> data){s -> next = q -> next;free(q);q = s -> next;}else{s = q;q = q -> next;}}}
}

6.试以单链表为存储结构实现简单选择排序的算法。

6. 【参考答案与解析】

void Linklist_Select_Sort(LinkList * l) {for (p = l; p->next->next != NULL; p = p->next) {q = p->next;x = q->data;// 在 q 后面寻找元素值最小的节点for (r = q; r->next != NULL; r = r->next) { if (r->next->data < x) {x = r->next->data;s = r;}// 找到了值比 q->data 更小的最小节点if (s != q) { s->next = s->next;p->next = s->next;s->next = q;t = q->next;q->next = p->next->next;p->next->next = t;}}}
}

解析:该函数实现单链表的简单选择排序。外层循环遍历链表,内层循环在当前节点 q 之后找值最小的节点,若找到更小值的节点 s(s != q ),则通过修改指针调整节点位置,将最小节点移到当前排序段的正确位置,逐步完成链表排序。

7.假设有两个元素值递增有序的线性表La和Lb,均以带头节点的单链表作为存储结构,编写算法将La表和Lb表合并为一个按元素值递减有序排列的线性表Lc,并要求利用原表(La和Lb表)的节点空间存放Lc。

7.【参考答案与解析】
算法思想:对单链表 La 和 Lb 进行扫描,将表 La 和 Lb 当前节点中较小者,插入 Lc 表表头。
单链表的类型定义为:

struct Lnode{datatype data;struct Lnode *next;
};void merge_dowum(Linklist &La, Linklist &Lb, Linklist &Lc){pa = La -> next;pb = Lb -> next;pc = La;Lc -> next == NULL;while(pa || pb){if(!pa){  // 只要存在一个非空表,用 pc 指向待摘取元素pc = pa;pa = pa -> next;} else if(!pb){pc = pb;pb = pb -> next;} else if(pa -> data <= pb -> data){pc = pa;pa = pa -> next;} else {pc = pb;pb = pb -> next;}else{pc = pb;pb = pb -> next;}pc -> next = Lc -> next; // 将 pc 指向的节点插在 Lc 的头节点之后Lc -> next = pc;}
}

8.已知指针La和Lb分别是两个带头节点单链表的头指针,下列算法是将表La的第i个元素起的len个元素删除并插入到表Lb的第j(j≥1)个元素之前,试问此算法是否正确?若有错,请改正。
设i≥1,len≥1,i+len≤ListLength(La),1≤j≤ListLength(Lb)。

void insertsubList(ListNode * La, ListNode * Lb, int i, int j, int len){pre = La;pa = La -> next;k = 1;while(k < i){p = p -> next;k = k + 1;}s = p;while(k < len){s = s -> next;k = k + 1;}pre -> next = s -> next;q = Lb, k = 0;while(k < j){q = q -> next;k = k + 1;}q -> next = p;s -> next = q -> next;
}

8.【参考答案与解析】
代码中存在错误。
(1) 为删除 La 的第 i 个元素起的 len 个元素,需要保存第 i 个元素节点前驱节点的指针。
(2) k 在 3 个循环中的作用是类似的,都是用于累计本次循环中已扫描过的元素节点的个数,所以进入第二个循环之前,k 要重新赋值,即 k = 1。
(3) 为将子表插在表 Lb 的第 j (j≥1) 个元素之前,需要修改 Lb 的第 j - 1 个元素节点的指针,故第三个循环的循环条件为 k < j - 1。
(4) 将子表插入到表 Lb 中,修改指针的顺序有误。

void insertsub(LNode * La, LNode * Lb, int i, int j, int len){pre = La;  // pre 指向 La 的头节点p = La -> next;  // p 指向 La 头节点后的第一个元素k = 1;// 遍历到 La 的第 i - 1 个元素,pre 指向第 i - 1 个元素, p 指向 La 第 i 个元素while(k < i){pre = p;p = p -> next;k = k + 1;}s = p;  // s 指向 La 的第 i 个元素k = 1;while(k < len){s = s -> next;k = k + 1;}pre -> next = s -> next;  // 删除从 La 的第 i 个元素起的后 len 个q = Lb;  // q 指向 Lb 的头节点k = 0;while(k < j - 1){  // 令 q 指向 Lb 的第 j - 1 个元素q = q -> next;k = k + 1;}s -> next = q -> next;  // 将 La 的第 i + len - 1 个元素插入 Lb 的第 j 个元素之前q -> next = p;
}

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

void reverse(LinkList &L){p = NULL;q = L;while(q! = NULL){(1) __________;q -> next = p;p = q;(2) __________;}(3) __________;
}

9.【参考答案与解析】
while 循环中的 q -> next 表明在原表中 p 指向的节点是 q 指向节点的前趋。在对 q -> next 赋值之前需要先保存。在处理完当前节点 (q 指向的节点) 后,所有用于扫描的指针均需要后移。题目中要求在该算法中用 L 返回倒置后链表的头指针。
(1) r = q -> next
(2) q = r
(3) L = p

10.对链表设置头节点的作用是什么(至少说出两条好处)?

10.【参考答案与解析】
(1) 对带头节点的链表,在表的任何元素节点之前插入节点或删除节点所需要做的都是修改前一个节点的指针域。
(2) 对带头节点的线性表,头指针是指向头节点非空指针,因此空表与非空表的处理方式一样。

真题实战

1.假定采用带头节点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,例如,“loading” 和 “being” 的存储映像如下图所示。 【全国统考 2012 年】

设str1和str2分别指向两个单词所在单链表的头节点,链表节点结构为[data​next​],请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀的起始位置(如图中字符i所在节点的位置p)。要求:
(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用 C 或 C++ 或 Java 语言描述算法,关键之处给出注释。
(3) 说明你所设计算法的时间复杂度。

1.【参考答案与解析】
(1) 顺序遍历两个链表到尾节点时,并不能保证两个链表同时到达尾节点。这是因为两个链表的长度不同。假设一个链表比另一个链表长 k 个节点,我们先在长链表上遍历 k 个节点,之后同步遍历两个链表,这样就能够保证它们同时到达最后一个节点。由于两个链表从第一个公共节点到链表的尾节点都是重合的,所以它们肯定同时到达第一个公共节点。于是得到算法思路:
①分别求出 str1 和 str2 所指的两个链表的长度 m 和 n。
②将两个链表以表尾对齐:令指针 p、q 分别指向 str1 和 str2 的头节点,若 m≥n, 则使 p 指向链表中的第 m - n + 1 个节点;若 m < n, 则使 q 指向链表中的第 n - m + 1 个节点,即使指针 p 和 q 所指的节点到表尾的长度相等。
③反复将指针 p 和 q 同步向后移动,并判断它们是否指向同一节点。若 p 和 q 指向同一节点,则该点即为所求的共同后缀的起始位置。
(2)

LinkNode * Find_Lst_Common(LinkList str1, LinkList str2){int len1 = Length(str1), len2 = Length(str2);LinkNode * p, * q;for(p = str1; len1 > len2; len2 --){  // 使 p 指向的链表与 q 指向的链表等长p = p -> next;}for(q = str2; len1 < len2; len2 --){  // 使 q 指向的链表与 p 指向的链表等长q = q -> next;}while(p -> next!= NULL && p -> next!= q -> next){  // 查找共同后缀起始点p = p -> next;  // 两个指针同步向后移动q = q -> next;}return p -> next;  // 返回共同后缀的起始点
}

(3) 时间复杂度为:O (len1 + len2), 其中 len1、len2 分别为两个链表的长度。

2.已知一个整数序列A=(a0​,a1​,⋯,an−1​),其中0≤ai​<n(0≤i<n)。若存在ap1​​=ap2​​=⋯=apm​​=x且m>n/2(0≤pk​<n,1≤k≤m),则称x为A的主元素。例如A=(0,5,5,3,5,7,5,5),则 5 为主元素;又如A=(0,5,5,3,5,1,5,7),则A中没有主元素。假设A中的n个元素保存在一个一维数组中,请设计一个尽可能高效的算法,找出A的主元素。若存在主元素,则输出该元素;否则输出−1。要求:
(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用 C 或 C++ 或 Java 语言描述算法,关键之处给出注释。
(3) 说明你所设计算法的时间复杂度和空间复杂度。 【全国统考 2013 年】

2.【参考答案与解析】
(1) 算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素 Num。然后重新计数,确认 Num 是否是主元素。
算法可分为以下两步:
①选取候选主元素:依次扫描所给数组中的每个整数,将第一个遇到的整数 Num 保存到 c 中,记录 Num 的出现次数为 1; 若遇到的下一个整数仍等于 Num, 则计数加 1, 否则计数减 1; 当计数减到 0 时,将遇到的下一个整数保存到 c 中,计数重新记为 1, 开始新一轮计数,即从当前位置开始重复上述过程,直到扫描完全部数组元素。
②判断 c 中元素是否是真正的主元素:再次扫描该数组,统计 c 中元素出现的次数,若大于 n/2, 则为主元素;否则,序列中不存在主元素。
(2) 算法实现:

int Majority(int A[], int n){int i, c, count = 1;  // c 用来保存候选主元素,count 用来计数c = A[0];  // 设置 A[0]为候选主元素for(i = 1; i < n; i ++){  // 查找候选主元素if(A[i] == c){count ++;  // 对 A 中的候选主元素计数} else {if(count > 0){  // 处理不是候选主元素的情况count --;} else {  // 更换候选主元素,重新计数c = A[i];count = 1;}}}if(count > 0){for(i = count = 0; i < n; i ++){  // 统计候选主元素的实际出现次数if(A[i] == c){count ++;}}}if(count > n / 2){  // 确认候选主元素return c;} else {  // 不存在主元素return -1;}
}

(3) 时间复杂度为 O (n), 空间复杂度为 O (1)。

3.用单链表保存m个整数,节点的结构为:[data][link],且∣data∣≤n(n为正整数)。现要求设计一个时间复杂度尽可能高效的算法,对于链表中data的绝对值相等的节点,仅保留第一次出现的节点而删除其余绝对值相等的节点。例如,若给定的单链表head如下:

要求:
(1) 给出算法的基本设计思想。
(2) 使用 C 或 C++ 语言,给出单链表节点的数据类型定义。
(3) 根据设计思想,采用 C 或 C++ 语言描述算法,关键之处给出注释。
(4) 说明你所设计算法的时间复杂度和空间复杂度。 【全国统考 2015 年】

3.【参考答案与解析】
(1) 算法的核心思想是用空间换时间。使用辅助数组记录链表中已出现的数值,从而只需对链表进行一趟扫描。因为 | data|≤n,故辅助数组 q 的大小为 n + 1,各元素的初值均为 0。依次扫描链表中的各节点,同时检查 q [|data|] 的值,如果为 0,则保留该节点,并令 q [|data|]=1;否则,将该节点从链表中删除。
(2)

typedef struct node{int data;struct node * link;
}NODE;
typedef NODE * PNODE;

(3)

void func(PNODE h, int n){PNODE p = h, r;int * q, m;q = (int *)malloc(sizeof(int) * (n + 1));  // 申请n + 1个位置的辅助空间for(int i = 0; i < n + 1; i ++)  // 数组元素初值置0*(q + i) = 0;while(p -> link != NULL){m = p -> link -> data > 0 ? p -> link -> data : -p -> link -> data;if( *(q + m) == 0 ){  // 判断该节点的data是否已出现过*(q + m) = 1;  // 首次出现p = p -> link;  // 保留} else {  // 重复出现r = p -> link;p -> link = r -> link;  // 删除free(r);}}free(q);
}

(4) 时间复杂度为 O (m),空间复杂度为 O (n)。

4.已知由n(n≥2)个正整数构成的集合A={ak​∣0≤k<n},将其划分为两个不相交的子集A1​和A2​,元素个数分别是n1​和n2​,A1​和A2​中元素之和分别为S1​和S2​。设计一个尽可能高效的划分算法,满足∣n1​−n2​∣最小且∣S1​−S2​∣最大。要求:
(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用 C 或 C++ 语言描述算法,关键之处给出注释。
(3) 说明你所设计算法的平均时间复杂度和空间复杂度。 【全国统考 2016 年】

4.【参考答案与解析】
(1) 算法的基本设计思想。
由题意知,将最小的 n/2 个元素放在 A₁中,其余的元素放在 A₂中,分组结果即可满足题目要求。仿照快速排序的思想,基于枢轴将 n 个整数划分为两个子集。根据划分后枢轴所处的位置 i 分别处理:
①若 i = n/2,则分组完成,算法结束。
②若 i < n/2,则枢轴及之前的所有元素均属于 A₁,继续对 i 之后的元素进行划分。
③若 i > n/2,则枢轴及之后的所有元素均属于 A₂,继续对 i 之前的元素进行划分。
基于该设计思想实现的算法,无须对全部元素进行全排序,其平均时间复杂度是 O (n),空间复杂度是 O (1)。
(2) 算法实现。

int setPartition(int a[ ], int n){int pivotkey, low = 0, low0 = 0, high = n - 1, high0 = n - 1, flag = 1, k = n/2, i;int s1 = 0, s2 = 0;while(flag) {pivotkey = a[low];  // 选择枢轴while(low < high){  // 基于枢轴对数据进行划分while(low < high && a[high] >= pivotkey) -- high;if(low != high) a[low] = a[high];while(low < high && a[low] <= pivotkey) ++ low;if(low != high) a[high] = a[low];}  // end of while(low < high)a[low] = pivotkey;if(low == k - 1) {  // 如果枢轴是第n/2小元素,划分成功flag = 0;} else {  // 是否继续划分if(low < k - 1) {low0 = ++ low;high = high0;} else {high0 = -- high;low = low0;}}}for(i = 0; i < k; i ++) s1 += a[i];for(i = k; i < n; i ++) s2 += a[i];return s2 - s1;
}

(3) 平均时间复杂度是 O (n),空间复杂度是 O (1)。

5.已知无表头节点的单链表la及单链表lb存在,写一算法,删除单链表la中第i个节点起长度为len的节点,并将其插入至单链表lb第j个节点之前。 【杭州电子科技大学 2018 年】

5.【参考答案与解析】

p = s = la;
q = lb;
m = q -> next;
for(int a = 0; a < i; a ++){p = p -> next;s = s -> next;
}  // 执行完后p、s均指向第i个节点
for(int a = 0; a < len; a ++){p -> next = s -> next;s = s -> next;
}  // 执行完后即已删除la第i个节点起的len个节点
for(int a = 0; a < j - 1; a ++){q = q -> next;m = m -> next;
}  // 执行完后q为lb的j - 1个节点;执行完后m为lb的第j个节点
q -> next = la;  // lb的第j - 1个节点指向la
while(p -> next != null){p = p -> next;  // 执行完后p为la的最后一个节点p -> next = m;  // la的最后一个节点指向lb的第j个节点,完成
}

6.一个线性表的元素均为正整数,使用带头指针的单链表实现。编写算法:判断该线性表是否符合:所有奇数在前面,偶数在后面。 【苏州大学 2018 年】

6.【参考答案与解析】
链表所有元素对 2 取余,将非 0 元素下标存放到一个数组中,如果下标之间不相邻即代表该线性表不符合要求。

int discriminant(List &l){Vector <int > result;int index = 0;List p;p = l;while(p){if(p -> data % 2 != 0) {  // 奇数元素坐标入列result.append(index);}p = p -> next;index ++;}for(int i = 0; i < result.size() - 1; i ++){flag = result[i + 1] - result[i];if(flag != 1)  // 不满足条件return 0;}return 1;  // 满足条件
}

7.写出递归删除单链表中所有值为item的算法。 【苏州大学 2013 年】

7.【参考答案与解析】

delete(LinkList &L, ElemType item){int p;if(L == NULL)return;  // 递归出口if(L -> data == item) {  // 判断节点值是否为itemp = L;L = L -> next;free(p);  // 删除节点delete(L, item);  // 注意当删除节点时,调用的为L} else {delete(L -> next, item);}
}

8.给定一个值,求出所有得到的新值的个数。例如给出值为 345,将其各位数字相加得到新值为 12,对 12 各位相加得到的新值为 3,则对 345 得到的新值的个数为 3 个(包括其本身)。 【苏州大学 2013 年】

8.【参考答案与解析】
算法思想:需要使用双循环,外循环用于计数,并将和赋值给新的数,内循环用于统计数字的和。循环的结束条件需要注意,外循环的结束条件为数字只有一位,内循环的结束条件是数字为 0。

int newNum(int num){int sum = num, i = 1;while(sum % 10 != 0) {  // 统计个数并将新的和赋值给numi ++;num = sum;sum = 0;while(num != 0) {  // 每一位数相加的内循环sum += num % 10;num = num / 10;}}return i;
}

9.定义三元组(a,b,c)(a,b,c均为正数)的距离D=∣a−b∣+∣b−c∣+∣c−a∣。给定三个非空整数集合S1​、S2​和S3​,按升序分别存储在三个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1​,b∈S2​,c∈S3​)中的最小距离。例如S1​={−1,0,9},S2​={−25,−10,10,11},S3​={2,9,17,30,41},则最小距离为 2,相应的三元组为{9,10,9}。要求:
(1) 给出算法的基本设计思想。
(2) 根据设计思想,采用 C 或 C++ 语言描述算法,关键之处给出注释。
(3) 说明你所设计的算法的时间复杂度和空间复杂度。 【全国统考 2020 年】

(2)

public static int minDistance(int[] a, int[] b, int[] c) {int curDis = 0;int min = 0;int minDis = Integer.MIN_VALUE;int i = 0;int j = 0;int k = 0;while (i < a.length && j < b.length && k < c.length) {curDis = max(Math.abs(a[i] - b[j]), Math.abs(a[i] - c[k]), Math.abs(b[j] - c[k]));if (curDis < minDis) {minDis = curDis;}min = min(a[i], b[j], c[k]);if (min == a[i]) {i++;} else if (min == b[j]) {j++;} else {k++;}}return minDis;
}private static int max(int a, int b, int c) {int max = a > b ? a : b;max = max > c ? max : c;return max;
}private static int min(int a, int b, int c) {int min = a < b ? a : b;min = min < c ? min : c;return min;
}
// 计算三个数中的最大值
int max(int a, int b, int c) {int temp = (a > b) ? a : b;return (temp > c) ? temp : c;
}// 计算三个数中的最小值
int min(int a, int b, int c) {int temp = (a < b) ? a : b;return (temp < c) ? temp : c;
}// 计算三个数组的最小距离
int minDistance(int a[], int lenA, int b[], int lenB, int c[], int lenC) {int curDis = 0;int minVal = 0;int minDis = INT_MIN;  // 初始化为最小整数int i = 0, j = 0, k = 0;// 遍历三个数组,直到其中一个遍历完成while (i < lenA && j < lenB && k < lenC) {// 计算当前三元组的距离curDis = max(abs(a[i] - b[j]), abs(a[i] - c[k]), abs(b[j] - c[k]));// 更新最小距离if (curDis < minDis) {minDis = curDis;}// 找到当前三个元素中的最小值minVal = min(a[i], b[j], c[k]);// 移动指向最小值的指针if (minVal == a[i]) {i++;} else if (minVal == b[j]) {j++;} else {k++;}}return minDis;
}

http://www.dtcms.com/a/330214.html

相关文章:

  • [TG开发]简单的回声机器人
  • Linux信号量和信号
  • 淘汰人工巡检!企业配电室无线测温实战:0布线+240点位同步监控
  • @进程管理工具 - Glances工具详细指南
  • 20250813测试开发岗(凉)面
  • 《探索C++ set与multiset容器:深入有序唯一性集合的实现与应用》
  • 网络存储技术:数据存储架构的演进与全景解析
  • 计算机网络——协议
  • 基于SpringBoot+Vue的智能消费记账系统(AI问答、WebSocket即时通讯、Echarts图形化分析)
  • Python 类元编程(元类基础知识)
  • 推荐系统论文分享之多任务模型--PLE(二)
  • python与JavaScript的区别
  • MoviiGen1.1模型脚本调用
  • C语言队列的实现
  • AUTOSAR进阶图解==>AUTOSAR_SWS_TTCANInterface
  • 开发避坑指南(25):MySQL不支持带有limit语句的子查询的解决方案
  • 【学习嵌入式day23-Linux编程-文件IO】
  • imx6ull-驱动开发篇22——Linux 时间管理和内核定时器
  • 力扣top100(day02-04)--二叉树 01
  • 18.10 SQuAD数据集实战:5步高效获取与预处理,BERT微调避坑指南
  • 数据分析可视化学习总结(美妆2)
  • Python解包技巧全解析
  • Python 基础语法(一)
  • 多处理器技术:并行计算的基石与架构演进
  • 疯狂星期四文案网第38天运营日记
  • 继《念念有词》后又一作品《双刃》开播 马来西亚新人演员业文Kevin挑战多面角色引期待
  • CF每日3题(1600)
  • element-ui 时间线(timeLine)内容分成左右两侧
  • npm run dev 的作用
  • Unity_2D动画