栈和队列是操作受限的线性表,因此不是任何对线性表的操作都可以作为栈和队列的操作。比如,不可以随便读取栈或队列中间的某个数据。
栈
我们每一次接触到一个新的数据结构都应该分别从其逻辑结构、存储结构和数据的运算三个方面进行分析。
栈的逻辑结构
后进先出(LIFO)的受限线性表。
栈的数学性质
n个不同元素入栈,出栈元素不同排列的个数为卡特兰数
C
2
n
n
/
(
n
+
1
)
C^n_{2n}/(n+1)
C2nn/(n+1)
栈的存储结构
顺序栈
顺序栈只能发生上溢,因为栈的插入和删除操作都是在栈顶进行的。
实现
利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针指示当前栈顶元素的位置。
基本运算
共享栈
利用栈底位置相对不变的特性,可让两个顺序栈共享一个一维数组空间,讲两个栈的占地分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
链栈
通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。
队列
队列的逻辑结构
先进先出(FIFO)的受限线性表,只允许在队列的一段插入,而再另一端删除。向队列插入元素我们称之为入队,删除队列中的元素我们称之为出队。
队列的存储结构
顺序存储
顺序队列
顺序队列的实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针front和队尾指针rear。
逻辑结构不是循环队列的顺序队列会出现“假溢出”的问题。
循环队列
将顺序队列假象成为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,又称循环队列。
当队首指针 Q.front = MaxSize - 1 后,再前进一个位置就自动到 0 ,这可以用 % 运算来实现。
- 初始时: Q.front = Q.rear = 0
- 队头指针进1:Q.front = (Q.front + 1) % MaxSize
- 队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize
- 队列长度:(Q.rear + MaxSize - Q.front) % MaxSize
至于队空队满的判断方法,需要根据具体问题中队front和rear的定义,自行判断。
链式存储
队列的链式存储表示称之为链队列,它实际上是一个带有队头指针和队尾指针的单链表。
头指针指向对头结点,尾指针指向队尾节点,即单链表的最后一个节点(注意这里与顺序存储可能有所不同,顺序存储有时候队尾指针指向的是队尾节点的下一个节点。)
不带头节点的链式队列在操作上,队列为空时,插入删除操作都需要特殊处理,因此通常将链式队列设计成一个带头节点的单链表,从而统一插入删除操作。
双端队列
双端队列是指两端都可以进行入队和出队操作的队列。其元素的逻辑结构仍然是线性结构。将队列的两端分别成为前端和后端,两端都可以出队和入队。
通常分为两种受限的双端队列:
- 输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列。
- 输入受限的双端队列:允许在一端进行插入和删除,但在另一端指允许删除的双端队列。
判断双端队列输出序列的问题是比较难的问题。
数组和特殊矩阵
数组的逻辑结构
数组是由n个相同类型的数据元素构成的有限序列,每个数据元素称为一个数组元素,每个元素在n个线性关系的序号被成为该元素的下标,下标的取值范围称为数组的维界。
数组是线性表的推广。一维数组可视为一个线性表,二维数组可视为其元素是定长线性表的线性表,以此类推。
数组的存储结构
一个数组的所有元素在内存中占用一段连续的存储空间(顺序存储)。
对于多维数组,有两种映射方法:按行优先、按列优先。
特殊矩阵的压缩存储
**压缩存储:**指为多个值相同的元素只分配一个存储空间,对零元素不分配存储空间。
**特殊矩阵:**矩阵中相同的矩阵元素或零元素的分布有一定规律性的矩阵。常见的特殊矩阵有对称矩阵、三角矩阵、对角矩阵等。
-
对称矩阵:上三角所有元素和下三角所有对应元素相同。
只存储下三角部分元素。
-
三角矩阵:上(下)三角区的所有元素均为同一常量。
存储下三角和主对角线上所有元素后,紧接着存储对角线上方的常量一次。
-
对角矩阵:又称带状矩阵。常见的有三对角矩阵。
三对角矩阵:所有非零元素都集中在以主对角线为中心的3条对角线的区域,其他区域的元素都为0。
稀疏矩阵
矩阵中的非零元素个数非常少。
因此,可以采用三元组的方式进行存储,然后按照某种规律存储这些三元组(可以采用数组存储,也可以采用十字链表法存储)。
稀疏矩阵压缩存储后便失去了随机存取特性。