前言
本章主要详解了栈和队列以及特殊矩阵的压缩知识,其中包括栈的顺序存储和链式存储,队列的顺序存储以及链式存储,循环队列的顺序实现以及优化改进-双端队列,最后介绍了三种特殊矩阵的存储结构压缩算法。
总览:

①栈的逻辑结构
栈的概念:
栈(stack)只允许在一端进行插入或者删除的线性表。
栈的基本操作:
Initstack(&S):初始化一个空栈S。
StackEmpty(S):判断一个栈是否为空,若栈为空则返回true,否则返回false。
Push(&s,x):进栈,若栈S未满,则将x加入使之成为新栈顶。
Pop(&s,&x):出栈,若栈非空,则弹出栈顶元素,并用×返回。
GetTop(s,&x):读栈顶元素,若栈非空则用x返回栈顶元素。
ClearStack(&S):销毁栈,并释放S占用的内存空间。
②栈的顺序存储结构
顺序栈的结构:

#define maxsize 10
typedef struct {
ElemType data[maxsize];//静态数组存放栈中元素
int top;//栈顶指针
}Sqstack;
顺序栈的基本操作:
栈的初始化
void InitStack(Sqstack &S){
S.top == -1;//s.top=0位置在初始化时并没有值
}
void test_stack(){
Sqstack s;
InitStack(s);
}
判断栈空
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 GetTop(Sqstack S,ElemType &x){
if(S.Top==-1){
return false;
}
else{
x=S.data[S.top];
return true;
}
}
栈的非连续输入和输出问题:
进栈序列:1,2,3…,n

(*)出栈序列中每一个元素后面所有比他小的元素组成一个递减序列。
合法出栈序列的个数:

合法出栈序列个数的公式如下:
f
(
n
)
=
C
2
n
n
n
+
1
\mathbf{f}\left( \mathbf{n} \right) =\frac{\mathbf{C}_{2\mathbf{n}}^{\mathbf{n}}}{\mathbf{n}+1}
f(n)=n+1C2nn
共享栈

共享栈的概念
将两个栈底设置在共享空间的两端,栈顶向空间中间延伸。(两个栈共享同一片空间。)
判空:
0号栈top == -1
1号栈top == MaxSize
栈满:
top1-top0 == 1
优点:
存取时间复杂度仍为O(1),但空间利用更加有效。
③栈的链式存储结构

typedef struct linknode{
Elemtype data;
struct linkinode *next;
}*linstack;
//所有的操作都在表头进行;
链栈:所有的操作都在表头进行。
④队列的逻辑结构

队列的概念:
队列( Queue):只允许在表的一端进行插入,表的另一端进行删除操作的线性表。
队列的基本操作:
InitQueue(&Q):初始化队列,构造一个空队列Q。
QueueEmpty(Q):判队列空,若队列Q为空返回true,否则返回 False。
EnQueue(&Q, x):入队,若队列Q末满,则将x加入使之成为新的队尾。
DeQueue(&Q, &x):出队,若队列Q非空,则删除队头元素,并用x返回。
GetHead(&x):读队头元素,若队列Q非空则用回队头元素。
ClearQueue(&Q):锴毁队列,并释放队列Q占用的内存空间。
⑤队列的顺序存储

顺序队列的概念:
采用顺序存储结构的队列。
#define Maxsize 50
typedef struct{
ElemType data[Maxsize];
int rear,front;
}SqQueue;
注意:
front指向队头元素,rear指向队尾元素的下一位元素(或 front指向队头元素的前一位置,rear指向队尾元素)。
初始时,front == rear == 0。
对空的条件:Q.rear == Q.front。
队长的计算:Q.rear-Q.front。
队满的条件:Q.rear-Q.front+1==Q.Maxsize。//该条件可以判断队满,但不可以提高队列的空间利用率!
注意:顺序队列中的rear和front下标只可以往后走,不可以回头,所以当rear达到栈尾的Maxsize时,只要有元素出栈(front++),实际的队列存储空间就在减少。
链队的实现:
链队的结构体:
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
链队的初始化操作:
//初始化队列(带头结点)
void InitQueue(LinkQueue &Q){
//初始时front, rear都指向头结点
Q.front=Q.rear=(LinkNode*)malloc(sizeof (LinkNode));
Q.front->next=NULL;
}
void testLinkQueue(){
LinkQueue Q;//声明一个队列
InitQueue(Q); //初始化队列
}
链队的入队操作:
//新元素入队(带头结点)
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));
s->data=x;
s->next=NULL;
Q.rear-next=s; //新结点插入到rear之后
Q.rear=s;//修改表尾指针
}
⑥循环队列的存储结构
循环队列的概念:

把存储队列的顺序队列在逻辑上视为一个环。
front指针移动:Q.front =(Q.front+1)% MaxSize
rear指针移动:Q.rear =(Q.rear+1)% MaxSize
队列长度:(Q.rear+MaxSize-Q.front)% MaxSize
循环队列的基本操作:
循环队列的初始化:
void InitQueue(SqQueue &Q){
Q.rear=Q.front=0;
}
循环队列的入队:
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 0;
}
循环队列的出队:
bool Dequeue(SqQueue &Q,Elemtype &x){
if(Q.rear==Q.front){
return false;
}
x=Q.data[Q.front];
Q.front=( Q.front+1)%MaxSize;//指针后移
return true;
}
循环队列的队头查找:
bool Dequeue(SqQueue &Q,Elemtype &x){
if(Q.rear==Q.front){
return false;
}
x=Q.data[Q.front];
return true;
}
解决循环队列的队满和队空条件相同问题:
队空的条件:Q.rear==Q.front
队满的条件:Q.rear==Q.front
方法一:牺牲一个存储单元

