数据结构与算法(五):栈

栈的定义

官方定义:栈(Stack)是一个后进先出Last in first outLIFO)的线性表,它要求只在表尾进行删除和插入操作

小甲鱼的定义:所谓的栈,其实也就是一个特殊的线性表(顺序表、链表),但是它再操作上有一些特殊的要求和限制:

  • 栈的元素必须"后进先出"
  • 栈的操作只能再这个线性表的表尾进行
  • 注:对于栈来说,这个表尾称为栈的栈顶(top),相应的表头称为栈底(bottom)

   

栈的插入和删除操作

  • 栈的插入操作(Push),叫做进栈,也称为压栈,入栈 类似子弹放入弹夹的动作
  • 栈的删除操作(Pop),叫做出栈,也称为弹栈 如同单价中的子弹出夹
  • 图片演示:

   

栈的顺序存储结构

   

定义一个顺序存储的栈

  • base是指向栈底的指针变量
  • top是指向栈顶的指针变量
  • stackSize是指栈的当前可使用的最大容量

typedef struct

{

    ElemType *base;

    ElemType *top;

    int stackSize;

}sqStack;

   

另一种声明方法

typedef int ElemType;

typedef struct

{

    ElemType data[MAXSIZE];

    int top; //用于标注栈顶位置

    int stackSize;

}

   

   

初始化创建一个栈

#define STACK_INIT_SIZE 100

initStack(sqStack *s)

{

    s->base = (ElemType *)malloc( STACK_INIT_SIZE * sizeof(ElemType) );

    if( !s->base )

        exit(0);

    s->top = s->base; // 最开始,栈顶就是栈底

    s->stackSize = STACK_INIT_SIZE;

}

   

入栈操作

  • 入栈操作又叫压栈操作,就是向栈中存放数据
  • 入栈操作要在栈顶进行,每次向栈中压入一个数据。top指针就要+1,直到栈满为止

#define SATCKINCREMENT 10

Push(sqStack *s, ElemType e)

{

// 如果栈满,追加空间

if( s->top – s->base >= s->stackSize )

{

s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));

if( !s->base )

exit(0);

s->top = s->base + s->stackSize; // 设置栈顶

s->stackSize = s->stackSize + STACKINCREMENT; // 设置栈的最大容量

}

*(s->top) = e;

s->top++;

}

   

   

出栈操作

  • 出栈操作就是在栈顶取出数据,栈顶指针随之下移
  • 每当栈内弹出一个数据,栈的当前容量就-1

Pop(sqStack *s, ElemType *e)

{

    if( s->top == s->base ) // 栈已空空是也

    return;

    *e = *--(s->top);

}

   

清空栈

就是将栈中的元素全部作废,但栈本身的物理空间并不发生改变(不是销毁)

因此我们只要将s->top的内容赋值为s->base即可,这样s->base等于s->top,也就表明这个栈是空的了

ClearStack(sqStack *s)

{

    s->top = s->base;

}

   

销毁栈

与清空栈不同,销毁一个栈是要释放带哦该栈所占据的物理内存空间

DestroyStack(sqStack *s){

    int i, len;

    len = s->stackSize;

    for( i=0; i < len; i++ ){

    free( s->base );

    s->base++;

    }

    s->base = s->top = NULL;

    s->stackSize = 0;

}

   

计算栈的当前容量

s.stackSize是栈的最大容量,并不是栈的当前容量

计算栈的当前容量也就是计算栈中元素的个数,只要计算s.top - s.base即可

int StackLen(sqStack s)

{

    return(s.top – s.base); // 初学者需要重点讲解

}

   

   

实例

二进制转换为十进制数

计算方法: (XnXn-1……X3X2X1)2 = X1*2^0+X2*2^1+…+Xn*2^(n-1)

一个二进制数要转换为相应的十进制数,就是从最低位起用每一位去乘以对应位的积,也就是说用第n位去乘以2^(n-1),然后全部加起来

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#define STACK_INIT_SIZE 20

