03. C

2027 · OCR 辅助 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照

#### 03. C

随机存取是指在 $O(1)$ 的时间访问下标为 $i$ 的元素,所需时间与顺序表中的元素个数 $n$ 无关。

#### 04. B

顺序表所占的存储空间 = 表长×sizeof(元素的类型),表长和元素的类型显然会影响存储空间的大小。若元素为结构体类型,则元素中各字段的类型也会影响存储空间的大小。

<!-- page: 34 -->

2027年数据结构考研复习指导

05. D

题干实际要求能最快存取第 $i - 1$ 、 $i$ 和 $i + 1$ 个元素值。选项 A、B、C 都只能从头结点依次顺序查找,时间复杂度为 $O(n)$;只有顺序表可以按序号随机存取,时间复杂度为 $O(1)$。

06. A

只有顺序表可以按序号随机存取,且在最后进行插入和删除操作时不需要移动任何元素。

07. C

对说法 I,解析略;对说法 II,在最后位置插入新结点不需要移动元素,时间复杂度为 $O(1)$;对说法 III,被删结点后的结点需要依次前移,时间复杂度为 $O(n)$;对说法 IV,需要后移 $n - i$ 个结点,时间复杂度为 $O(n)$。

08. C

对说法 II,顺序表只需 3 次交换操作;链表需要分别找到两个结点前驱,第 4 个结点断链后再插入到第 2 个结点后,效率较低。对说法 III,需要依次顺序访问每个元素,时间复杂度相同。

09. C

需要将元素 $a_{i+1} \sim a_n$ 依次前移一位,共移动 $n - (i + 1) + 1 = n - i$ 个元素。

10. C

在第 $i$ 个位置插入一个元素,需要移动 $n - i + 1$ 个元素,时间复杂度为 $O(n)$。

11. C

对 $n$ 个元素进行排序的时间复杂度最小也要 $O(n)$(初始有序时),通常为 $O(n \log_2 n)$ 或 $O(n^2)$,通过第 8 章学习后会更容易理解。选项 B 和 D 显然错误。顺序表支持按序号的随机存取方式。

12. D

顺序存储需要连续的存储空间,在申请时需申请 $n + m$ 个连续的存储空间,然后将线性表原来的 $n$ 个元素复制到新申请的 $n + m$ 个连续的存储空间的前 $n$ 个单元。

13. D

对于顺序存储的有序表,查找指定值元素可以采用顺序查找法或折半查找法,平均时间复杂度最少为 $O(\log_2 n)$。插入指定值元素需要先找到插入位置,然后将该位置及之后的元素依次后移一个位置,最后将指定值元素插入到该位置,平均时间复杂度为 $O(n)$。删除第 $i$ 个元素需要将该元素之后的全部元素依次前移一个位置,平均时间复杂度为 $O(n)$。获取第 $i$ 个元素只需直接根据下标读取对应的数组元素即可,时间复杂度为 $O(1)$。

## 二、综合应用题

### 01. 【解答】

算法思想:搜索整个顺序表,查找最小值元素并记住其位置,搜索结束后用最后一个元素填补空出的原最小值元素的位置。

本题代码如下:

