【笔记】栈及其应用

来源:互联网 发布:lol mac国服怎么取消了 编辑:程序博客网 时间:2024/06/16 15:55

  • 一栈的定义
  • 二栈的表示和实现
    • 顺序栈的实现
    • 栈的链式表示与实现
  • 三栈的应用
    • 算术表达式求值

一、栈的定义

  是限定仅在表尾进行插入或删除操作的线性表。对栈来说,表尾端具有其特殊含义,称为栈顶,相应地,表头端称为栈底。不含元素的空表称为空栈。栈又称为后进先出的线性表(简称LIFO结构)。

二、栈的表示和实现

  栈的基本操作只有两个:

  • 入栈(Push):即将数据保存到栈顶。进行该操作前,先修改栈顶指针,使其向上移一个元素位置,然后将数据保存到栈顶指针所指的位置。
  • 出栈(Pop):即将栈顶的数据弹出,然后修改栈顶指针,使其指向栈中的下一个元素。栈是一种特殊的线性表,因此占也可以使用两种存储表示方法。
  • 顺序栈:即使用一组地址连续的内存单元一次保存栈中的数据。一般是定义一个指定大小的数组来作为栈,序号为0的元素就是栈底,再定义一个变量top保存栈中的数据。一般是定义一个指定大小的数组来作为栈,序号为0的元素就是栈底,再定义一个变量top保存栈顶的序号即可。
  • 链式栈:使用链表形式保存栈中各元素的值,链表尾部(指向地位为NULL)为栈底,链表首部(head指针所指向元素)为栈顶。

1.顺序栈的实现

  顺序栈:即栈的顺序存储结构是利用一组地址连续的存储单元一次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。一般来说,在初始化设空栈的不应限定栈的最大容量。一个较合理的做法是,先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时再逐段扩大。非空栈中的栈顶指针始终在栈顶元素的下一个位置上。

  • 类型定义