#define STACKINCREMENT 10

typedef char ElemType;

typedef struct

{

        ElemType *base;

        ElemType *top;

        int stackSize;

}sqStack;

void InitStack(sqStack *s)

{

        s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));

        if( !s->base )

        {

                exit(0);

        }

   

        s->top = s->base;

        s->stackSize = STACK_INIT_SIZE;

}

void Push(sqStack *s, ElemType e)

{

        if( s->top - s->base >= s->stackSize )

        {

                s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));

                if( !s->base )

                {

                        exit(0);

                }

        }

   

        *(s->top) = e;

        s->top++;

}

void Pop(sqStack *s, ElemType *e)

{

        if( s->top == s->base )

        {

                return;

        }

        *e = *--(s->top);

}

int StackLen(sqStack s)

{

        return (s.top - s.base);

}

int main()

{

        ElemType c;

        sqStack s;

        int len, i, sum = 0;

        InitStack(&s);

        printf("请输入二进制数,输入#符号表示结束!\n");

        scanf("%c", &c);

        while( c != '#' )

        {

                Push(&s, c);

                scanf("%c", &c);

        }

        getchar(); // '\n'从缓冲区去掉

        len = StackLen(s);

        printf("栈的当前容量是: %d\n", len);

        for( i=0; i < len; i++ )

        {

                Pop(&s, &c);

                sum = sum + (c-48) * pow(2, i);

        }

        printf("转化为十进制数是: %d\n", sum);

        return 0;

   

二进制转八进制数

一个十六进制数最多占4bit,一个字节(8bit)刚好用两个十六进制数完全表示,节省了显示空间

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#define STACK_INIT_SIZE 20

#define STACKINCREMENT 10

typedef char ElemType;

typedef struct

{

        ElemType *base;

        ElemType *top;

        int stackSize;

}sqStack;

// 函数功能:初始化栈

// 参数*s:栈的地址

void InitStack(sqStack *s)

{

        s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));

        if( !s->base )

        {

                exit(0);

        }

   

        s->top = s->base;

        s->stackSize = STACK_INIT_SIZE;

}

// 函数功能:入栈操作

// 参数*s:栈的地址

// 参数e:待压入栈的元素

void Push(sqStack *s, ElemType e)

{

        if( s->top - s->base >= s->stackSize )

        {

                s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));

                if( !s->base )

                {

                        exit(0);

                }

   

                s->top = s->base + s->stackSize;

                s->stackSize = s->stackSize + STACKINCREMENT;

        }

   

        *(s->top) = e;

        s->top++;

}

// 函数功能:弹栈操作

// 参数*s:栈的地址

// 参数e:存放从栈里弹出的数据

void Pop(sqStack *s, ElemType *e)

{

        if( s->top == s->base )

        {

                return;

        }

        *e = *--(s->top);

}

// 函数功能:计算栈s的当前长度

// 参数s:栈

int StackLen(sqStack s)

{

        return (s.top - s.base);

}

int main()

