233 阅读6分钟

定义

栈是一种特殊的线性表,限定插入和删除操作只能在表的一端(通常是表尾)进行。(又称为后进先出的线性表,简称LIFO结构)。

栈是仅在表尾进行擦汗如,删除操作的线性表。表尾称为栈顶To(an端)p;表头称为栈底Base(a1端)。

image.png 插入元素到栈顶(表尾)的操作,称为入栈[压栈](push)。从栈顶(表尾)删除最后一个元素的操作,称为出栈[弹栈](pop)。

储存结构

作为一种线性表,栈既可以用顺序栈,也可以用链栈储存。但是顺序栈更常见。
栈与一般线性表的区别就在于运算规则不同 因为运算规则是只能在栈顶运算,后进先出。我们的首要任务就是编写入栈和出栈函数。

栈的经典应用

进制转化

N是我们输入的某进制数(就当作10进制吧),要转化为另一个进制(8进制),N除以8,余数保留在栈中,得到的结果存在N接着与8整除运算,直到N / 8 == 0,最后把栈中数据取出即可。因为结果是从低到高纪录,先将结果全部入栈之后再全部出栈,正好用到了栈的规则,先进后出的特性

括号匹配

在编写代码的时候,经常会用到两种括号:圆括号 “()” 和大括号 “{}” 。不管使用哪种括号,程序编译没有问题的其中一个重要因素就是所使用的括号是否能够匹配上,可以利用栈判断括号是否匹配。从控制台正常输入一串括号,在输入期间,检测到左括号,进栈,右括号就要和和左括号比较,如何比较呢,我们可以把右括号翻转,说白了就是遇见右括号就让它变成指定的左括号形式,如:if(ch == '}') 这时就可以把ch改成'{'或者'('再和栈中元素进行比较。右边括号(),})就出栈进行比较,看是否输入一对括号,如果匹配,就进行下一个比较,否则return,没有再比较的必要了。》<br 错误情况

  • 当遇到某一个右括号时,栈已空,说明目前右括号多余左括号
  • 从栈中弹出的做括号与当前检验的有括号类型不同,说明括号交叉了,不合法
  • 算数表达式输入完毕,但栈中还没有匹配的左括号,说明左括号多余有括号
表达式求值