typedef struct stack{    DATA data[SIZE+1]; //数据元素     int top; //栈顶 }SeqStack;

  整型变量top用来保存栈顶的序号,当top=0时表示栈为空,当top=SIZE时表示栈满。

  • 初始化栈
SeqStack *SeqStackInit(){    SeqStack *p;    if(p=(SeqStack *)malloc(sizeof(SeqStack))) //申请栈内存     {        p->top=0; //设置栈顶为0         return p;//返回指向栈的指针     }    return NULL;}void SeqStackFree(SeqStack *s) //释放栈所占用空间 {    if(s)        free(s);}

  注:在不使用栈时,应显示调用free函数释放内存,是一种好的编程习惯。当程序终止运行时,系统会将所有内存释放,但如果程序一直在运行,且在不断申请内存,则最终可能会导致内存不足的错误。

  • 判断栈的状态

  在对栈进行操作之前通常需要先判断栈的状态,再决定是否进行操作。

int SeqStackIsEmpty(SeqStack *s) //判断栈是否为空 {    return(s->top==0);}void SeqStackClear(SeqStack *s)  //清空栈 {    s->top=0;} int SeqStackIsFull(SeqStack *s) //判断栈是否已满{    return(s->top==SIZE);} 
  • 入栈操作

  入栈算法:

  1. 首先判断top是否大于等于SIZE,若是,则给出溢出信息,做出错处理;
  2. 设置top=top+1。
  3. 将入栈元素保存在top指向的位置。
int SeqStackPush(SeqStack *s,DATA data) //入栈操作 {     if((s->top+1)>SIZE)     {         printf("栈溢出!\n");          return 0;     }     s->data[++s->top]=data;//将元素入栈     return 1; }
  • 出栈操作

  出栈算法:

  1. 首先判断top是否小于等于SIZE,若是,则给出栈为空信息,做出错处理;
  2. 将栈顶指针top所指位置的元素返回。
  3. 设置top=top-1。
DATA SeqStackPop(SeqStack *s) //出栈操作 {     if(s->top==0)     {         printf("栈为空!");         exit(0);     }     return (s->data[s->top--]);}
  • 获取栈顶元素
DATA SeqStackPeek(SeqStack *s) //读栈顶数据{     if(s->top==0)     {         printf("栈为空!");         exit(0);     }     return (s->data[s->top]);} 
  • 主程序
#include <stdio.h>#include <stdlib.h>#define SIZE 50typedef struct{    char name[15];    int age;}DATA;int main(){    SeqStack *stack;    DATA data,data1;        stack=SeqStackInit();  //初始化栈    printf("入栈操作:\n");    printf("输入姓名 年龄进行入栈操作:");     scanf("%s%d",data.name,&data.age);    SeqStackPush(stack,data);    printf("输入姓名 年龄进行入栈操作:");     scanf("%s%d",data.name,&data.age);    SeqStackPush(stack,data);    printf("\n出栈操作:\n按任意键进行出栈操作:");    getch();    data1=SeqStackPop(stack);    printf("出栈的数据是(%s,%d)\n" ,data1.name,data1.age);            printf("再按任意键进行出栈操作:");    getch();    data1=SeqStackPop(stack);    printf("出栈的数据是(%s,%d)\n" ,data1.name,data1.age);     SeqStackFree(stack); //释放栈所占用的空间     getch();    return 0;}
  • 测试结果


这里写图片描述


2.栈的链式表示与实现

  在顺序栈中由于顺序存储结构需要实现静态分配,而存储规模往往又难以确定,如果栈空间分配过小,可能会造成溢出;如果栈空间分配过大,又造成存储空间浪费。因此为了克服顺序栈存储的缺点,采用链式存储结构表示栈。
  栈的链式存储结构是用一组不一定连续的存储单元来存放栈中的数据元素的。一般来说,当栈中数据元素的数目变化较大或不确定时,使用链式存储结构作为栈的存储结构是合适的。链式存储结构的表示又称为链栈链式栈
  链栈通常用单链表表示。插入和删除操作都在栈顶指针的位置进行,这一端称为栈顶,通常由栈顶指针top指示。为了操作方便,通常在链栈中设置一个头结点,用栈顶指针top指向头结点,头结点的指针指向链栈的第一个结点。
  栈顶指针top始终指向头结点,最先入栈的元素在链栈的栈底,最后入栈的元素成为栈顶元素。由于链栈的操作都是在链表的表头位置进行,因而链栈的基本操作时间复杂度为O(1)。


这里写图片描述

  • 类型定义
typedef struct node{DataType data;struct node *next;}LinkStackNode,*LinkStack;

  对于带头结点的链栈,初始化链栈时,有top->next=NULL,判断栈空的条件为top->next==NULL。对于不带头结点的链栈,初始化链栈时,有top=NULL,判断栈空的条件为top==NULL。

  • 初始化链栈
void InitStack(LinkStack *top)/*初始化链式栈。为头结点动态申请一块内存空间,并将指针域置为NULL*/{    if((*top=(LinkStack)malloc(sizeof(LinkStackNode)))==NULL)    {        printf("分配结点失败!\n");        exit(-1);    }    (*top)->next=NULL;}
  • 判断链栈状态
int StackEmpty(LinkStack top)/*判断链式栈是否为空。如果链式栈为空,则返回1;否则,返回0*/{    if(top->next==NULL)     /*如果头结点的指针域为空*/        return 1;           /*则返回1*/    else                    /*如果头结点的指针域不为空*/        return 0;           /*则返回0*/}int StackLength(LinkStack top)/*求栈的长度*/{    int n=0;                /*n用作计数器*/    LinkStackNode *p;    p=top;              /*从栈顶指针开始*/    while(p->next!=NULL)    /*如果栈不为空*/    {        p=p->next;          /*访问下一个结点*/        n++;                /*计数器加1*/    }    return n;               /*返回栈的长度*/}
  • 入栈操作
      先动态生成一个结点,用p指向该结点,将元素e值赋给*p结点的数据域,然后将新结点插入链表的第一个结点之前。把新结点插入链表中分为两个步骤:第一步,p->next=top->next;第二步,top->next=p。

    这里写图片描述
int PushStack(LinkStack top,DataType e)/*入栈操作。如果入栈操作成功,返回1*/{    LinkStackNode *p;    if((p=(LinkStackNode*)malloc(sizeof(LinkStackNode)))==NULL) /*动态生成一个新结点*/    {        printf("内存分配失败!\n");        exit(-1);    }    p->data=e;              /*将元素值e存放到新结点数据域*/    p->next=top->next;      /*新结点指向原来的栈顶*/    top->next=p;            /*新结点成为栈顶*/    return 1;               /*返回1表示入栈操作成功*/}
  • 出栈操作
      先判断栈是否为空,如果栈为空,返回0表示出栈操作失败;否则将栈顶元素出栈,并将栈顶元素赋给e,最后释放结点空间。

    这里写图片描述
int PopStack(LinkStack top,DataType *e)/*出栈操作。出栈操作成功返回1,否则返回0*/{    LinkStackNode *p;    p=top->next;            /*p指向栈顶结点*/    if(!p)                  /*如果栈为空*/    {        printf("栈为空,不能进行出栈操作!\n");        return 0;           /*返回0表示出栈操作失败*/    }    top->next=p->next;      /*删除栈顶结点*/    free(p);                /*释放结点所占内存空间*/    return 1;               /*返回1表示出栈操作成功*/}
  • 获取栈顶元素
int GetTop(LinkStack top,DataType *e)/*取栈顶元素。如果取栈顶元素成功返回1,否则返回0*/{    LinkStackNode *p;    p=top->next;            /*p指向栈顶结点*/    if(!p)                  /*如果栈不为空*/    {        printf("栈已空,不能取栈顶元素!\n");        return 0;           /*返回0表示取栈顶元素失败*/    }    *e=p->data;             /*取出栈顶元素赋值给e*/    return 1;               /*返回1表示取栈顶元素成功*/}
  • 销毁链栈
void ClearStack(LinkStack top)/*清空栈。释放链式栈中的每一个结点所占的内存空间*/{    LinkStackNode *p,*q;    p=top;          /*从栈顶指针开始*/    while(!p)           /*如果栈不为空*/    {        q=p;            /*q指向要释放的结点*/        p=p->next;      /*p指向下一个结点*/        free(q);        /*释放结点*/    }}
  • 主程序
#include <stdio.h>#include <stdlib.h>#define SIZE 50typedef struct{    char name[15];    int age;}DataType;int main(){    LinkStack stack;    DataType data,data1;    InitStack(&stack);                      /*初始化链栈*/    printf("入栈操作:\n");    printf("输入姓名 年龄进行入栈操作:");    scanf("%s%d",data.name,&data.age);    PushStack(stack,data);    printf("输入姓名 年龄进行入栈操作:");    scanf("%s%d",data.name,&data.age);    PushStack(stack,data);    printf("\n出栈操作:\n按任意键进行出栈操作:");    getch();    PopStack(stack,&data1);    printf("出栈的数据是(%s,%d)\n" ,data1.name,data1.age);    printf("再按任意键进行出栈操作:");    getch();    PopStack(stack,&data1);    printf("出栈的数据是(%s,%d)\n" ,data1.name,data1.age);    ClearStack(stack); //释放栈所占用的空间    getch();    return 0;}
  • 测试结果


这里写图片描述


三.栈的应用

1.算术表达式求值

详情请参见算术表达式求值

原创粉丝点击