栈和队列是限定插入和删除只能在表的“端点” 进行的线性表

  栈和队列是线性表的子集(是插入和删除位置受限的线性表)

数据结构 - 栈和队列1_数据结构


一。栈(stack)是一个特殊的线性表,是限定仅在一端进行插入和删除操作的线性表

                         又称为后进先出(Last In First Out)的线性表,简称LIFO结构。


1.栈的相关概念

栈是仅在表尾进行插入、删除操作的线性表。

表尾(即an端)称为栈顶Top;

表头(即a端)称为栈底Base。


插入元素到栈顶(即表尾)的操作,称为入栈。

从栈顶(即表尾)删除最后一个元素的操作,称为出栈。


[思考]假设有3个元素a, b, C,入栈顺序是a,b,c

则它们的出栈顺序有几种可能?

​cba abc acb bac bca 


二。队列(queue)只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表

​是一种先进先出(Frist In Frist Out ---FIFO)的线性表。


1.逻辑结构

与同线性表相同,仍为一对一关系。

2.存储结构

顺序队或链队,以循环顺序队列更常见。

3.运算规则

只能在队首和队尾运算,且访问结点时依照先进先出(FIFO) 的原则。

4.实现方式

关键是掌握入队和出队操作,具体实现依顺序队或链队的不同而不同。



【案例3】为了实现表达式求值。需要设置两个栈:

一个是算符栈OPTR,用于寄存运算符。

另一个称为操作数栈QPND,用于寄存运算数和运算结果。

求值的处理过程是自左至右扫描表达式的每一个字符

●当扫描到的是运算数,则将其压入栈OPND,

●当扫描到的是运算符时

若这个运算符比OPTR栈顶运算符的优先级高,则入栈OPTR,继续向后处理

若这个运算符比OPTR栈顶运算符优先级低,则从OPND栈中弹出两个运算数,从栈

OPTR中弹出栈顶运算符进行运算,并将运算结果压入栈OPND。

●继续处理当前字符,直到遇到结束符为止。


【案例4】舞伴问题

假设在舞会上,男士和女士各自排成一队。舞会开始时,依次从男队和女队的队头各出一-人配成舞伴。 如果两队初始人数不相同,则较长的那一-队中未配对者等待下一轮舞曲。现要求写一_算法模拟 上述舞伴配对问题。



三。 栈的抽象数据类型的类型定义

​ADT[Stack ){

数据对象:

D={ai | ai∈ElemSet, i=1,2...n. n≥0 }

数据关系:

R1={ <ai-1,ai>| ai-1, ai∈D, i=2...n}

约定an端为栈顶,a1 端为栈底。

基本操作:初始化、进栈、出栈、取栈顶元素等

} ADT Stack


InitStack(&S)

初始化操作

操作结果:构造一个空栈S。

DestroyStack(&S)

销毁栈操作

初始条件:栈S已存在。

操作结果:栈S被销毁。

StackEmpty(P)判定S是否为空栈

初始条件:栈S已存在。

操作结果:若栈S为空栈,则返回TRUE,

否则FALSE.

StackLength(Sj

求栈的长度

初始条件:栈S已存在。

操作结果:返回S的元素个数,即栈的长度。

​GetTop(S, &e)

取栈顶元素

初始条件:栈S已存在且非空。

操作结果:用e返回S的栈顶元素。

ClearStack(&S) 栈置空操作

初始条件:栈S已存在。

操作结果:将S清为空栈。

Push(&S, e)入栈操作

初始条件:栈S已存在。

操作结果:插入元素e为新的栈顶元素。

Pog(&S, &e)出栈操作

初始条件:栈S已存在且非空。

操作结果:删除S的栈顶元素an,并用e返回

其值。


四。顺序栈的表示和实现

利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。


   ​空栈:base == top是栈空标志

   栈满top-base= =stacksize

​#define MAXSIZE 100

​typedef struct{

SElemType *base; //栈底指针

SElemType *top; //栈顶指针

int stacksize; //栈可用最大容量

}SqStack; .​

​InitStac​初始化

void InitStack(SqStack S)
{
S.base = (struct SElemType*)malloc(MAXSIZE*sizeof(struct SElemType));
if (S.base == NULL)
{
printf("%s\n", strerror(errno));
}
S.top = S.base;
S.stacksize =MAXSIZE;
}


 StackEmpty是否空栈

int StackEmpty(SqStack S)
{
if (S.base == S.top)
{
return 1;
}
else
{
return 0;
}
}


StackLenght栈元素个数

int StackLenght(SqStack S)
{
return S.top - S.base;
}


ClearStack清空栈

void ClearStack(SqStack S)
{
if (S.base)
{
S.top = S.base;
}
}


DestroyStack销毁栈

void DestroyStack(SqStack S)//销毁
{
S.stacksize = 0;
free(S.base);
S.base = NULL;
S.top = NULL;
}



PushStack入栈

void PushStack(SqStack S, struct SElemType e)
{
if (S.top - S.base==S.stacksize)//是否上溢
{
return;
}
else
{
*S.top = e;
S.top++;
}
}


Pop函数 出栈

struct SElemType Pop(SqStack S)
{
if (S.top - S.base == 0)
{
return;
}
else
{
return *--S.top;
}
}


五。链栈的表示和实现


InitStack初始化

void InitStack(LinkStack S)
{
S = NULL;
}


StackEmpty

int StackEmpty(LinkStack S)
{
if (!S)
return 1;
else
return 0;
}


 InsertStack

void InsertStack(LinkStack S, struct SElemType e)
{
StackNode * p = (StackNode*)malloc(sizeof(StackNode));
p->data = e;
p->next = S;
S = p;
}


PopStack出栈  - 出最后一个元素

void PopStack(LinkStack S, struct SElemType e)
{
if (S == NULL)return;
e = S->data;
StackNode * p = S;
S = S->next;
free(p);
}


GetTop获取栈顶元素

struct SElemType GetTop(LinkStack S)
{
if (S!=NULL)
return S->data;
}


六。栈与递归

1.递归的定义

■若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对

象是递归的;

​​■若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。

2 递归的必备的三个条件

1、能将一一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的

2、可以通过上述转化而使问题简化>

3、必须有一个明确的递归出口,或称递归的边界


数据结构 - 栈和队列1_数据结构_02


“递归工作栈” --- 程序运行期间使用的数据存储区


3.递归的优缺点

优点:结构清晰,程序易读

缺点:每次调用要生成工作记录,保存状态信息,入栈;返回时要出栈,

恢复状态信息。时间开销大。


注意:单向递归和尾递归都可以转换为循环结构解决






0