栈
栈的定义
定义:栈(Stack)是限定仅在表尾进行插入和删除操作的线性表。
允许插入删除的一端成为 栈顶(top) ,另一端成为 栈底(bottom) ,不含任何数据元素的称为 空栈 。
栈又被称为 LIFO(Last In First Out) 结构。
可以形象得与手枪弹夹做对比。
栈的插入称为进栈(push),即子弹进入弹夹。
栈的删除称为出站(pop),即子弹从弹夹出去。
进出栈的变化形式:不一定是123进321出,还可能1进1出再2进2出再3进3出(123进123出)等多种组合的可能。
顺序栈
使用数组下标为0的一段作为栈底
top: 游标,表征栈顶的位置。存储一个元素时top=0,空栈top=-1
StackSize: 存储栈的长度
top<StackSize
当StackSzie=5时,不同的栈顶位置和top关系如下
顺序栈结构定义
typedef int SElemType
typedef struct
{
SElemType data[MAXSIZE];
int top;/*栈顶游标指针*/
}SqStack;
进栈操作push
Status Push(SqStack *S,SElemType e)
{
if(S->top==MAXSIZE-1)/*代表栈满,无法再插入*/
{
return ERROR;
}
S->top++;
S->data[S->top]=e;
return OK;
}
出栈操作pop
Status Pop(SqStack *S,SElemType *e)
{
if(S->top==-1)
return ERROR;
*e=S->data[S->top];
S->top--;
return OK;
}
两栈共享数组空间
下标0为其中一个栈的栈底,下标为n-1是另一个栈的栈底。
两个栈如果增加元素,游标从两端向中间延伸。
栈满判断条件
top1+1==top2/*满足条件则代表共用数组栈满*/
两栈共享数组空间代码如下
typedef struct
{
SElemType data[MAXSIZE];
int top1;
int top2;
}SqDoubleStack;
链栈
使用头指针作为栈顶指针,一般不需要头结点
基本不存在栈满的情况
空链栈:头指针指向NULL
链栈结构代码
typedef struct StackNode/*链表结点结构*/
{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack/*在链表基础上的链栈结构*/
{
LinkStackPtr top;
int count;
}LinkStack;
链栈的进栈
Status Push(LinkStack *S,SElemType e)
{ /*创建一个节点,并且插入到原来的链表最前面*/
LinkStackPtr s=(LikStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top;/*原栈顶结点变成新结点的直接后继*/
/*刷新链栈的栈顶位置和栈元素个数*/
S->top=S;
S->count++;
return OK;
}
链栈的出栈
Status Pop(LinkStack *S,SElemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))//判断链栈是否为空的函数
return ERROR;
*e=S->top->data;
p=S->top;/*赋值并释放该结点*/
S->top=S->top->next;/*刷新游标指针*/
free(p);
S->count--;
return OK;
}
链栈和顺序栈的选择:顺序栈没有指针域,因此大概元素变化比较大时用链栈,当变化在可控范围内时使用顺序栈。
栈的应用——递归: 递归的过程中有前行和退回阶段(不考虑尾递归),在退回过程中,需要恢复前行过程中的数据(局部变量,参数,返回地址等),就是编译器通过栈实现的,实现了存储并恢复的功能(现场保护)。
栈的应用——计算逆波兰表达式(后缀表达式):计算机可以处理后缀表达式,无论是从中缀表达式转换为后缀表达式还是从后缀表达式运算出结果都需要栈。详见《大话数据结构》p108
队列
队列的定义
引入:电脑卡机时的把之前点击的过程顺序执行一遍就是多个程序先后依次排队等待造成的。
定义:队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先入先出(FIFO)的线性表。允许插入的一端称为队尾,允许删除的一端称为队头。
队头删除,队尾插入,如下图所示。
循环队列(顺序结构)
顺序结构的队列:入队列操作简单,而出队列操作复杂(需要将后续元素全部向前移位)
front:队头位置
rear:队尾下一个位置
空队列情况
f
r
o
n
t
=
=
r
e
a
r
front==rear
front==rear
满队列情况:规定留有一个空间不可以使用,满队列情况有两种如上图,rear在front的左边或右边,但都满足
(
r
e
a
r
+
1
−
f
r
o
n
t
)
%
Q
u
e
u
e
S
i
z
e
=
=
0
(rear+1-front)\%QueueSize==0
(rear+1−front)%QueueSize==0
队列长度计算公式
(
r
e
a
r
−
f
r
o
n
t
+
Q
u
e
u
e
S
i
z
e
)
%
Q
u
e
u
e
S
i
z
e
(rear-front+QueueSize)\%QueueSize
(rear−front+QueueSize)%QueueSize
//插入操作核心部分
//先判断是否为满队列
Q->rear=(Q->rear+1)%QueueSize;//插入时是尾部插入,右移尾部指标rear
//删除操作核心部分
//先判断是否为空队列
Q->front=(Q->front+1)%QueueSize;//删除时头部删除,右移头部指标front
队列的链式结构(链队列)
链队列:尾进头出的单链表
队头指针指向头结点,队尾指针指向终端节点,如下图
空队列的情况
链队列的结构
typedef int QElemType
typedef struct QNode/*节点结构*/
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front,rear;/*队头队尾指针*/
}LinkQueue;