队空条件:Q.front== Q.rear
队满条件:Q.front==(Q.rear + 1)%MaxSize
方法二:增加一个变量代表元素的个数
初始化:Q.size = 0//用于元素的计数
队空条件:Q.size==0
队满条件:Q.size==MaxSize
方法三:增加tag标识
每次删除操作成功时,都令tag=0;每次插入操作成功时,都令tag=1。
初始化:tag=0
队空条件:Q.front=Q.rear&& tag==0
队满条件:Q.front=Q.rear&& tag==1
⑦双端队列的存储结构
双端队列的概念:

双端队列允许两端都可以进行入队以及出队操作的队列。
⑧栈和队列的应用
括号匹配:

算法思想:
1)初始一个空栈,顺序读入括号。
2)若是右括号,则与栈顶元素进行匹配:
若匹配,则弹出栈顶元素并进行下一个元素;
若不匹配,则该序列不合法。
3)若是左括号,则压入栈中。
4)若全部元素遍历完毕,栈中非空则序列不合法。
括号匹配的算法实现:
#define MaxSize 10//定义栈中元素的最大个数
typedef struct{
char data[Maxsize];//静态数组存放栈中元素
int top;//栈顶指针
}SqStack;
bool bracketCheck(char str1, int length) {
Sqtack S;
InitStack(S); //初始化一个栈
for (int i=0; iclength; i++){
if (str[i]=s'('||str[i]='['||str[i]=='{'){
Push(S, str([i]);} //扫描到左括号,入栈}
else{
if (StackEmpty(S)) //扫描到右括号,且当前栈空
return false; //匹配失败
char topElem;
Pop(S, topElem); //栈顶元素出栈
if(str[i]==')' && topElem!='(')
return false;
if(str[i]==']' && topElem!='[')
return false;
if(str[i]=='}'&& topElem!='{')
return false;
}
}
表达式求值:

中缀运算符转后缀的算法思想:
若为数字:
直接加入后缀表达式。
若为运算符:
a.若为’(’,入栈。
b.若为)’,则依次把栈中的运算符加入后缀表达式,直到出现(,并从栈中删除(’。
c.若为’+’,’-’,’/’,’*’,
*栈空,入栈;
*栈顶元素为’(,入栈;
*高于栈顶元素优先级,入栈;
*否则,依次弹出栈顶运算符,直到优先级比它低的运算符或’)'为止。
d.遍历完成,若栈非空则依次弹出所有元素。
⑨特殊矩阵的压缩存储
特殊矩阵和压缩存储的概念:
压缩存储:指多个值相同的元素只分配一个存储空间,对零元素不分配存储空间。
特殊矩阵:指具有许多相同矩阵元素或零元素,并且这些相同矩阵元素或零元素的分布有一定规律性的矩阵。
特殊矩阵的压缩存储:找岀特殊矩阵中值相同的矩阵元素的分布规律,把那些呈现规律性分布、值相同的多个矩阵元素压缩存储到一个存储空间上。
对称矩阵的压缩计算:

对称矩阵:若对一个n阶方阵A中的任意元素ai,j有ai,j=aj,i(1≤i,j≤n),则称其为对称矩阵。
按行优先:

由数组下标:k=1+2+…+(-1)+j-1,得如下坐标计算公式:
数组下标
k
=
{
i
(
i
−
1
)
/
2
+
j
−
1
,
i
⩾
j
j
(
j
−
1
)
/
2
+
i
−
1
,
i
<
j
\text{数组下标}\mathbf{k}=\begin{cases} \mathbf{i}\left( \mathbf{i}-1 \right) /2+\mathbf{j}-1,\mathbf{i}\geqslant \mathbf{j}\\ \mathbf{j}\left( \mathbf{j}-1 \right) /2+\mathbf{i}-1,\mathbf{i}<\mathbf{j}\\ \end{cases}
数组下标k={i(i−1)/2+j−1,i⩾jj(j−1)/2+i−1,i<j
三角矩阵的压缩计算:

三角矩阵:
若对一个n阶方阵Anxn中上(下)对角区元素均为同一常量,则称为下(上)三角矩阵。
下三角矩阵的下标计算公式:
数组下标
k
=
{
i
(
i
−
1
)
/
2
+
j
−
1
,
i
⩾
j
n
(
n
+
1
)
/
2
,
i
<
j
\text{数组下标}\mathbf{k}=\begin{cases} \mathbf{i}\left( \mathbf{i}-1 \right) /2+\mathbf{j}-1,\mathbf{i}\geqslant \mathbf{j}\\ \mathbf{n}\left( \mathbf{n}+1 \right) /2,\mathbf{i}<\mathbf{j}\\ \end{cases}
数组下标k={i(i−1)/2+j−1,i⩾jn(n+1)/2,i<j
上三角矩阵的计算公式,同理。
三对角矩阵的压缩计算:

三对角矩阵:若对个n阶方阵A中的任意元ai,j,当|i-j|>1有ai,j=0(1≤i,j≤n),则称为三对角矩阵。
数组下标 k = 3 ∗ ( i − 1 ) − 1 + j − i + 1 + 1 − 1 = 2 i + j − 3 \text{数组下标}\mathbf{k}=3*\left( \mathbf{i}-1 \right) -1+\mathbf{j}-\mathbf{i}+1+1-1=2\mathbf{i}+\mathbf{j}-3 数组下标k=3∗(i−1)−1+j−i+1+1−1=2i+j−3
1871

被折叠的 条评论
为什么被折叠?



