Files
Data-Structure/习题解析/02 线性表/第02章 线性表.md
2020-04-29 18:44:51 +08:00

515 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第2章 线性表
## 一、基础知识题
### 2.1 描述以下三个概念的区别:头指针,头结点,首元结点(第一个元素结点)。
> **首元结点**是指链表中存储线性表中第一个数据元素a1的结点。
> **头结点**是为了操作方便,在链表的首元结点之前附设的一个结点,该结点的数据域中不存储线性表的数据元素,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理。
> **头指针**是指向链表中第一个结点(或为头结点或为首元结点)的指针。若链表中附设头结点,则不管线性表是否为空表,头指针均不为空,否则表示空表的链表的头指针为空。
> 这三个概念对单链表、双向链表和循环链表均适用。是否设置头结点,是不同的存储结构表示同一逻辑结构的问题。
### 2.2 填空题
>1在顺序表中插入或删除一个元素需要平均移动 <u>表中一半</u> 元素,具体移动的元素个数与 <u>该元素的位置</u> 有关。
>2顺序表中逻辑上相邻的元素的物理位置 <u>必定</u> 相邻。单链表中逻辑上相邻的元素在物理位置 <u>不一定</u> 相邻。
>3在单链表中除了首元结点外任一结点的存储位置由 <u>其直接前驱结点的链域的值</u> 指示。
>4在单链表中设置头结点的作用是 <u>插入和删除首元素时不必进行特殊处理</u>。
### 2.3 在什么情况下用顺序表比链表好?
> 当不需频繁在存储的元素间进行插入和删除操作时,用顺序表较好。
### 2.4 对以下单链表分别执行下列各程序段,并画出结果示意图。
![2.4](_v_images/20181125013824965_16208.png)
**1**
```c
Q=P->next;
```
![2.4.1](_v_images/20181125014137666_10327.png)
**2**
```c
L=P->next;
```
![2.4.2](_v_images/20181125014156819_4098.png)
**3**
```c
R->data=P->data;
```
![2.4.3](_v_images/20181125014215622_10158.png)
**4**
```c
R->data=P->next->data;
```
![2.4.4](_v_images/20181125014234236_4049.png)
**5**
```c
P->next->next->next->data=P->data;
```
![2.4.5](_v_images/20181125014248274_14430.png)
**6**
```c
T=P;
while(T!=NULL)
{
T->data=T->data*2;
T=T->next;
}
```
![2.4.6](_v_images/20181125014311773_4232.png)
**7**
```c
T=P;
while(T->next!=NULL)
{
T->data=T->data*2;
T=T->next;
}
```
![2.4.7](_v_images/20200429182140887_31499.png)
> 虽然原图中最后有省略号但是在做此题时应将S结点视为链表的最后一个结点。
> &emsp;&emsp;因为从出题人的角度出发该题与2.4.6题要形成对照。
### 2.5 画出执行下列各行语句后各指针及链表的示意图。
```c
L = (LinkList) malloc (sizeof(LNode));
P = L;
for(i=1; i<=4; i++)
{
P->next= (LinkList) malloc (sizeof(LNode));
P = P->next;
P->data = i*2-1;
}
P->next = NULL;
for(i=4; i>=1; i--)
Ins_LinkList(L, i+1, i*2);
for(i=1; i<=3; i++)
Del_LinkList(L, i);
```
![2.5.1](_v_images/20181125014418601_7477.png)
![2.5.2](_v_images/20181125014438821_5785.png)
![2.5.3](_v_images/20181125014451902_15334.png)
![2.5.4](_v_images/20181125014506111_5250.png)
### 2.6 已知L是无表头结点的单链表且P结点既不是首元结点也不是尾元结点试从下列提供的答案中选择合适的语句序列。
>a.在P结点后插入S结点的语句序列是 <u>41</u>。
>b.在P结点前插入S结点的语句序列是 <u>711841</u>。
>c.在表首插入S结点的语句序列是 <u>512</u>。
>d.在表尾插入S结点的语句序列是 <u>916</u>。
>
>1P->next=S;
>2P->next=P->next->next;
>3P->next=S->next;
>4S->next=P->next;
>5S->next=L;
>6S->next=NULL;
>7Q=P;
>8while(P->next!=Q)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>9while(P->next!=NULL)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>10P=Q;
>11P=L;
>12L=S;
>13L=P;
### 2.7 已知L是带表头结点的非空单链表且P结点既不是首元结点也不是尾元结点试从下列提供的答案中选择合适的语句序列。
>a.删除P结点的直接后继结点的语句序列是 <u>11314</u>。
>b.删除P结点的直接前驱结点的语句序列是 <u>1012811314</u>。
>c.删除P结点的语句序列是 <u>10127314</u>。
>d.删除首元结点的语句序列是 <u>1211314</u>。
>e.删除尾元结点的语句序列是 <u>911314</u>。
>
>1P=P->next;
>2P->next=P;
>3P->next=P->next->next;
>4P=P->next->next;
>5while(P!=NULL)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>6while(Q->next!=NULL)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=Q;
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Q=Q->next;
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
>7while(P->next!=Q)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>8while(P->next->next!=Q)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>9while(P->next->next!=NULL)
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P=P->next;
>10Q=P;
>11Q=P->next;
>12P=L;
>13L=L->next;
>14free(Q);
### 2.8 已知P结点是某双向链表的中间结点试从下列提供的答案中选择合适的语句序列。
>a.在P结点后插入S结点的语句序列是 <u>71236</u>。
>b.在P结点前插入S结点的语句序列是 <u>84513</u>。
>c.删除P结点的直接后继结点的语句序列是 <u>1511118</u>。
>d.删除P结点的直接前驱结点的语句序列是 <u>1621018</u>。
>e.删除P结点的语句序列是 <u>14917</u>。
>
>1P->next=P->next->next;
>2P->priou=P->priou->priou;
>3P->next=S;
>4P->priou=S;
>5S->next=P;
>6S->priou=P;
>7S->next=P->next;
>8S->priou=P-priou;
>9P->priou->next=P->next;
>10P->priou->next=P;
>11P->next->priou=P;
>12P->next->priou=S;
>13P->priou->next=S;
>14P->next->priou=P->priou;
>15Q=P->next;
>16Q=P->priou;
>17free(P);
>18free(Q);
### 2.9 简述下列算法的功能。
**1**
```c
Status A(LinkedList L) //L是无表头结点的单链表
{
if(L&&L->next)
{
Q=L;
L=L->next;
P=L;
while(P->next)
P=P->next;
P->next=Q;
Q->next=NULL;
}
return OK;
}//A
```
> 1如果L的长度不小于2则将首元结点删去并插入表尾。
**2**
```c
void BB(LNode *s, LNode *q)
{
p=s;
while(p->next!=q)
p=p->next;
p->next=s;
}//BB
void AA(LNode *pa, LNode *pb)
{//pa和pb分别指向单循环链表中的两个结点
BB(pa, pb);
BB(pb, pa);
}//AA
```
> 2将单循环链表拆成两个单循环链表。
## 二、算法设计题
##### 本章算法题目涉及的顺序表和线性链表的类型定义如下:
```c
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef struct
{
ElemType *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配的存储容量
}SqList; //顺序表类型
// 注此文档中ElemType被定义为int类型。
typedef struct LNode
{
ElemType data;
Struct Lnode *next;
}LNode, *LinkList; //线性链表类型
```
### 2.10 指出以下算法的错误和低效(即费时)之处,并将它改写为一个既正确又高效的算法。
```c
Status DeleteK(SqList &a, int i, int k)
{ //本过程从顺序存储结构的线性表a中删除第i个元素起的k个元素
if(i<1 || k<0 || i+k>a.length)
return INFEASIBLE; //参数不合法
else
for(count=1; count<k; count++)
{ //删除一个元素
for(j=a.length; j>=i+1; j--)
a.elem[j-1] = a.elem[j];
a.length--;
}
return OK;
} //DeleteK
```
>错误有两处:
>1参数不合法的判别条件不完整。合法的入口参数条件为(0<i≤a.length) && (0≤k≤a.length-i+1)
>2第二个for语句中元素前移的次序错误。
>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;低效之处是每次删除一个元素的策略。
----------
### 2.11 设顺序表va中的数据元素递增有序。试写一算法将x插入到顺序表的适当位置上以保持该表的有序性。
----------
### 2.12 设A=(a1,...,an)和B=(b1,...,bn)均为顺序表A'和B'分别为A和B中除去最大共同前缀后的子表例如A=(x,y,y,z,x,z)B=(x,y,y,z,y,x,x,z),则两者中最大的共同前缀为(x,y,y,z)在两表中除去最大共同前缀后的子表分别为A'=(x,z)和B'=(y,x,x,z)。若A'=B'=空表则A=B若A'=空表而B'≠空表或者两者均不为空表且A'的首元小于B'的首元则A<B否则A>B。试写一个比较AB大小的算法请注意在算法中不要破坏原表A和B并且也不一定先求得A'和B'才进行比较)。
----------
### 2.13 试写一算法在带头结点的单链表结构上实现线性表操作LOCATE(L,X)。
----------
### 2.14 试写一算法在带头结点的单链表结构上实现线性表操作LENGTH(L)。
----------
### 2.15 已知指针ha和hb分别指向两个单链表的头结点并且已知两个链表的长度分别为m和n。试写一算法将这两个链表连接在一起即令其中一个表的首元结点连在另一个表的最后一个结点之后假设指针hc指向连接后的链表的头结点并要求算法以尽可能短的时间完成连接运算。请分析你的算法和时间复杂度。
----------
### 2.16 已知指针la和lb分别指向两个无头结点单链表中的首元结点。下列算法是从表la中删除自第i个元素起共len个元素后将它们插入到表lb中的第j个元素之前。试问此算法是否正确如有错则请改正之。
```c
Status DeleteAndInsertSub (LinkedList la, LinkedList lb, int i, int j, int len)
{
if(i<0 || j<0 || len<0)
return INFEASIBLE;
p=la; k=1;
while(k<i)
{
p=p->next;
k++;
}
q=p;
while(k<=len)
{
q=q->next;
k++;
}
s=lb;
k=1;
while(k<j)
{
s=s->next;
k++;
}
s->next=p;
q->next=s->next;
return OK;
} //DeleteAndInsertSub
```
### 2.17 试写一算法在无头结点的动态单链表上实现线性表操作INSERT(L, i, b),并和在带头结点的动态单链表上实现相同操作的算法进行比较。
### 2.18 同2.17题要求。试写一算法实现线性表操作DELETE(L, i)。
----------
### 2.19 已知线性表中的元素以值递增有序排列并以单链表作存储结构。试写一高效的算法删除表中所有值大于mink且小于maxk的元素若表中存在这样的元素同时释放被删结点空间并分析你的算法的时间复杂度注意mink和maxk是给定的两个参变量它们的值可以和表中的元素相同也可以不同
> 时间复杂度分析最坏的情况是全部扫描完也没找到适合的元素故时间复杂度与链表长度有关为O(Length(L))。
### 2.20 同2.19题条件(递增有序排列),试写一高效的算法,删除表中所有值相同的多余元素(使得操作后的线性表中所有元素的值均不相同),同时释放被删结点空间,并分析你的算法的时间复杂度。
----------
### 2.21 试写一算法,实现顺序表的就地逆置,即利用原表的存储空间将线性表(a1, a2, ..., an)逆置为(an, an-1, ..., a1)。
----------
### 2.22 试写一算法,对单链表实现就地逆置。
----------
### 2.23 设线性表A=(a1, a2, ..., am)B=(b1, b2, ..., bn)试写一个按下列规则合并AB为线性表C的算法即使得
###### &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C=(a1, b1, ..., am, bm, bm+1, ..., bn) 当m<=n时
###### 或者&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C=(a1, b1, ..., an, bn, an+1, ..., am) 当m>n时。
### 线性表AB和C均以单链表作存储结构且C表利用A表和B表中的结点空间构成。注意单链表的长度值m和n均未显式存储。
> 关键词:单链表
----------
### 2.24 假设有两个按元素值递增有序排列的线性表A和B均以单链表作存储结构请编写算法将A表和B表归并成一个按元素值递减有序即非递增有序允许表中含有值相同的元素排列的线性表C并要求利用原表即A表和B表的结点空间构造C表。
> 关键词:递增单链表
----------
### 2.25 假设以两个元素依值递增有序排列的线性表A和B分别表示两个集合即同一表中的元素值各不相同现要求另辟空间构成一个线性表C其元素为A和B中元素的交集且表C中的元素也依值递增有序排列。试对顺序表编写求C的算法。
> 关键词:递增无重复顺序表
----------
### 2.26 要求同2.25题。试对单链表编写求C的算法。
> 关键词:递增无重复单链表
----------
### 2.27 对2.25题的条件作以下修改对顺序表重新编写求得表C的算法。
###### 1假设在同一表A或B中可能存在值相同的元素但要求新生成的表C中的元素值各不相同
###### 2利用A表空间存放表C。
> 关键词:递增有重复顺序表
----------
### 2.28 对2.25题的条件作以下两点修改对单链表重新编写求得表C的算法。
###### 1假设在同一表A或B中可能存在值相同的元素但要求新生成的表C中的元素值各不相同。
###### 2利用原表A表或B表中的结点构造表C并释放A表中的无用结点空间。
> 关键词:递增有重复单链表,释放无效结点
----------
### 2.29 已知AB和C为三个递增有序的线性表现要求对A表作如下操作删去那些既在B表中出现又在C表中出现的元素。试对顺序表编写实现上述操作的算法并分析你的算法的时间复杂度注意同一表中各元素值可能相同
----------
### 2.30 要求同2.29题。试对单链表编写算法请释放A表中的无用结点空间。
----------
### 2.31 假设某个单向循环链表的长度大于1且表中既无头结点也无头指针。已知s为指向链表中某个结点的指针试编写算法在链表中删除指针s所指结点的前驱结点。
----------
### 2.32 已知有一个单向循环链表其每个结点中含三个域predata和next其中data为数据域next为指向后继结点的指针域pre也为指针域但它的值为空(NULL)试编写算法将此单向循环链表改为双向循环链表即使pre称为指向前驱结点的指针域。
----------
### 2.33 已知由一个线性链表表示的线性表中含有三类字符的数据元素(如:字母字符、数字字符和其他字符),试编写算法将该线性链表分割为三个循环链表,其中每个循环链表表示的线性表中均只含一类字符。
----------
##### 在2.34至2.36题中,“异或指针双向链表”类型`XorLinkedList`和指针异或函数`XorP`定义为:
```c
typedef struct XorNode
{
char data;
struct XorNode LRPtr;
} XorNode, *XorPointer;
//无头结点的异或指针双向链表
typedef struct
{
XorPointer Left, Right; //分别指向链表的左端和右端
} XorLinkedList;
XorPointer XorP(XorPointer p, XorPointer q); //指针异或函数XorP返回指针p和q的异或(XOR)值
```
##### 异或表
![异或表](_v_images/20181125021743781_1329.png)
##### 异或指针链表的动态创建过程
![异或1](_v_images/20181125021828395_8351.png)
![异或2](_v_images/20181125021843504_17730.png)
![异或3](_v_images/20181125021854448_14854.png)
![异或4](_v_images/20181125021917117_7519.png)
![异或5](_v_images/20181125021939528_12633.png)
![异或6](_v_images/20181125022000822_29327.png)
### 2.34 假设在算法描述语言中引入指针的二元运算“异或”用“⊕”表示若a和b为指针则a⊕b的运算结果仍为原指针类型
##### a⊕(a⊕b)=(a⊕a)⊕b=b
##### (a⊕b)⊕b=a⊕(b⊕b)=a
### 则可利用一个指针域来实现双向链表L。链表L中的每个结点只含两个域data域和LRPtr域其中LRPtr域存放该结点的左邻与右邻结点指针不存在时为NULL的异或。若设指针L.Left指向链表中的最左结点L.Right指向链表中的最右结点则可实现从左向右或从右向左遍历此双向链表的操作。试写一算法按任一方向依次输出链表中各元素的值。
### 2.35 采用2.34题所述的存储结构写出在第i个结点之前插入一个结点的算法。
### 2.36 采用2.34题所述的存储结构写出删除第i个结点的算法。
----------
### 2.37 设以带头结点的双向循环链表表示的线性表L=(a1, a2, ..., an)试写一时间复杂度为O(n)的算法将L改造为L=(a1, a3, ..., an, ..., a4, a2)。
----------
### 2.38 设有一个双向循环链表每个结点中除有predata和next三个域外还增设了一个访问频度域freq。在链表被起用之前频度域freq的值均初始化为零而每当对链表进行一次LOCATE(L, x)的操作后被访问的结点即元素值等于x的结点中的频度域freq的值便增1同时调整链表中结点之间的次序使其按访问频度非递增的次序顺序排列以便始终保持被频繁访问的结点总是靠近表头结点试编写符合上述要求的LOCATE操作的算法。
----------
##### 在2.39至2.40题中稀疏多项式采用的顺序存储结构SqPoly定义为
```c
typedef struct
{
int coef;
int exp;
} PolyTerm;
typedef struct
{ //多项式的顺序存储结构
PolyTerm *data;
int last;
} SqPoly;
```
### 2.39 已知稀疏多项式:![2.39](_v_images/20181125022414116_20977.png)其中n=em>em-1>…>e1≥0ci≠0(i=1,2,...,m),m≥1。试采用存储量同多项式项数m成正比的顺序存储结构编写求Pn(x0)的算法(x0为给定值),并分析你的算法的时间复杂度。
> 时间复杂度为O(n),只与顺序表长度有关。
### 2.40 采用2.39题给定的条件和存储结构,编写求![2.40](_v_images/20181125022508237_15214.png)的算法,将结果多项式存放在新辟的空间中,并分析你的算法的时间复杂度。
----------
##### 在2.41至2.42题中稀疏多项式采用的循环链表存储结构LinkedPoly定义为
```c
typedef struct PolyNode
{
PolyTerm data;
struct PolyNode *next;
} PolyNode, *PolyLink;
typedef PolyLink LinkedPoly;
```
### 2.41 试以循环链表作稀疏多项式的存储结构,编写求其导函数的算法,要求利用原多项式中的结点空间存放其导函数(多项式),同时释放所有无用(被删)结点。
### 2.42 试编写算法,将一个用循环链表表示的稀疏多项式分解成两个多项式,使这两个多项式中各自仅含奇次项或偶次项,并要求利用原链表中的结点空间构成这两个链表。
----------