栈基础详解

栈和队列都属于线性表

什么是栈

栈也是一种线性表,但它是受到限制的线性表,就是先放入的东西后被取到,后放入的东西优先被取到,认为其遵循 “后进先出” 原则。

栈中允许插入和删除操作的一端成为栈顶,不允许插入和删除操作的一端成为栈底

向一个栈中插入新元素称为入栈压栈,入栈之后该元素被放在栈顶元素的上面,称为新的栈顶元素
入栈和出栈 图1.1
从一个栈中删除元素又称为出栈弹栈,是把栈顶元素删除,使其相邻元素成为新的栈顶元素。

栈也分为顺序栈链栈两种存储结构。

栈的操作

入栈

入栈:先将元素插入到栈中,然后按照数据入栈的先后顺序,从下往上以此排列,每当插入新的元素时,栈顶指针就会向上移动,指向新插入的元素

(顺序栈)
入栈 图1.2
从上图可以看出,向栈中插入元素时,需要先使top指针指向栈顶元素上面的空位,然后进行赋值。

当栈已满时不能执行入栈操作

出栈

执行出栈操作时,栈顶元素会被先弹出,接着按照后进先出的原则将栈中的元素依次弹出。弹出栈顶元素后,栈顶指针就向下移动,指向原栈顶下面的一个元素,这个元素就成为了新的栈顶元素。

(顺序栈)
出栈 图1.2

栈为空时,不能继续执行出栈操作。

【注意】:出栈是将top指针向下移动,指示到新的栈顶元素,原来的栈顶元素依然存在于存储单元中,但无法通过栈进行访问.

若要从栈中获取元素,只能通过栈顶指针取到栈顶元素,无法取得其他元素,,在上图中,当Data3没有被弹出时,无法读取到下面的元素。

删除栈中元素

只将top指针往下移动,指向新的栈顶元素,删除过程参考上图的出栈

栈的实现(顺序栈)

两栈共享空间(顺序栈)

如果有两个相同类型的栈,为它们各自开辟了数组空间,极有可能是第一个栈已经满了,再进栈就溢出了,而另外一个栈还有很多存储空间。所以两栈共享空间的思想是:让一个栈的栈底为数组的开始端,即下标为0处,另一个栈的栈底为数组的末端,即下标为数组长度的n-1出,这样,两个栈如果增加元素,就是两端点向中间延伸。当两个栈见面之时,也就是两个指针相差1时,即top1 + 1 == top2时为栈满。

不使用两栈共享空间的顺序栈实现的基本原理都差不多,不在详细叙述

定义最大长度

#define MAX 20

定义结构体

typedef struct 
{
    int data[MAX];
    int top1,top2;//两栈共享空间
}strock;

创建空栈(初始化栈)

void createStrock(strock *s,int n){
    if(n == 1)//判断初始化哪一个栈
        s->top1 = -1;
    if(n == 2)
        s->top2 = MAX;
}

入栈

//入栈
void Push(strock *s,int n){
    if (s->top2 - s->top1 == 1)
    {
        printf("栈已满\n");
    }
    else{
        if (n == 1)
        {
            scanf("%d",&s->data[++s->top1]);
        }
        if (n == 2)
        {
            scanf("%d",&s->data[--s->top2]);
        }
        
    }   
}

获取栈顶元素

//获取栈顶元素
void showData(strock *s,int n){
    if (n == 1 && s->top1 > -1)
    {
        printf("top1栈顶元素为:%d\n",s->data[s->top1]);
    }
    else if (n == 2 && s->top2 < MAX)
    {
        printf("top2栈顶元素为:%d\n",s->data[s->top2]);
    }
    else
        printf("栈为空或其它错误!\n");
}

出栈

