2. 线性结构--栈

来源:互联网 发布:百度php面试题 编辑:程序博客网 时间:2024/06/11 16:05

定义

具有一定操作约束的线性表,只在一端(栈顶、Top)做插入、删除

  • 插入数据:入栈(Push)
  • 删除数据:出栈(Pop)
  • 后入先出:Last In First Out(LIFO)

抽象数据类型描述

  • 类型名称:栈(Stack)
  • 数据对象集:一个有0个或多个元素的有穷线性表
  • 操作集:长度为MaxSize的栈SStack,堆栈元素itemElementType
    1. Stack CreateStack(int MaxSize):生成空栈,其3最大长度为MaxSize
    2. int IsFull(Stack S, int MaxSize):判断栈S是否已满
    3. void Push(Stack S, ElementType item):将元素item压入栈
    4. int IsEmpty(Stack S):判断栈S是否为空
    5. ElementType Pop(Stack S):删除并返回栈顶元素

顺序存储实现

栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成

代码定义

#define MaxSize <存储数据元素的最大个数>typedef struct SNode *Stack;struct SNode {    ElementType Data[MaxSize];    int Top;};

主要操作

入栈

void Push(Stack PtrS, ElementType item) {    if (PtrS->Top == MaxSize - 1) {        printf("堆栈满");        return;    }    PtrS->Data[++(PtrS->Top)] = item;}

出栈

ElementType Pop(Stack PtrS) {    if (PtrS->Top == -1) {        printf("堆栈空");        return ERROR;           // ERROR是ElementType的特殊值,标志错误    }    return PtrS->Data[(PtrS->Top)--];}

一个数组实现两个栈

使用一个数组实现两个栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功

分析

使这两个栈分别从数组的两头开始向中间生长,当两个栈的栈顶指针相遇,表示两个栈都满了。

结构定义

#define MaxSize <存储数据元素的最大个数>struct DStack {    ElementType Data[MaxSize];    int Top1;       // 栈1的栈顶指针    int Top2;       // 栈2的栈顶指针} S;S.Top1 = -1;S.Top2 = MaxSize;

主要操作

入栈
// Tag 作为区分两个栈的标志,取值为1和2void Push(struct DStack *PtrS, ElementType item, int Tag) {    if (PtrS->Top2 - PtrS->Top1 == 1) {     // 栈满        printf("栈满");        return;    }    if (Tag == 1)   // 对第一个栈操作        PtrS->Data[++(PtrS->Top1)] = item;    else            // 对第二个栈操作        PtrS->Data[--(PtrS->Top2)] = item;}

出栈

// Tag作为区分两个栈的标志,取值为1和2ElementType Pop(struct DStack *PtrS, int Tag) {    if (Tag == 1) {        if (PtrS->Top1 == -1) {            printf("堆栈1空");            return NULL;        }        return PtrS->Data[(PtrS->Top1)--];    } else {        if (PtrS->Top2 == MaxSize) {            printf("堆栈2空");            return NULL;        }        return PtrS->Data[(PtrS->Top2)++];    }}

链式存储实现

栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。

栈顶指针Top应该在链表的头部。

代码定义

typedef struct SNode *Stack;struct SNode {    ElementType Data;    struct SNode *Next;};

主要操作

栈初始化(建立空栈)

// 构建一个栈的头结点,返回指针Stack CreateStack() {    Stack S;    S = (Stack)malloc(sizeof(struct SNode));    S->Next = NULL;    return S;}

判断栈是否为空

// 判断堆栈S是否为空,如果为空返回1,否则返回0int IsEmpty(Stack S) {    return (S->Next == NULL);}

入栈

void Push(ElementType item, Stack S) {    struct SNode *TmpCell;    TmpCell = (struct SNode *)malloc(sizeof(struct SNode));    TmpCell->Data = item;    TmpCell->Next = S->Next;    S->Next = TmpCell;}

出栈

ElementType Pop(Stack S) {    struct SNode *FirstCell;    ElementType TopElem;    if (IsEmpty(S)) {        printf("栈空");        return NULL;    }    FirstCell = S->Next;    S->Next = FirstCell->Next;    TopElem = FirstCell->Data;    free(FirstCell);    return TopElem;}

栈的应用:表达式求值

应用栈实现后缀表达式求值

从左到右读入后缀表达式的各项(运算符或运算数):
1. 运算数:入栈
2. 运算符:从栈中弹出适当数量的运算数,计算并把结果入栈
3. 最后,栈顶上元素就是表达式的结果值

中缀表达式求值

基本策略

将中缀表达式转换为后缀表达式,然后求值

转换过程

从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理:
1. 运算数:直接输出
2. 左括号:压入栈
3. 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(左括号出栈,但是不输出)
4. 运算符:
* 若优先级大于栈顶运算符时,则把它压栈
* 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比教新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
5. 若各对象处理完毕,则把栈中存留的运算符一并输出

中缀转后缀示例:(2(9+6/35)+4)

步骤 待处理表达式 堆栈状态(底<–>顶) 输出状态 1 2(9+6/35)+4 2 (9+6/35)+4 2 3 (9+6/35)+4 2 4 9+6/35)+4 ( 2 5 +6/35)+4 ( 2 9 6 6/35)+4 ( + 2 9 7 /35)+4 ( + 2 9 6 8 35)+4 ( + / 2 9 6 9 5)+4 ( + / 2 9 6 3 10 5)+4 ( 2 9 6 3 / + 11 )+4 ( 2 9 6 3 / + 5 12 +4 2 9 6 3 / + 5 13 4 + 2 9 6 3 / + 5 14 + 2 9 6 3 / + 5 4 15 2 9 6 3 / + 5 4 +
原创粉丝点击