上一章中比较重要的算法:链表的头插法、尾插法、逆置法、归并法、双指针法;因为顺序表可以直接存取,所以经常结合排序和查找的集中算法设计思路进行设计,e.g.归并排序、二分查找等。等做了应用题再来回顾这些知识点,先不用记忆,在这里放个坑
纸质考试中的算法设计题,先写出数据结构类型的定义、并写出正确的算法思想为主,比较复杂的地方可以直接用文字表达。什么情况算复杂有待多练习把控
8.3 | 63-72页:栈、顺序栈(基本操作*5)、链栈、共享栈、卡特兰数公式 |
---|---|
8.4 | 73-79 |
8.6 | 80-92页:队列的顺序 链式存储和基本操作,循环队列,双端队列,受输入限制的双端队列【待上传错题】 |
8.7 | 93-112:栈和队列的应用:有效括号、中缀表达式与后缀表达式及转化算法、后缀表达式的计算、递归的特点和优缺点、栈在函数调用中的作用和工作原理、队列与层次问题、队列实现二叉树的层次遍历、队列解决计算机系统的两个问题;数组的逻辑结构、特殊矩阵的压缩(下标对应关系)、数组的存储结构、稀疏矩阵的存储方式和存储结构 |
第三章 栈、队列和数组
本章重点:栈:出入栈的过程、出栈顺序的合法性;队列的操作及其特征。二者均是线性表的应用和推广,较容易出现在算法设计题中。
除此之外,栈和队列的顺序存储、链式存储及其特点、双端队列的特点、栈和队列的常见应用,以及数组和特殊矩阵的压缩存储都是必须掌握的。
数组 901中常考点(貌似)
注意:
- 每接触一种数据结构,都应从其逻辑结构、存储结构和运算三个方面着手。
- 在解答算法题时,如果题干未做出限制,可以直接使用各个数据类型的基本操作函数。所以这些也是需要记忆的。
【上一章也没整理,最后一起整理吧】- 本章要记忆的文字性内容较多,认真过问答,有一部分是应用题,之后可结合课后练习记忆
文字内容
问
- 什么是顺序栈 它和栈的关系?
- 顺序栈在入栈操作上什么限制,应该怎么处理?
- 描述一下共享栈
- 共享栈如何判断栈满或栈空
- 共享栈如何出入栈
- 共享栈的优点/目的
- 描述链栈及其优点
- 为什么会出现假溢出?怎么解决?
- 判断循环队列队空或队满的条件?
- 不带头结点的链队列是如何出入队的,如何判断队空
- 带头结点的链队列是如何出入队的,如何判断队空
- 双端链表中的出入队顺序该如何判断?
- 如何由入队序列a b c d得到出队序列d c a b
- 什么是输出受限的双端队列?
- 用栈解决下列问题,文字说明思路(leetcode 20)【可能应用】
- 算术表达式中的中缀表达式和后缀表达式分别有什么特点?为什么要将中缀转为后缀?
- 中缀表达式转后缀表达式的算法思路【应用】
- 后缀表达式求值的算法思路【应用】
- 什么是递归
- 递归的特点和优缺点是什么
- 栈在函数中的作用和工作原理
- 层次问题的解决方法及其使用的数据结构
- 使用队列完成层次遍历二叉树的算法描述【应用】
- 描述队列是如何解决主机与外部设备之间速度不匹配的问题
- 描述队列是如何解决由多用户引起的资源竞争问题
- 数组与线性表的关系
- 多维数组的按行优先存储的基本思想/思路,及其存储结构关系式
- 多维数组的按列优先存储的基本思想/思路,及其存储结构关系式
- 压缩存储以及特殊矩阵的定义,举例常见的特殊矩阵
- 特殊矩阵的压缩方法是什么
- 对称矩阵的定义及其压缩方式
- 三角矩阵的定义及其压缩方式,与对称矩阵有什么不同之处
- 三对角矩阵的定义及其压缩方式
- 稀疏矩阵的定义及存储(存储信息,存储结构)
答
- 采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针top指示当前栈顶元素的位置。类似于 顺序表之于线性表,是栈的顺序存储结构。
- 顺序栈在入栈操作上受数组上界的约束,当对栈的最大使用空间估计不足时,有可能发生栈上溢,此时应该及时向用户报告消息,以便及时处理,避免出错。
- 共享栈具体是利用栈底位置相对不变的特性,可让两个顺序栈共享一个一位数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
可将两个栈 top top相对,左侧为0号栈栈底,右侧为1号栈栈底两个栈的栈顶指针都指向栈顶元素,当top0 == -1 时0号栈为空栈,top1 == MaxSize-1时1号栈为空栈;当top1 -top0 == 1时,判断为栈满。- 0号栈进栈时,top0先加1 再赋值,1号栈进栈时,top1先减1再赋值;出栈刚好相反,0号栈先输出出栈元素再top0–,1号栈先输出出栈元素再top1++;
- 共享栈是为了更有效地利用存储空间,两个栈地空间相互调节,只有在整个存储空间被占满时才会发生上溢。其存取数据的时间复杂度均为O(1),所以对存取效率没有什么影响。
- 采用链式存储的栈称为链栈,其优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。采用链式存储,便于结点的插入与删除。
- 当队列使用顺序存储的时候,队列中仅有一个元素,但是Q.rear == MaxSize,这时入队出现“上溢出”,但是这种溢出不是真正的溢出,在data数组中仍然存在可以存放元素的空位置,所以是一种“假溢出”。
解决这种假溢出,引入循环队列的概念,将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称之为循环队列。
具体是,当队首指针 Q.front == Maxsize-1后,再前进一个位置就自动到0,可以利用%运算实现。 - a. 牺牲一个单元来区分队空和队满,入队时少用一个队列单元。 队满条件----(Q.rear+1)%MaxSize == Q.front;队空条件----Q.front == Q.rear; 队列中元素个数----(Q.rear-Q.front+MaxSize)%MaxSize;
为什么和没设置牺牲单元时队列长度一致???只是少用,并没有改变之前的入队规律和出队规律,所以不影响计算长度的公式,只是会影响结果的最大值,从MaxSize变为了MaxSize-1。
b. 类型中增设size数据成员,表示元素个数。 删除成功size–;插入成功size++;队空Q.size == 0;队满Q.size == MaxSize,两种情况都有Q.front == Q.rear;
c. 类型中增设tag数据成员,以区分是队满还是队空。 删除成功设置tag =0若导致Q.front == Q.rear,则为队空;插入成功 tag = 1,若导致Q.front == Q.rear则为队满。
- 入队时,建立一个新结点,将新结点插入到链表的尾部,并让Q.rear指向这个新插入的结点(若原队列为空队,则令Q.front也指向该结点)。出队时,首先判断队是否为空,若不空,则取出队头元素,将其从链表中摘除,并让Q.front指向下一个结点(若该结点为最后一个结点,则置Q.front 和Q.rear都为NULL)。Q.front == NULL;
- 通常将链式队列设计为一个带头结点的单链表,这样插入和删除操作就统一了。初始化时,让Q.front = Q.rear = head;入队时,建立一个新结点,将新结点插入到链表的尾部,并Q.rear指向下一个/新结点;出队时,首先判断队是否为空,若不空,则删除Q.front指向的下一个结点,Q.front不变。Q.front->next == NULL;or Q.front == Q.rear;
- 在双端队列进队时,前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面;在出队时,无论是前端还是后端出队,先出的元素排列在后出的元素的前面。
- 由队列的FIFO可推断,根据出队顺序可以看出入队顺序。使用双端队列,则先是ab依次从右端入队,然后cd从左端入队,就能获得这个顺序了。
- 允许在一段进行插入和删除,但在另一端只允许删除的双端队列称为输入首先的双端队列。
- 有效的括号:初始设置一个空栈,顺序读入括号;
如果是左括号,则压入栈中;
若是右括号,判断栈顶元素是否与其匹配,若匹配则将栈顶元素出栈,若不匹配,退出程序或返回false;
算法结束时,栈为空,则括号序列合法,返回true,否则括号序列不匹配,返回false。 - 中缀表达式的特点是运算符位于操作数之间,符合人类阅读和书写习惯,因此容易理解,但需要使用括号来明确运算顺序,容易产生歧义,还需要考虑运算符的优先级和结合性规则;
后缀表达式的特点是运算符位于操作数之后,不需要括号,只有操作数和运算符,因为运算顺序由表达式本身决定,这样消除了运算优先级问题,避免了歧义;
后缀表达式适合用栈进行求值,简化了计算过程,且更符合计算机的处理方式。因此,将中缀表达式转换为后缀表达式有助于消除歧义,简化计算,并且便于计算机处理。 - 转化过程中使用栈来保存暂时还不能确定运算顺序的运算符。从左到右扫描中缀表达式的每一项,具体过程如下:
i. 初始化一个空栈
ii.遇到操作数,直接加入后缀表达式中
iii.遇到界限符,若为左括号,直接入栈;若为右括号,依次弹出栈中的运算符,并加入到后缀表达式中,直到弹出左括号为止,删除弹出的左右括号。
iv.遇到运算符,若优先级高于除左括号以外的栈顶运算符,则直接入栈。否则从栈顶开始,依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,直到遇到一个优先级低于它的运算符 或 栈为空 或 遇到左括号为止,之后将当前运算符入栈。
v.扫描结束后,将栈中所有元素弹出并加入后缀表达式。
在应用题中,模拟上述过程时应该分别列出步骤数、待处理序列、栈内、后缀表达式、扫描项、说明,详情见电子版108页,王道94页表3.1。 - 从左到右依次扫描表达式的每一项,若是该项是操作数,将其压入栈中;
若是操作符<op>,则从栈中退出两个操作数Y和X,形成运算置零X<op>Y,并将计算结果压入栈中;
当所有项都扫描并处理完后,栈顶存放的就是最后的计算结果。
应用题中应列除 步骤数、 扫描项、项类型、动作、栈中。电子版109 王道95 表3.2 - 递归是一种重要的程序设计方法。简单地说,若在一个函数、过程或数据结构的定义中又应用了它自身,则这个函数、过程或数据结构称为是递归定义的,简称递归。
- 它通常把一个大型的复杂问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的代码就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。但在通常情况下他的效率并不是太高,但优点是代码简单,容易理解。
- 在递归调用的过程中,系统为每一层的返回点、局部变量、传入实参等开辟了递归工作栈来进行数据存储(栈的作用+工作原理),递归次数过多容易造成栈溢出等。而其效率不高的原因是递归调用过程中包含很多重复的计算。(效率问题)
- 在信息处理中有一大类问题需要逐层或逐行处理。这类问题的解决方法往往是在处理当前层或行时就对下一层或下一行做预处理,把处理顺序安排好,等到当前层或当前行处理完毕,就可以处理下一层或者下一行。使用队列是为了保存下一步的处理顺序。
- 初始化一个队列;①根结点入队;②若队空(所有结点都已处理完毕),则结束遍历,否则重复下一步操作;③队列中第一个结点出队,并访问之,若其有左孩子,左孩子入队,若其有右孩子,右孩子入队,返回②。应用题中,序号、说明、队内、队外电子110 王道97 表3.3
- 【问题描述】对于主机和外部设备之间速度不匹配问题,仅以主机和打印机之间速度不匹配的问题为例做简要说明。主机输出数据给打印机打印,输出数据的速度比打印数据的速度要快得多,因为速度不匹配,若直接把输出的数据送给打印机打印,显示不行的。
【解决方案】解决方法是设置一个打印数据缓冲区,主机把要打印输出的数据依次刺蛾如这个缓冲区,写满后就暂停输出,转去做其他事情。打印机就从缓冲区中按照FIFO的原则依次取出数据并打印,打印完后再向主机发出请求。主机接到请求后再向缓冲区写入打印数据。这样做即保证了打印数据的正确,又使主机提高了效率。由此可见,打印数据缓冲区中所储存的数据就是一个队列。 - 【问题描述】对于多用户引起的资源竞争问题,CPU资源的竞争就是一个典型的例子。在一个带有多终端的计算机系统上,有多个拥护需要CPU各自运行自己的程序,他们分别通过各自的终端向操作系统提出占用CPU的请求。
【解决方案】操作系统通常按照每个请求在时间上的先后顺序,把它们排成一个队列,每次把CPU分配给队首请求的用户使用。当相应的程序运行结束或用完规定的时间间隔后,令其出队,再把CPU分配给新的队首请求的用户使用。这样既能满足每个用户的请求,又使CPU能够正常运行。 - 数组是线性表的推广。一维数组可视为一个线性表;二维数组可视为其元素是定长数组的线性表,以此类推。数组一旦被定义,其维数和维界就不再改变。因此,除结构的初始化和销毁外,数组只会有存取元素和修改元素的操作。
- 以二维数组为例,按行优先存储的基本思想是:先行后列,先存储行号较小的元素,行号相等先存储列号较小的元素。设二维数组的行下标与列下标的范围分别为
[
0
,
h
1
]
和
[
0
,
h
2
]
[0,h_1]和[0,h_2]
[0,h1]和[0,h2],则存储关系式是
L
O
C
(
a
i
,
j
)
=
L
O
C
(
a
0
,
0
)
+
[
i
×
(
h
2
+
1
)
+
j
]
×
L
LOC(a_{i,j}) = LOC(a_{0,0})+[i×(h_2+1)+j]×L
LOC(ai,j)=LOC(a0,0)+[i×(h2+1)+j]×L
(可以记忆为以 a 0 , 0 a_{0,0} a0,0为原点, a i , j a_{i,j} ai,j到原点按照按行优先的规则存了多少个数组元素,然后✖L就行), - 以二维数组为例,按列优先存储的基本思想是:先列后行,先存储列号较小的元素,列号相等先存储行号较小的元素。设二维数组的行下标与列下标的范围分别为 [ 0 , h 1 ] 和 [ 0 , h 2 ] [0,h_1]和[0,h_2] [0,h1]和[0,h2],则存储关系式是 L O C ( a i , j ) = L O C ( a 0 , 0 ) + [ j × ( h 1 + 1 ) + i ] × L LOC(a_{i,j}) = LOC(a_{0,0})+[j×(h_1+1)+i]×L LOC(ai,j)=LOC(a0,0)+[j×(h1+1)+i]×L
- 压缩存储指为多个值相同的元素只分配一个存储空间,对零元素不分配空间;
特殊矩阵指具有许多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。常见的特殊矩阵有对称矩阵、上\下三角矩阵、对角矩阵等。 - 找出特殊矩阵中值相同的矩阵元素的分布规律,把那些呈现规律性分布的、值相同的多个矩阵元素压缩存储到一个存储空间中。
- 若对一个n阶矩阵A中的任意元素
a
i
,
j
a_{i,j}
ai,j都有
a
i
,
j
=
a
j
,
i
(
1
≤
i
,
j
≤
n
)
a_{i,j} = a_{j,i}(1≤i,j≤n)
ai,j=aj,i(1≤i,j≤n),则称其为对称矩阵。对于n阶对称矩阵,上下三角区元素对应相同,若仍采用二维数组存放,则会浪费几乎一半的空间,为此将n阶对称矩阵A[1…n][1…n]存放在一维数组B[n(n+1)/2]中,即元素
a
i
,
j
a_{i,j}
ai,j存放在
b
k
b_k
bk中。
若存下三角矩阵 k = i ( i − 1 ) / 2 + j − 1 , i ≥ j k = i(i-1)/2+j-1 , i ≥ j k=i(i−1)/2+j−1,i≥j ,存上三角矩阵 k = j ( j − 1 ) / 2 + i − 1 , i < j k = j(j-1)/2+i-1 , i < j k=j(j−1)/2+i−1,i<j。(它在一维数组𝐵中的索引𝑘应该是前面所有行的元素数加上当前行的元素数。)
注意:
- A[1…n][1…n]中下三角区i>j、主对角线i=j、上三角区i<j三者不重叠overlap
- 如果A[0…n][0…n]说明一共n+1行n+1列,注意题干定义
- 下三角矩阵中,上三角区的所有元素都为同一常量。其存储思想与对称矩阵类似,不同之处在于存储完下三角区和主对角线上的元素之后,紧接着存储对角线上方的常量一次,所以可以将n阶下三角矩阵A压缩存储在B[n(n+1)/2 + 1]中。
元素下标对应关系为 k = i ( i − 1 ) / 2 + j , i > = j k = i(i-1)/2 + j , i>=j k=i(i−1)/2+j,i>=j下三角区和主对角线元素, k = n ( n + 1 ) / 2 , i < j k = n(n+1)/2 , i<j k=n(n+1)/2,i<j上三角区元素
上三角矩阵压缩存储的下标对应关系 k = ( 2 n − i + 2 ) ( i − 1 ) / 2 + j − i , i > = j k = (2n-i+2)(i-1)/2 + j-i , i>=j k=(2n−i+2)(i−1)/2+j−i,i>=j上三角区和主对角线元素, k = n ( n + 1 ) / 2 , i > j k = n(n+1)/2 , i>j k=n(n+1)/2,i>j下三角区元素
注意:
- 下标对应关系中的公式是根据等差数列求和公式推的:i-1都是项数,上三角首项n尾项n-i+2(因为是从1到i-1行,如果到i行就是n-i+1),下三角首项1尾项i-1。
- k = 前i-1行的元素数+当前行 a i , j a_{i,j} ai,j之前的元素数 =(首项+尾项)*项数/2 + (j or j-i)
- 对角矩阵也称带状矩阵。对n阶矩阵A中的任意一个元素
a
i
,
j
a_{i,j}
ai,j,当|i-j|>1时,若有
a
i
,
j
=
0
(
1
<
=
i
,
j
<
=
n
)
a_{i,j}=0(1<= i,j <= n)
ai,j=0(1<=i,j<=n)则称为三对角矩阵。在三对角矩阵中,所有非零元素都集中在以主对角线为中心的3条对角线的区域,其他区域的元素都为0。采用压缩矩阵,元素按行优先方式存放在一维数组B中,且
a
1
,
1
a_{1,1}
a1,1存放于B[0]中。
下标对应关系为 k = 2 i + j − 3 k = 2i+j-3 k=2i+j−3,若已知k反推 i , j : i = ⌊ ( k + 1 ) / 3 + 1 ⌋ , j = k − 2 i + 3 i , j : i = \lfloor (k+1)/3 +1 \rfloor , j = k-2i+3 i,j:i=⌊(k+1)/3+1⌋,j=k−2i+3.
不太会推这个公式就记住 :+1+2-3,j i 1 - 矩阵中非零元素的个数t,相对矩阵元素的个数s来说非常少,即s>>t的矩阵称为稀疏矩阵。使用常规方法存储稀疏矩阵,浪费存储空间,因此仅存储非零元素,但通常非零元素的分布没有规律,所以还要存储它所在的行和列。因此,将非零元素及其相应的行和列构成一个三元组(行标 i i i , 列标 j j j , 值 a i , j a_{i,j} ai,j)。稀疏矩阵压缩存储后便失去了随机存取特性。适合的存储结构有数据、十字链表。
挖
- 栈stack是线性表,但限定线性表只能【】;栈顶Top是线性表【】的一端;栈底Bottom是【adj.】且是【】的一端;空栈是【n.】。
- 栈的操作特性可以明显地概括为【】。
- 当有n个不同的元素进栈时,出栈元素不同排列的个数为【】。
- 队列Queue简称队,也是一种【】的线性表,只允许在表的【】插入,被称为【】,而在表的【】删除,被称为【】,空队列是【】。
- 向队列中插入元素称为【】,删除元素称为【】,其操作特性为【】。
- 代码:循环队列的初始【】,队首出队【】,队尾入队【】,队列长度【】。(指针按顺时针进1取模)
- 队列的链式表示称为【】,它实际上是同时拥有【】和【】的单链表。
- 单链表表示的链式队列特别适合于【】的情形,而且不存在【】的问题。
- 另外,加入程序中要使用多个队列,与多个栈的情形一样,最好使用【】,这样就不会出现【】和【】的问题。
- 双端队列是指【】的线性表,为了方便理解,左端也视为【】,右端也视为【】。
- 若双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就蜕变为【】。
- 必须注意递归不能是循环定义的,其必须满足两个条件【】&【】,递归的精髓在于【】。
- 队列在计算机系统中的应用非常广泛,以下仅从两个方面来阐述:第一方面是解决【】问题,第二方面是解决【】问题。
- 数组是由n(【】)个【】的数据元素构成的【】,每个数据元素称为【】,下标是【】、下标的取值范围称为数组的【】。
- 大多数计算机语言都提供了数组数据类型,逻辑意义上的数组可采用计算机语言中的数组数据类型进行存储,一个数组的所有元素在内存中占用一段【adj.】存储空间,以一维数组A[0…n]为例,其存储结构关系式为【】。
- 对于多维数组,有两种映射方法:【】和【】。
空
- 在某一端进行插入和删除操作、允许进行插入删除、固定的、不允许插入删除、空表(不含任何元素的)
- 后进先出 LIFO last in first out
- 1 n + 1 C ( n 2 n ) \frac{1}{n+1}C \binom{n}{2n} n+11C(2nn)
- 操作受限、一端、队头front、另一端、队尾rear、空表
- 入队 or 进队、 出队 or 离队、先进先出 FIFO first in first out
- Q.front = Q.rear = 0; 、Q.front = (Q.front+1)%MaxSize; 、 Q.rear = (Q.rear+1)%MaxSize;、(Q.rear - Q.front+MaxSize)%MaxSize;
- 链队列、队头指针、队尾指针
- 数据元素变动比较大、队列满且产生溢出
- 链式队列、存储分配不合理、溢出
- 允许两端都进行插入和删除操作 、前端、后端
- 两个栈底相接的栈
- 递归表达式(递归体)、边界条件(递归出口)、能否将原始问题转换为属性相同但规模较小的问题
- 主机与外部设备之间速度不匹配、由多用户引起的资源竞争
- n>=1、相同类型、有限序列、一个数组元素、每个元素在n个线性关系中的序号、维界
- 连续的、 L O C ( a i ) = L O C ( a 0 ) + i × L ( 0 ≤ i < n ) LOC(a_{i}) = LOC(a_0)+i×L(0≤i<n) LOC(ai)=LOC(a0)+i×L(0≤i<n), 其中L为每个数组元素所占的存储单元
- 按行优先、按列优先
选择(仅错题)8.8之前全部上传!
比较多的是判断出入栈顺序,应该有技巧的吧。
栈的深度,即栈的元素个数,一般是给出进栈出栈序列求最大深度,就算不给也需要先推出进栈出栈序列,再按照FILO的特点手工模拟判断过程中栈中包含的最多元素数,即为最大深度。有例题!!!
循环队列的题也比较多,其中对某端输入/输出受限的双端队列的出入队顺序合法性考察比较多,技巧就是先通过 只能一端输入/只能一端输出 这个条件将题目中的出/入队顺序写到队列中,然后再逆推是否合法。 20 24
循环队列的基本操作常考!!仔细记忆,尤其是判队空判队满,如果是牺牲一个单元的方式,先确定MaxSize再一步步计算,还有就是出入队的front rear公式。链式队列中出队的操作比较容易错,所以要多记忆。
【错误选项】A
【思路】栈和队列都是线性表的应用,所以应该是逻辑结构 B。
【错误选项】D
【思路】先入栈再变栈顶指针,所以是data[top++] = x选B;
【错误选项】?
【思路】语言标识符只能以下划线和字母为开头,所以问的是输出中能做变量名的字符串几个。一共有5种除去以数字开头的2种还剩3 ,所以选C。
【补充】语言标识符(Identifier)是编程语言中的一个基本概念,用来命名变量、函数、类型、类、模块等各种程序元素。标识符在代码中起到识别和引用的作用,使得程序能够清晰地表达各种逻辑关系和操作。 就是变量名。
【错误选项】D
【思路】以d开头,说明d进栈后就直接出栈了,且abc进栈后都选择停留,e还没进栈,根据e进栈的时机,可以得知有cbae、ceba、cbea、ecba四种形式。所以选B。
【错误选项】A
【思路】P3在P2后出栈,说明为P2后面的数4-n;元素入栈后可以出栈,P2之前已经出了一个,且P2=3说明1、2之间出去了一个,此时栈中只有1个数。P3为后面的值一共有n-3个数,加上栈中留下的那个数(1或2)一共是n-1个值,选C。
【错误选项】A
【思路】虽然链式队列采用动态分配方式,但其长度也受内存空间的限制,不能无限增长;顺序队列和链式队列的进出队时间复杂度都为O(1);顺序队列和链式队列都可以顺序访问;所以D,顺序队列可以通过队头指针和队尾指针计算队列中的元素个数。
【错误选项】B
【思路】队头在链表尾,队尾就是链表头噻;入队在队尾,需要知道指向链表头的指针也就是尾指针,已知是循环单链表,只设头指针,所以需要遍历整个链表才能得到尾指针,时间复杂度O(n)选A。注意读题!
【错误选项】D
【思路】因为入队是对rear做改变也就是rear = (rear + 1)%MaxSize。这里的maxsize = n;所以要求入队后rear为0,所以原本rear是n-1。由于初始化循环队列,rear == front,所以front = n-1。所以初始时,rear和front都是n-1. 错的!!
【错误原因】对初始化认知片面,不是rear == front,而是rear = front = 0;但这里要求了入队后,存储在下标为0的位置,再加上入队只影响队尾指针,所以rear为n-1,front仍为默认的0.故:B。
【错误选项】C
【补充】图的广度搜索类似于树的层次遍历,都要借助于队列所以B。
【错误选项】C
【思路】B显然错;D中只有队列符合叙述;C通常使用栈来处理函数或过程调用!!所以C错。选A
【补充】使用栈可以模拟递归的过程,以此来消除递归,但对于单项递归和尾递归而言,可以用迭代的方式来消除递归,所以A对;
【错误选项】D
【思路】驶出次序从1-9,所以1要第一个出来,n条轨道中n-1条都是用来存储935248这几个数,来协调1-9的出栈顺序。应该是4条,分别存储的列车号为[89] [45] [23] [167]选C。
【错误选项】A
【思路】根据11题A选项可知Ⅰ错;Ⅱ对;Ⅲ很明显不能确定;Ⅳ错。所以错误的是 C。以为是选正确的来着
【错误选项】B
【思路】对称矩阵,按行,下三角。看下图。
【错误选项】?
【思路】三角矩阵–>上?下?;A从1开始;B从1开始;按行优先;i = 66 > j = 65所以下三角矩阵。B从1开始所以计算的是包含[66][65]在内的元素个数。
【勘误】我就说死活算不出(╯‵□′)╯︵┻━┻,果然是三对角矩阵,也就是带状矩阵。所以前65行一共是65*3-1,再加上1结果为195选B。
代码内容
栈
栈的顺序存储类型:顺序栈
#define MaxSize 50
typedef struct{
Elemtype data[MaxSize];
int top;
}SqStack;
S.top//栈顶指针
S.top = -1 ;//栈空StackEmpty()
S.top = MaxSize-1;//栈满
S.top++;S.data[top] = newElem;//栈不满时,进栈push
topElem = S.data[top];S.top--;//栈非空时,出栈pop
顺序栈的初始化 判栈空 进栈 出栈 读栈顶元素的基本操作实现
修改输入栈 则 &S否则 S
void InitStack(SqStack &S){
S.top = -1;
}
bool StackEmpty(SqStack S){
if(S.top == -1)return true;
else return false;
}
bool Push(SqStack &S , ElemType x){
//栈满
if(S.top == MaxSize-1) return false;
S.data[++S.top] = x;
return true;
}
bool Pop(SqStack &S , ElemType &x){
//栈空
if(S.top == -1) return false;
x = S.data[S.top--];
return true;
}
bool GotTop(SqStack S , ElemType &x){
//栈空
if(S.top == -1) return false;
x = S.data[S.top];
return true;
}
栈的链式存储类型
通常采用单链表实现,并规定所有的操作都是在单链表的表头进行的,一般没有头结点,Lhead指向栈顶元素。
typedef struct Linknode{
ElemType data;
struct Linknode *next;
}LiStack;
队列
队列的顺序存储类型
#define MaxSize 50
typedef struct{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
判断队空/初始时:Q.front = Q.rear = 0;
入队:队尾 , 队不满时, 先送值再队尾+1
出队:对头 , 队不空时, 先取值再对头+1
循环队列的操作
初始化
void InitQueue(SqQueue &Q){
Q.rear = Q.front = 0;
}
判队空
bool isEmpty(SqQueue Q){
if(Q.rear == Q.front) return true;
else return false;
}
入队
bool EnQueue(SqQueue &Q ,ElemType x){
if((Q.rear + 1)%MaxSize == Q.front){//队满,牺牲一个单元
return false;
}
Q.data[Q.rear] = x;
Q.rear = (Q.rear+1)%MaxSize;
return true;
}
出队
bool DeQueue(SqQueue &Q,ElemType &x){
if(Q.rear == Q.front){ //队空
return false;
}
x = Q.data[Q.front];
Q,front = (Q.front+1)%MaxSize;//队头加1取模
return true;
}
队列的链式存储类型
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front , *rear;
}LinkQueue;
不带头结点时,当Q.front == NULL 且Q.rear == NULL时,链式队列为空。
链式队列的基本操作
初始化
void InitQueue(LinkQueue &Q){
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkQueue));
Q.front->next = NULL;
}
判队空
bool isEmpty(LinkQueue Q){
return Q.front->next == NULL;
}
bool isEmpty(LinkQueue Q){
return Q.front == Q.rear;
}
入队在队尾
void EnQueue(LinkQueue &Q,ElemType x){
LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
出队在队头【多练几遍!!!】
bool DeQueue(LinkQueue &Q,ElemType &x){
if(Q.front == NULL) return false;
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if(Q.rear == p){ //若原队列只有一个结点,删除后变空
Q.rear = Q.front;
}
free(p);
return true;
}
栈和队列的应用
递归实现斐波那契数列
int F(int n){
if( n == 0 )return 0;
else if( n == 1)return 1;
else return F(n-1)+F(n-2);
}