栈知识介绍、存储方式介绍及栈的应用

1.知识体系

栈知识

2.栈的基本概念

2.1 基本概念

栈是一种只能在一端进行插入或删除操作的线性表。

  • 栈顶:允许进行插入或删除的一端
  • 栈底:固定的,不允许插入或删除的一端
  • 空栈:不含任何元素的空表
  • 满栈: 表空间满,无法存储新元素。
  • 栈的特点: 先进后出(FILO)
2.2 数学性质

n个不同元素进栈,出栈元素不同排列的个数为
1 n + 1 C ( n 2 n ) \frac{1}{n+1}C{n \choose 2n} n+11C(2nn)
以上数字成为卡特兰数。

3.顺序栈

顺序栈,按其存储方式,主要分为:静态顺序栈、动态顺序栈。
通常用b(bottom)表示栈底指针,用t(top)指示当前栈顶位置,栈可以存放n个元素,在进行顺序栈的初始化时,可以选取以下几种:

序号初始化栈空条件栈顶指针位置栈满条件入栈出栈栈元素数量
方法1b=t=-1b==t指向栈顶元素t-b>=nt++;放入元素弹出元素;t- -t-b
方法2(常用)b=t=0b==t栈顶元素的下一个空位置t-b>=n放入元素;t++t- -;弹出元素t-b
方法3b=t=nb==t指向栈顶元素b-t>=nt- -;放入元素弹出元素;t++b-t
方法4b=t=n-1b==t栈顶元素的下一个空位置b-t>=n放入元素;t- -t++;弹出元素b-t
3.1 静态顺序栈

静态栈分配空间后,内存地址将不会发生变化。

3.1.1 栈的定义
//静态顺序栈
#define MAX_STACK_SIZE 100
typedef int ElementType;
typedef struct static_sq_stack{
    ElementType stack_array[MAX_STACK_SIZE];
    int top;
}SqStack;
3.1.2 栈的初始化
void InitStack(SqStack *S){
    //初始化操作
    S->top = 0;
}
3.1.3 栈的入栈
bool push_stack(SqStack S, ElementType e){
    //静态顺序栈进栈
    if (S.top == MAX_STACK_SIZE) return false;
    S.data[S.top] = e;
    S.top++;
    //以上可简写为 S.data[S.top++] = e
    return true;
}

3.1.4 栈的出栈
bool pop_stack(SqStack S, ElementType * e){
    //静态顺序栈出栈
    if (S.top == 0) return false;
    S.top--;
    * e = S.data[S.top];
    return true;
}
3.2 动态顺序栈

所谓动态,指的是可以动态改变栈的大小。可以用bottom表示栈底指针,固定不变;用top(称为栈顶指针)指示当前栈顶位置,栈顶随着进栈和退栈操作而变化。
注意:假如原栈的大小为50个,其栈顶位置为t1,栈底位置为b1;则将栈大小改为60个,则其栈顶内存地址将可能会由t1变为t2,栈底内存地址可能会由b1变为b2。即t1不一定等于t2,b1不一定等于b2。

3.2.1 定义
// 动态顺序栈
#define STACK_SIZE 100       /*栈初始数组大小*/
#define STACKINCREMENT 10    /*存储空间分配增量*/
typedef int ElementType;
// 用bottom表示栈底指针,栈底是固定不变的;用top(成为栈顶指针)表示当前栈顶位置,栈顶随着进栈和出栈操作而发生变化;
typedef Struct sqstack{
    ElementType *bottom; //栈不存在时,值为NULL
    ElementType *top;   //栈顶指针
    int stacksize;      //当前分配空间,以元素为单位
}SqStack_dynamic;

3.2.2 动态顺序链栈的初始化
bool Init_dynamic_stack(SqStack_dynamic S){
    //动态顺序栈进行初始化
    S.bottom = (ElementType *)malloc(STACK_SIZE * sizeof(ElementType));
    if (!S.bottom) return false;
    S.top = S.bottom;
    S.stacksize = STACK_SIZE;
    return true;
}