{

        ElemType c;

        sqStack s1;

        sqStack s2;

        int len, i, j, sum = 0;

        InitStack(&s1); // 初始化栈s1,用来存放二进制输入

        printf("请输入二进制数,输入'#'号表示结束!\n\n");

        scanf("%c", &c);

        while( c != '#' )

        {

                if( c=='0' || c=='1' ) // 检查输入是否二进制

                        Push(&s1, c);

                scanf("%c", &c);

        }

        getchar(); // '\n'从缓冲区去掉

        len = StackLen(s1);

        InitStack(&s2); // 初始化栈s2,用来存放转换的八进制

        for( i=0; i < len; i+=4 )

        {

                for( j=0; j < 4; j++ )

                {

                        Pop( &s1, &c ); // 取出栈顶元素

                        sum = sum + (c-48) * pow(2, j);

   

                        if( s1.base == s1.top )

                        {

                                break;

                        }

                }

                switch( sum )

                {

                        case 10: sum = 'A'; break;

                        case 11: sum = 'B'; break;

                        case 12: sum = 'C'; break;

                        case 13: sum = 'D'; break;

                        case 14: sum = 'E'; break;

                        case 15: sum = 'F'; break;

                        default: sum += 48;

                }

                Push( &s2, sum );

                sum = 0;

        }

        printf("\n转化为十六进制数是: ");

        while( s2.base != s2.top )

        {

                Pop( &s2, &c );

                printf("%c", c);

        }

        printf("(H)\n"

二进制转八进制

进行二进制到八进制的转换时,要将二进制数的每三位抓换成一个八进制数来表示,然后按顺序输出即可

   

   

   

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#define STACK_INIT_SIZE 20

#define STACKINCREMENT 10

typedef char ElemType;

typedef struct

{

        ElemType *base;

        ElemType *top;

        int stackSize;

}sqStack;

// 函数功能:初始化栈

// 参数*s:栈的地址

void InitStack(sqStack *s)

{

        s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));

        if( !s->base )

        {

                exit(0);

        }

        s->top = s->base;

        s->stackSize = STACK_INIT_SIZE;

}

// 函数功能:入栈操作

// 参数*s:栈的地址

// 参数e:待压入栈的元素

void Push(sqStack *s, ElemType e)

{

        if( s->top - s->base >= s->stackSize )

        {

                s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));

                if( !s->base )

                {

                        exit(0);

                }

                s->top = s->base + s->stackSize;

                s->stackSize = s->stackSize + STACKINCREMENT;

        }

   

        *(s->top) = e;

        s->top++;

}

// 函数功能:弹栈操作

// 参数*s:栈的地址

// 参数e:存放从栈里弹出的数据

void Pop(sqStack *s, ElemType *e)

{

        if( s->top == s->base )

        {

                return;

        }

        *e = *--(s->top);

}

// 函数功能:计算栈s的当前长度

// 参数s:栈

int StackLen(sqStack s)

{

        return (s.top - s.base);

}

int main()