bool Del_Min(SqList &amp;amp;L,ElemType &amp;amp;value){
//删除顺序表 L 中最小值元素结点,并通过引用型参数 value 返回其值
//若删除成功,则返回 true;否则返回 false
if (L.length==0)
return false; //表空,中止操作返回
value=L.data[0];
int pos=0; //假定 0 号元素的值最小
for (int i=1; i&amp;lt;L.length; i++) //循环,寻找具有最小值的元素
if (L.data[i]&amp;lt;value){ //让 value 记忆当前具有最小值的元素
value=L.data[i];

&lt;!-- page: 35 --&gt;

第2章 线性表
23

```txt
pos=i;
}
L.data[pos]=L.data[L.length-1]; //空出的位置由最后一个元素填补
L.length--;
return true; //此时,value 为最小值
}
**注意** 本题也可用函数返回值返回,两者的区别是:函数返回值只能返回一个值,而参数返回(引用传参)可以返回多个值。 ## 02. 【解答】 算法思想:扫描顺序表 $L$ 的前半部分元素,对于元素 L.data[i]($0&lt;=i&lt;L.length/2$),将其与后半部分的对应元素 L.data[L.length-i-1]进行交换。 本题代码如下:
void Reverse(SqList &amp;amp;L) {
ElemType temp; //辅助变量
for (int i=0; i&amp;lt;L.length/2; i++) {
temp=L.data[i]; //交换 L.data[i] 与 L.data[L.length-i-1]
L.data[i]=L.data[L.length-i-1];
L.data[L.length-i-1]=temp;
}
}
## 03. 【解答】 解法1:用 $k$ 记录顺序表 $L$ 中不等于 $x$ 的元素个数(需要保存的元素个数),扫描时将不等于 $x$ 的元素移动到下标 $k$ 的位置,并更新 $k$ 值。扫描结束后修改 $L$ 的长度。 本题代码如下:
void del_x_1(SqList &amp;amp;L,ElemType x) {
//本算法实现删除顺序表 L 中所有值为 x 的数据元素
int k=0,i; //记录值不等于 x 的元素个数
for (i=0; i&amp;lt;L.length; i++)
if (L.data[i]!=x) {
L.data[k]=L.data[i];
k++; //不等于 x 的元素增 1
}
L.length=k; //顺序表 L 的长度等于 k
}
解法2:用 $k$ 记录顺序表 $L$ 中等于 $x$ 的元素个数,一边扫描 $L$,一边统计 $k$,并将不等于 $x$ 的元素前移 $k$ 个位置。扫描结束后修改 $L$ 的长度。 本题代码如下:
void del_x_2(SqList &amp;amp;L,ElemType x) {
int k=0, i=0; //k 记录值等于 x 的元素个数
while (i&amp;lt;L.length) {
if (L.data[i]==x)
k++;
else
L.data[i-k]=L.data[i]; //当前元素前移 k 个位置
i++;
}
L.length=L.length-k; //顺序表 L 的长度递减
}
此外,本题还可以考虑设头、尾两个指针($i = 1, j = n$),从两端向中间移动,在遇到最左端 <!-- page: 36 --> 2027年数据结构考研复习指导 值 $x$ 的元素时,直接将最右端值非 $x$ 的元素左移至值为 $x$ 的数据元素位置,直到两指针相遇。但这种方法会改变原表中元素的相对位置。 ## 04. 【解答】 算法思想:从前向后扫描顺序表 $L$,用 $k$ 记录值在 $s$ 和 $t$ 之间的元素个数(初始时 $k = 0$)。对于当前扫描的元素,若其值不在 $s$ 和 $t$ 之间,则前移 $k$ 个位置;否则执行 $k++$。每个不在 $s$ 和 $t$ 之间的元素仅移动一次,因此算法效率高。 本题代码如下:
bool Del_s_t(SqList &amp;amp;L,ElemType s,ElemType t){
//删除顺序表 L 中值在给定值 s 和 t(要求 s&lt;t)之间的所有元素 int=&quot;&quot; i,k=&quot;0;&quot; if(l.length=&quot;0||s&quot; {=&quot;&quot;&gt;=t)
return false; //线性表为空或 s、t 不合法,返回
for(i=0;i&lt;l.length;i++) (i=&quot;0;&quot; (l.data[i]=&quot;&quot; &lt;table=&quot;&quot; else=&quot;&quot; i++)=&quot;&quot; i&lt;=&quot;&quot; i,j=&quot;&quot; i,j;=&quot;&quot; i&lt;l.length=&quot;k;&quot; i&lt;=&quot;t)&quot; if(l.data[i]=&quot;L.data[i];&quot; l.length=&quot;k;&quot; l.data[i-k]=&quot;L.data[i];&quot; {=&quot;&quot;&gt;=s&amp;amp;&amp;amp;L.data[i]&amp;lt;=t) //当前元素前移 k 个位置
}
## 注意 本题也可从后向前扫描顺序表,每遇到一个值在 $s$ 和 $t$ 之间的元素,就删除该元素,其后的所有元素全部前移。但移动次数远大于前者,效率不够高。 ## 05. 【解答】 算法思想:注意是有序顺序表,值相同的元素一定在连续的位置上,用类似于直接插入排序的思想,初始时将第一个元素视为非重复的有序表。之后依次判断后面的元素是否与前面非重复有序表的最后一个元素相同,若相同,则继续向后判断,若不同,则插入前面的非重复有序表的最后,直至判断到表尾为止。 本题代码如下:
bool Delete_Same(SeqList&amp;amp; L){
if(L.length==0)
return false;
int i,j; //i存储第一个不相同的元素,j为工作指针
for(i=0,j=1;j&lt;l.length;j++) (i=&quot;0,&quot; (l.data[i]=&quot;L.data[j])&quot; 表找下一个与上一个元素值不同的元素=&quot;&quot; 扫到后,将元素前移=&quot;&quot; 扫到后,将元素前移=&quot;&quot; 扫描入前面的非重复有序表的最后,直至判断到表尾为止。=&quot;&quot; 表格本题中的有序表改为无序表,你能想到时间复杂度为=&quot;&quot; 表格的算法,请读者用序列1,2,2,2,2,3,3,3,4,4,5来手动模拟算法的执行过程,在模拟过程中要标注i和j所指示的元素。=&quot;&quot; 06.=&quot;&quot; 06.【解答】=&quot;&quot; 算法思想:首先,按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中。然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面。=&quot;&quot; 当前元素前移=&quot;&quot;&gt;&lt;/l.length;j++)&gt;&lt;/l.length;j++)&gt;&lt;/l.length;i++)&gt;

&lt;!-- page: 37 --&gt;

第2章 线性表
25

本题代码如下:

```java
bool Merge(SeqList A, SeqList B, SeqList &amp;amp;C) {
//将有序顺序表A与B合并为一个新的有序顺序表C
if(A.length + B.length &amp;gt; C.maxSize) //大于顺序表的最大长度
return false;
int i = 0, j = 0, k = 0;
while(i &amp;lt; A.length &amp;amp;&amp;amp; j &amp;lt; B.length) { //循环,两两比较,小者存入结果表
if(A.data[i] &amp;lt;= B.data[j])
C.data[k++] = A.data[i++];
else
C.data[k++] = B.data[j++];
}
while(i &amp;lt; A.length) //还剩一个没有比较完的顺序表
C.data[k++] = A.data[i++];
while(j &amp;lt; B.length)
C.data[k++] = B.data[j++];
C.length = k;
return true;
}
**注意** 本算法的方法非常典型,需要牢固掌握。 ## 07. 【解答】 算法思想:首先将数组 $A[m + n]$ 中的全部元素 $(a_{1}, a_{2}, a_{3}, \dots, a_{m}, b_{1}, b_{2}, b_{3}, \dots, b_{n})$ 原地逆置为 $(b_{n}, b_{n-1}, b_{n-2}, \dots, b_{1}, a_{m}, a_{m-1}, a_{m-2}, \dots, a_{1})$,然后对前 $n$ 个元素和后 $m$ 个元素分别使用逆置算法,即可得到 $(b_{1}, b_{2}, b_{3}, \dots, b_{n}, a_{1}, a_{2}, a_{3}, \dots, a_{m})$,从而实现顺序表的位置互换。 本题代码如下:
typedef int DataType;
void Reverse(DataType A[], int left, int right, int arraySize) {
//逆转(aleft, aleft + 1, aleft + 2, \dots, aright) 为 (aright, aright - 1, \dots, aleft)
if (left &amp;gt;= right || right &amp;gt;= arraySize)
return;
int mid = (left + right) / 2;
for(int i = 0; i &amp;lt;= mid - left; i++) {
DataType temp = A[left + i];
A[left + i] = A[right - i];
A[right - i] = temp;
}
}
void Exchange(DataType A[], int m, int n, int arraySize) {
/* 数组 A[m + n] 中,从 0 到 m-1 存放顺序表(a1, a2, a3, \dots, am),从 m 到 m+n-1 存放顺序表(b1, b2, b3, \dots, bn),算法将这两个表的位置互换 */
Reverse(A, 0, m + n - 1, arraySize);
Reverse(A, 0, n - 1, arraySize);
Reverse(A, n, m + n - 1, arraySize);
}
## 08. 【解答】 算法思想:顺序存储的线性表递增有序,可以顺序查找,也可以折半查找。题目要求“用最少的时间在表中查找数值为 $x$ 的元素”,这里应使用折半查找法。 本题代码如下:
void SearchExchangeInsert(ElemType A[], ElemType x) {
int low = 0, high = n - 1, mid; //low 和 high 指向顺序表下界和上界的下标
while(low &amp;lt;= high) {

&lt;!-- page: 38 --&gt;

2027年数据结构考研复习指导

```c
mid=(low+high)/2; //找中间位置
if(A[mid]==x) break; //找到x,退出while循环
else if(A[mid]&lt;x) (a[mid=&quot;1;&quot; (low=&quot;mid+1);&quot; (low=mid+1);=&quot;&quot; 子)=&quot;&quot; 子)=&quot;&quot; else=&quot;&quot; high=&quot;mid-1;&quot; high)=&quot;&quot; if(a[mid]=&quot;x&amp;amp;&amp;amp;mid!=n-1)&quot; if(inv=&quot;&quot; min)=&quot;&quot; q)=&quot;&quot; q):=&quot;&quot; {=&quot;&quot; }=&quot;&quot; }=&quot;&quot;&gt;high; i--) A[i+1]=A[i]; //后移元素
A[i+1]=x; //插入x
} //结束插入
本题的算法也可写成三个函数:查找函数、交换后继函数与插入函数。写成三个函数的优点是逻辑清晰且易读。 ## 09. 【解析】 1)算法的基本设计思想。 使用三个下标变量从小到大遍历数组。当三个下标变量指向的元素相等时,输出并向前推进指针,否则仅移动小于最大元素的下标变量,直到某个下标变量移出数组范围,即可停止。 2)算法的实现。
void samekey(int A[], int B[], int C[], int n) {
int i = 0, j = 0, k = 0; //定义三个工作指针
while (i &amp;lt; n &amp;amp;&amp;amp; j &amp;lt; n &amp;amp;&amp;amp; k &amp;lt; n) { //相同则输出,并集体后移
if (A[i] == B[j] &amp;amp;&amp;amp; B[j] == C[k]) {
printf(&quot;%d\n&quot;, A[i]);
i++; j++; k++;
} else {
int maxNum = max(A[i], max(B[j], C[k]));
if (A[i] &amp;lt; maxNum) i++;
if (B[j] &amp;lt; maxNum) j++;
if (C[k] &amp;lt; maxNum) k++;
}
}
3)每个指针移动的次数不超过 $n$,且每次循环至少有一个指针后移,故时间复杂度为 $O(n)$,算法只用到了常数个变量,空间复杂度为 $O(1)$。 ## 10. 【解答】 1)算法的基本设计思想: 可将问题视为把数组 $ab$ 转换成数组 $ba$($a$ 代表数组的前 $p$ 个元素,$b$ 代表数组中余下的 $n-p$ 个元素),先将 $a$ 逆置得到 $a^{-1}b$,再将 $b$ 逆置得到 $a^{-1}b^{-1}$,最后将整个 $a^{-1}b^{-1}$ 逆置得到 $(a^{-1}b^{-1})^{-1} = ba$。设 Reverse 函数执行将数组逆置的操作,对 abcdefgh 向左循环移动 $3(p=3)$ 个位置的过程如下: Reverse(0, p-1) 得到 cbadefgh: Reverse(p,n-1) 得到 cbahgfed: Reverse(0, n-1) 得到 defghabc。 注:在 Reverse 中,两个参数分别表示数组中待转换元素的始末位置。 2)使用C语言描述算法如下:
void Reverse(int R[], int from, int to) {
int i, temp;
}
```&lt;/x)&gt;

&lt;!-- page: 39 --&gt;

第2章 线性表
27

```javascript
for(i=0;i&amp;lt;(to-from+1)/2;i++)
{
temp=R[from+i];R[from+i]=R[to-i];R[to-i]=temp;
}
void Converse(int R[], int n, int p) {
Reverse(R, 0, p-1);
Reverse(R, p, n-1);
Reverse(R, 0, n-1);
}
3)上述算法中三个 Reverse 函数的时间复杂度分别为 $O(p/2)$、$O((n-p)/2)$ 和 $O(n/2)$,故所设计的算法的时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。 【另解】借助辅助数组来实现。算法思想:创建大小为 $p$ 的辅助数组 S,将 R 中前 $p$ 个整数依次暂存在 S 中,同时将 R 中后 $n-p$ 个整数左移,然后将 S 中暂存的 $p$ 个数依次放回到 R 中的后续单元。时间复杂度为 $O(n)$,空间复杂度为 $O(p)$。 ## 11. 【解答】 1)算法的基本设计思想如下。 分别求两个升序序列 $A$、$B$ 的中位数,设为 $a$ 和 $b$,求序列 $A$、$B$ 的中位数过程如下: ① 若 $a = b$,则 $a$ 或 $b$ 为所求中位数,算法结束。 ② 若 $a &lt; b$,则舍弃序列 $A$ 中较小的一半,同时舍弃序列 $B$ 中较大的一半,要求两次舍弃的长度相等。 ③ 若 $a &gt; b$,则舍弃序列 $A$ 中较大的一半,同时舍弃序列 $B$ 中较小的一半,要求两次舍弃的长度相等。 在保留的两个升序序列中,重复过程①、②、③,直到两个序列中均只含一个元素时为止,较小者为所求的中位数。 2)本题代码如下:
int M_Search(int A[], int B[], int n) {
int s1, d1, m1, s2, d2, m2;
s1 = 0; d1 = n-1;
s2 = 0; d2 = n-1;
while (s1 != d1 || s2 != d2) {
m1 = (s1 + d1) / 2;
m2 = (s2 + d2) / 2;
if (A[m1] == B[m2])
return A[m1]; //满足条件①
if (A[m1] &amp;lt; B[m2]) { //满足条件②
if ((s1 + d1) %2 == 0) { //若元素个数为奇数
s1 = m1; //舍弃 A 中间点以前的部分,且保留中间点
d2 = m2; //舍弃 B 中间点以后的部分,且保留中间点
}
else { //元素个数为偶数
s1 = m1 + 1; //舍弃 A 的前半部分
d2 = m2; //舍弃 B 的后半部分
}
}
else { //满足条件③
if ((s1 + d1) %2 == 0) { //若元素个数为奇数
d1 = m1; //舍弃 A 中间点以后的部分,且保留中间点
s2 = m2; //舍弃 B 中间点以前的部分,且保留中间点
}
else { //元素个数为偶数
d1 = m1; //舍弃 A 的后半部分
s2 = m2 + 1; //舍弃 B 的前半部分
<!-- page: 40 --> 2027年数据结构考研复习指导
}
}
}
return A[s1]&amp;lt;B[s2]? A[s1]:B[s2];
3)算法的时间复杂度为 $O(\log_2 n)$,空间复杂度为 $O(1)$。 【另解】对两个长度为 $n$ 的升序序列 $A$ 和 $B$ 中的元素按从小到大的顺序依次访问,这里访问的含义只是比较序列中两个元素的大小,并不实现两个序列的合并,因此空间复杂度为 $O(1)$。按照上述规则访问第 $n$ 个元素时,这个元素为两个序列 $A$ 和 $B$ 的中位数。