这里的运算表达式是后缀表达式(所有的运算符都在运算数字的后面出现),包括了:

  • 操作数:常量,变量
  • 运算符:算数运算符,关系运算符和逻辑运算符
  • 界限符:左右括号与表达式结束符(表达的结束符'#',与虚设的表达起始符'#‘

为了实现表达式求值,需要两个栈。一个算符栈是OPTR,用于寄存运算符。另一个是操作数栈OPND,用于寄存运算数和运算结果。
求值的过程是自左到右扫描表达式的每一个字符。当扫描到的是运算数时,压入栈OPND。当扫描到是运算符时,若这个运算符比OPTR栈顶的运算符优先级高,则入栈OPTR,继续向后处理。若这个运算符比OPTR栈顶运算符优先级低,则从OPND栈中弹出两个运算数,从栈OPTR中弹出栈顶运算符进行运算,并将运算结果压入OPND。继续处理直到遇到结束符。

你也可以嫞栈将平时使用的表达式转化为后缀表达式。

栈的实现

关于栈的抽象数据类型的类型定义:

image.png

InitStack(&S);//构造一个空栈
DestroyStack(&S);//初始条件:栈S已存在    操作结果:栈S被销毁
StackEmpty(S);//初始条件:栈S乙存在    操作结果:若栈为空,返回true否则false
StackLength(S);//初始条件:栈S已存在    操作结果:返回S元素的个数,即栈的长度
GetTop(S,&e);//初始条件:栈S存在且非空     操作结果:用e返回栈S的栈顶元素
ClearStack(&S);//初始条件:栈S已存在    操作结果:将S清为空栈
Push(&S,e);//初始条件:栈S已存在       操作结果:插入元素e成为新的栈顶元素
Pop(&S,e);//初始条件:栈S已存在        操作结果:删除栈S的栈顶元素an,并是用e返回其值

栈也分为顺序栈和链栈,顺序栈同一般线性表的顺序储存结构完全相同[利用一组连续的储存单元一次存放自栈底到栈顶的数据元素,一般栈底在低地址端],有top指针,指示栈顶元素在顺序栈的位置(为了方便操作,通常top指针指示真正的栈顶元素之上的下标地址),base指针,指示栈底元素在顺序栈的位置。

顺序栈实现

结构体:

typedef struct{
    StackElemType *base;//栈底指针
    StackElemType *top;//栈顶指针
    int stacksize;//栈的最大容量
}SqStack;

顺序栈的初始化:

Status InitStack(SqStack &S){//构造一个空栈
    //S.base = new StackElemType[MAXSIZE];//c++
    S.base = (StackElemType*) malloc(sizeof(StackElemType) * MAXSIZE);//指向首元素也就是栈底
    if (!S.base) exit(0);//储存分配失败
    S.top = S.base;//初始时栈底指针等于栈顶指针
    S.stacksize = MAXSIZE;
    return true;
}

判断栈是否为空

//初始条件:栈S乙存在    操作结果:若栈为空,返回true否则false
bool StackEmpty(SqStack S){
    if(S.top == S.base)
        return true;
    else
        return false;
}

获取栈的元素个数(长度):

//初始条件:栈S已存在    操作结果:返回S元素的个数,即栈的长度
int StackLength(SqStack S){
    if(!S.base)
        return false;
    return S.top - S.base;
}

获取栈顶元素:

//初始条件:栈S存在且非空     操作结果:用e返回栈S的栈顶元素
Status GetTop(SqStack S,StackElemType &e){
    if (S.base)//栈是否存在
        return false;
    if (StackEmpty(S))
        return false;//栈是否为空
    //e = S.base[S.top - S.base];
    e = *(S.top - 1);
    return true;
}

入栈

//初始条件:栈S已存在       操作结果:插入元素e成为新的栈顶元素
/*
 * 1.判断是否栈满,若满则出错
 * 2.元素e压入栈
 * 3.栈顶指针+1
 */
Status Push(SqStack &S,StackElemType e){
    if(S.top - S.base == S.stacksize)//栈满,在栈中头尾指针相减时中间的空间
        return false;
    //也可以增加内存
    *S.top = e;
    S.top++;
    //*S.top++ = e;
    return true;
}

出栈

//初始条件:栈S已存在        操作结果:删除栈S的栈顶元素an,并是用e返回其值
/*
 * 1.判断栈是否为空,若为空则出错(下溢
 * 2.获取栈顶元素e
 * 3.栈顶指针-1
 */
Status Pop(SqStack &S,StackElemType e){
    if(StackEmpty(S))//if(S.top == S.base)
        return false;//栈是否为空
    S.top--;
    e = *S.top;
    //e = *--S.top;
}

遍历:

void Travel(SqStack S){
    StackElemType *temp;//栈顶指针
    temp = S.base;
    while(temp != S.top)
        cout << *temp++ << " ";
    cout << endl;
}

链栈的实现

链栈就是运算受限的单链表,只能在链表的头部进行操作。所以结构和单链表时相同的。 栈的结构类型和单链表相同:

//结构体
typedef  struct StackNode{
    StackElemType data;//数据域
    struct StackNode *next;
}StackNodeta,*LinkStack;

链表的头指针就是栈顶,而栈顶不需要头结点
不会出现栈满的情况,空栈相当于头指针指向空。 初始化:

bool InitStack(LinkStack &S){
    S = NULL;
    return true;
}

判空:

Status StackEmpty(LinkStack S){
    if(S == NULL)
        return true;
    else
        return false;
}

入栈:

/入栈
 Status PUSH(LinkStack &S,StackElemType e){
    LinkStack p = (LinkStack) malloc(sizeof(StackNode);
    //LinkStack p = new StackNode;//生成新结点p
    p->data = e;//将新结点数据域设置为e
    p->next = S;//将新结点插入栈顶
    S = p;//修改栈顶指针
    return true;
}

出栈:

//出栈
Status Pop(LinkStack &S,StackElemType &e){
    if (S == NULL)
        return false;
    e = S->data;//将删除的元素存在e
    LinkStack p = S;
    S = S->next;
    free(p);
    //delete p;
    return true;
}

取出栈顶元素:

//取栈顶元素
StackElemType Gettop(LinkStack S){
    if(S != NULL)
        return S->data;
}

遍历:

void Travel(LinkStack &S){
    LinkStack p = S;
    while(p) {
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

着就是栈的基础知识。