{

        ElemType c;

        sqStack s1;

        sqStack s2;

        int len, i, j, sum = 0;

        InitStack(&s1); // 初始化栈s1,用来存放二进制输入

        printf("请输入二进制数,输入'#'号表示结束!\n\n");

        scanf("%c", &c);

        while( c != '#' )

        {

                if( c=='0' || c=='1' ) // 检查输入是否二进制

                        Push(&s1, c);

                scanf("%c", &c);

        }

        getchar(); // '\n'从缓冲区去掉

        len = StackLen(s1);

        InitStack(&s2); // 初始化栈s2,用来存放转换的八进制

        for( i=0; i < len; i+=3 )

        {

                for( j=0; j < 3; j++ )

                {

                        Pop( &s1, &c ); // 取出栈顶元素

                        sum = sum + (c-48) * pow(2, j);

   

                        if( s1.base == s1.top )

                        {

                                break;

                        }

                }

                Push( &s2, sum+48 );

                sum = 0;

        }

        printf("\n转化为八进制数是: ");

        while( s2.base != s2.top )

        {

                Pop( &s2, &c );

                printf("%c", c);

        }

        printf("(O)\n"

   

栈的链式存储结构

(简称栈链)

栈的定义

typedef struct StackNode

{

    ElemType data; // 存放栈的数据

    struct StackNode *next;

}StackNode, *LinkStackPtr;

   

typedef struct LinkStack

{

    LinkStackPrt top; // top指针

    int count; // 栈元素计数器

}

   

进栈操作

对于栈链的Push操作,假设元素值为e的新结点是s,top为栈顶指针

Status Push(LinkStack *s, ElemType e)

{

    LinkStackPtr p = (LinkStackPtr) malloc (sizeof(StackNode));

    p->data = e;

    p->next = s->top;

    s->top = p;

    s->count++;

    return OK;

}

   

出栈操作

对于栈链的Pop操作,假设变量p用来存储要删除的栈顶结点

将栈顶指针下移一位,最后释放p即可

Status Pop(LinkStack *s, ElemType *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;

}

   

逆波兰表达式

   

   

(1-2)*(4+5) 逆波兰表达式:1 2 - 4 5 + *

数字12进栈,遇到减号运算符则弹出两个元素进行运算并把结果入栈

   

45入栈,遇到加号运算符,45弹出栈,相加后将结果9入栈

   

然后又遇到乘法运算符,9-1弹出栈进行乘法计算,此时栈空并无数据压栈,-9为最终运算结果!

   逆波兰计算器

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 20
#define STACKINCREMENT  10
#define MAXBUFFER       10

typedef double ElemType;
typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

InitStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if( !s->base )
        exit(0);

    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

Push(sqStack *s, ElemType e)
{
    //栈满,追加空间,必须懂 
    if( s->top - s->base >= s->stackSize )
    {
        s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
        if( !s->base )
            exit(0);

        s->top = s->base + s->stackSize;
        s->stackSize = s->stackSize + STACKINCREMENT;
    }

    *(s->top) = e;      // 存放数据 
    s->top++;
}

Pop(sqStack *s, ElemType *e)
{
    if( s->top == s->base )
        return;

    *e = *--(s->top);   // 将栈顶元素弹出并修改栈顶指针 
}

int StackLen(sqStack s)
{
    return (s.top - s.base);
}

int main()
{
    sqStack s;
    char c;
    double d, e;
    char str[MAXBUFFER];
    int i = 0;

    InitStack( &s );

    printf("请按逆波兰表达式输入计算数据,以#结束\n");
    scanf("%c", &c);

    while( c != '#' )
    {
        while( isdigit(c) || c=='.' )  // 用于过滤数字 
        {
            str[i++] = c;
               str[i] = '\0';
            if( i >= 10 )
            {
                printf("出错,大\n");
                return -1;
            }
            scanf("%c", &c);
            if( c == ' ' )
            {
                d = atof(str);
                Push(&s, d);
                i = 0;
                break;
            }
        }

        switch( c )
        {
            case '+':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s, d+e);
                break;
            case '-':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s, d-e);
                break;
            case '*':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s, d*e);
                break;
            case '/':
                Pop(&s, &e);
                Pop(&s, &d);
                if( e != 0 )
                {
                    Push(&s, d/e);
                }
                else
                {
                    printf("\n出错,0\n");
                    return -1;
                }
                break;
        }

        scanf("%c", &c);
    }

    Pop(&s, &d);
    printf("\n最终计算结果为:%f\n", d);

    return 0;
}
// 5 - (6 + 7) * 8 + 9 / 4
// 5 - 13 * 8 + 9 / 4
// 5 - 104 + 2.25
// -99 + 2.25
// 5 6 7 + 8 * - 9 4 / +

 

中缀表达式转换为后缀表达式

从左到右遍历中缀表达式的每个数字和符号,若是数字则直接输出

若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号,则栈顶元素依次出栈并输出,直到遇到左括号或栈空才将吃屎的那个符号入栈

1*(2+3)

  • 从左往右扫描中缀表达式

  • 如果是数字那么将其直接入栈到数组num

  • 如果是运算符 +-*/,若opera栈空或者为左括号直接插入,否则与opera栈顶元素比较优先级,比栈顶高插入,比栈顶低则输出栈顶

  • 如果是左括号直接插入opera栈

  • 如果是右括号将opera中的运算符依次出栈,并入栈到num中,直到遇到左括号

  • 如果中缀表达式扫描完了,那么将opera中的操作数依次出栈

    需要注意的是opera中操作数,越靠近栈顶,优先级越高

posted @ 2020-04-23 11:27  kyrie_wx  阅读(573)  评论(0编辑  收藏  举报