3.2.3 动态顺序链栈的入栈
bool push_d_stack(SqStack_dynamic S, ElementType e){
    //动态顺序栈的入栈操作
    if (S.top - S.bottom >= S.stacksize){
        //栈满时,追加存储空间
        S.bottom = (ElementType *)realloc(S.bottom, (STACKINCREMENT + S.stacksize) * sizeof(ElementType));
        if (!S.bottom) return false;
        S.top = S.bottom + S.stacksize;
        S.stacksize += STACKINCREMENT;
    }
    //将元素放入到顶指针指向的空位置。
    * S.top = e;
    // 栈顶指针加1,e成为新的栈顶
    S.top++;
    return true;
}
3.2.4 动态顺序链栈的出栈
bool pop_d_stack(SqStack_dynamic S, int *e){
    //弹出栈顶元素,并将值赋给e;
    if(S.top == 0) return false;
    S.top--;
    *e = *S.top;
    return true;
}
3.3 共享顺序栈

共享顺序栈,又称对顶栈。
假如有两个顺序栈,让两个栈共享同一片存储空间,这片存储空间不单独属于任何一个栈,某个栈需要的多一点,它就可能得到多一些的存储空间。两个栈的栈底在这片存储空间的两端,当元素入栈时,两个栈的栈顶指针相向而行,这样的栈称为对顶栈。
栈满条件: |t1 - t2| == 1

4.链栈

链栈其实是头出头插的单链表,即入栈时头插法,将新结点插在头结点的后面;出栈时是头出法,将头结点后的结点删除。

4.1 链栈的定义
//定义链栈
typedef struct Stack_Node{
    ElementType data;
    struct Stack_Node * next;
}Stack_Node;

一个栈顶指针为top的链栈指向头结点,其链栈为空的条件是:
top->next == NULL

4.2 链栈的初始化
Stack_Node *Init_Link_Stack(){
    // 链栈的初始化
    Stack_Node *top;
    top = (Stack_Node *)malloc(sizeof(Stack_Node));
    top ->next = NULL;
    return top;
}
4.3 链栈的入栈
bool push_Link_Stack(Stack_Node * S, ElementType e){
    //链栈的入栈
    Stack_Node * p;
    p = (Stack_Node *)malloc(sizeof(Stack_Node));
    if (!p) return false;
    p->data = e;
    p->next = S->next;
    S->next = p;
    return true;
}

4.4 链栈的出栈
bool pop_Link_Stack(Stack_Node * S, ElementType * e){
    // 链栈的出栈
    Stack_Node * p;
    p = S->next;
    if (p == NULL) return false;
    S->next = p->next;
    * e = p->data;
    free(p);
    return true;
}

4.5 链栈的注意事项
  1. 若以链表作为栈的存储结构,退栈时,需要判断栈是否为空;
  2. 与顺序栈相比,链栈的优势是通常不会出现栈满的情况;
  3. 假设链表不带头结点且所有操作均在表头进行,则最不适合作为链栈的是:只有表头指针没有表尾指针的循环单链表

5.栈的应用

5.1 括号的匹配

能够匹配成对出现的( ) [ ] { }

5.2 函数和递归的调用

递归函数,先调用,后执行。

5.3 表达式求解

中序表达式转后缀表达式:
①转化前先建立两个栈,命名为s1和s2。用栈s1存储表达式中的运算数,用栈s2存储符号。
②在转化的过程中,需从左到右扫描中缀表达式的每个运算数和符号,若是运算数则压入到栈s1
③若扫描到的是符号,设符号是C,则可能出现以下情况。
a.若s2为空,则C压入到栈s2中,
b.若C为左括号,则C压入到栈s2中。
c.若s2的栈顶符号为左括号,则C压入到栈s2中。
d.若s2的栈顶符号的优先级小于C,则C压入到栈s2中(右括号的优先级最低)。
C.否则,就要将s2的栈顶符号弹出,从s1中弹出两个(也可能是1个)运算数,进行运算,并把运算的结果压回s1。
④扫描到表达式结束,若最后栈s2中还有符号,则将其依次出栈,并从s1中弹出两个(也可能是1个)
运算数,进行运算,并把运算的结果压回s1。
⑤最终完成转化过程。

5.4 进制转换

将十进制数100,转为8进制数为?

数字nn / 8(取商)n%8(取余数)
100124
1214
101

将上述最后一列存入栈中,按序取出即为转换后的结果:144

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskylife

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值