//出栈
int Pop(strock *s,int n){
    int x;
    if(s->top1 != -1 && n == 1){
         x = s->data[s->top1];
         printf("栈顶元素:%d\n",s->data[--s->top1]);
    }
    else if (s->top2 != MAX && n == 2)
    {
       x = s->data[s->top2];
       printf("栈顶元素:%d\n",s->data[++s->top2]);
    }
    else
        x = -1;
    return x;
}

顺序栈的其他操作不再详细介绍

链栈

栈的链式存储也称为链栈,它和链表的存储原理一样,都可以利用闲散空间来存储元素,用指针来建立各结点之间的逻辑关系。链栈也会设置一个栈顶元素的标识符top,称为栈顶指针。和链表的区别是,它只能在一端进行各种操作。说白了,链栈就是一个单端操作的链表。

创建结构体

typedef struct stackNode{//数据结点
    int data;//数据
    struct stackNode *next;//指针
}sNode;
typedef struct topNode{//此结构记录栈的大小和栈顶元素指针
    sNode *top;//链栈元素指针
    int lens;//栈大小
}stackTop;

创建栈

//创建栈
stackTop *Create(){
    stackTop *h;
    h = (stackTop*)malloc(sizeof(stackTop));//开辟空间
   //栈链可以不需要头节点
    h->top = NULL;
    h->lens = 0;
    return h;
}

判断栈是否为空

//判断链栈是否为空
int IsEmpty(stackTop *h){
    if(h->lens == 0 || h->top == NULL)
        return 1;
    return 0;
}

入栈

//入栈
//入栈
void push(stackTop *h){
    sNode *node = (sNode *)malloc(sizeof(sNode));
    scanf("%d",&node->data);//数据
    node->next = h->top;//新元素指向下一个结点
    h->top = node;//top指向心新结点
    h->lens++;
}

出栈

//出栈
void Pop(stackTop *h){
    if(isEmpty(h)){
        printf("栈为空\n");
    }
    else{
       sNode *top1 = h->top;//top1指向栈顶元素
       h->top = top1->next;//top指向下一个元素
       free(top1); 
       h->lens--;
    }
   
}

获取栈顶元素

//获取栈顶元素
//获取栈顶元素
void get(stackTop *h){
    sNode *node = h->top;
    if(isEmpty(h))
        printf("栈为空\n");
    else
     printf("%d\n",node->data);
    
}

中缀转后缀表达式

中缀表达式:是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法,但是不容易被计算机解析。

后缀表达式;逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式。

中缀转后缀表达式:中缀表达式的简单是相对人类的思维结构来说的,对计算机而言中缀表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

转化算法

首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为存放结果(逆波兰式)的栈S2(空栈)。

1. 从左到右扫描依次每一个字符。如果扫描到的字符是操作数(如a、b、c等),就直接存储到栈S2中。

2. 如果扫描到的字符是一个操作符,可以分为三种情况:

(1)如果栈S1为空,直接将操作符存储到栈S1中

(2)如果该操作符的优先级大于栈S1栈顶的操作符(不包括括号运算符),就直接将操作符存储到栈S1中

(3)如果该操作符的优先级低于栈S1栈顶的操作符,就将栈S1栈顶的操作符导出并存储到栈S2中 ,直至栈S1栈顶运算符(包括左括号)低于(不包括等于)该运算符优先级时停止弹出运算符,最后将该运算符送入S1栈。

3. 如果遇到的操作符是左括号"(”,直接将该操作符存储到栈S1中。并且,该符号需特殊处理,如果扫描到的操作符是右括号“)”,将栈S1中的操作符出栈,存储到栈S2中,直到遇见左括号“(”。并将栈S1中的左括号出栈。继续扫描下一个字符

4. 重复上述步骤,直至处理完所有的输入字符。

5. 如果中缀表达式扫描完毕,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

6. 完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了。

后缀转中缀表达式

将后缀表达式依次元素的压入到栈中,当压入的是字符是不采取任何操作,当压入的是运算符时,把运算符下面的两个字符依次弹出和运算符进行运算,然后把结果继续压入栈中,重复以上操作,直到结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值