数据结构实验三——栈的基本操作(2021级zzu)

ps: 滴滴滴~
打卡第三天,冲冲冲~~~~~~

一、实验目的

熟练掌握的基本操作,深入了解栈的特性,能在实际问题的背景下灵活运用他们,并加深对这种结构的理解。

二、实验内容(选其中之一书写实验报告)

选择合适的存储结构(顺序栈或链式栈)表示栈,给出其定义,在上述存储结构上实现栈的基本操作:初始化、置栈空、入栈、出栈、取栈顶元素等。然后完成下述任务之一:
(1) 用栈实现括号匹配的检验
(2) 用栈实现形如 a + b@b + a# 的中心对称的字符序列的检验
(3) 设计一个程序,演示用算符优先法对算术表达式求值的过程。以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用教科书表 3.1给出的算符优先关系,实现对算术四则运算混合运算表达式的求值,并仿照教科书的例子 3-1演示在求值中运算符栈、运算数栈、输入字符和主要操作的变化。测试数据可以选择例子 3-1 的算术表达式 3 ∗ (7 − 2),或自选。

三、问题描述

问题概况:栈实现括号匹配检验、栈实现中心对称字符检验、算符优先法实现四则混合运算表达式求值。

四、数据结构定义

采用栈的链式存储方式,数据元素类型字符型。

五、算法思想及算法设计

(实验代码部分没有中文注释,在此部分给出)

5.1 实验内容(1)

  • 题目:用栈实现括号匹配的检验。

思考:对于一个括号序列,不难想到匹配成功的条件是:当把它们一一放入栈内时,每时每刻都不能违背新栈顶的“意愿”。此“意愿”就是指,栈顶期待一个与之匹配的右括号,或者让步于一个更期待右括号的左括号。从栈底到栈顶的期待程度递增

5.1.1 代码实现

void Check(Stack& S) {
    char ch, x;
    int flag = 0;
    
    scanf("%c", &ch);
    Push(S, ch);
    
    if (ch != '(' && ch != '[') { 
        printf("NO"); 
        return; // 第一个括号必定是左括号
    }
    
    while (S != NULL || (flag == 0 && ch != ')' && ch != ']')) {
        scanf("%c", &ch);
        flag++;
        
        if (ch == '(' || ch == '[') { 
            Push(S, ch); 
            continue; // 只要是左括号就进栈
        } else if (ch == ')' || ch == ']') {
            if (GetTop(S) == '(' && ch == ')') { 
                Pop(S, x); 
                continue; // 遇到右小括号,那么观察栈顶是否为左小括号。若是,“满足期待”的栈顶出栈。栈顶变成新一个“最期待”的左括号
            } else if (GetTop(S) == '[' && ch == ']') { 
                Pop(S, x); 
                continue; // 同上,只是变成了右中括号
            } else {
                break; // 如果不能匹配,那么这个“凭空出现”的右括号使得整个括号序列不再匹配
            }
        }
    }
    
    if (S == NULL) {
        printf("YES"); // S=NULL 说明 while 循环完毕。
    } else {
        printf("NO"); // 否则,出现了不能匹配的右括号。
    }
}

5.2 实验内容(2)

  • 题目:用栈实现形如 a+b@b+a# 的中心对称的字符序列的检验。

思考:利用栈实现无非就是用“后进先出”的思想。观察式子,被‘@’分隔为两部分。我们可以先让前半部分进栈,然后在输入后半部分时与栈顶一一比较,不同则跳出返回“NO”,相同则继续输入比较,直到‘#’。

5.2.1 代码实现

Status Check2(Stack& S) {
    char ch, x;
    PUSH(S, '#'); // 因为式子末尾有‘#’,所以在栈底放置一个‘#’
    
    while (1) {
        scanf("%c", &ch);
        if (ch == '@') break; // 前半部分输入完退出
        PUSH(S, ch);
    }
    
    while (S != NULL) {
        scanf("%c", &ch);
        if (ch == GetTop(S)) {
            POP(S, x); // 如果相等,则出栈,方便下一对字符的比较
        } else {
            break; // 如果有一对不相等,退出。
        }
    }
    
    if (S == NULL) {
        printf("YES"); // 栈空说明比较完毕,对称
    } else {
        printf("NO"); // 不空,说明还有剩余,不对称
    }
    
    return OK;
}

5.3 实验内容(3)

  • 题目:设计一个程序,演示用算符优先法对算术表达式求值的过程。以字符序列的形式从终端输入语法正确的、不含变量的整数表达式。利用教科书表3.1给出的算符优先关系,实现对算术四则运算混合运算表达式的求值,并仿照教科书的例子 3-1 演示在求值中运算符栈、运算数栈、输入字符和主要操作的变化。测试数据可以选择例子 3-1 的算术表达式 3*(7-2),或自选。

思考:需要解决运算符间的优先关系,可利用教材表3.1完成precede函数来实现。另外,最核心的函数便是EvaluateExpression, 难点在于运算符间优先关系的不同会有进出栈操作的不同,容易混乱。

5.4 部分函数代码实现

  • <1>Precede 函数
char Precede(char theta1, char theta2) {
    if ((theta1 == '(' && theta2 == ')') || (theta1 == '#' && theta2 == '#')) {
        return '='; // 左右括号相遇表示其内运算已完成;两 # 相遇表示整个表达式求值完毕
    } else if (theta1 == '(' || theta1 == '#' || theta2 == '(' || 
               (theta1 == '+' || theta1 == '-') && (theta2 == '*' || theta2 == '/')) {
        return '<'; // theta1=='('或者'#'时都为‘<’('='情况已在 if 中);theta2=='('时都为'<'; 再把剩余几种情况加进去即可。
    } else {
        return '>'; // 不满足以上两种的,都是'>'关系
    }
}

  • <2>EvaluateExpression 函数
char EvaluateExpression() {
    Stack OPTR, OPND; // 运算符栈和操作数栈
    char ch, theta, a, b, x, top;
    
    InitStack(OPND);
    InitStack(OPTR);
    Push(OPTR, '#'); // 先放入一个 #, 以便和输入的表达式最后的 # 相匹配
    
    scanf("%c", &ch);
    while (ch != '#' || (GetTop(OPTR) != '#')) {
        if (Out(ch)) { // 判断是否为操作数
            Push(OPND, ch);
            scanf("%c", &ch);
        } else if (In(ch)) { // 判断是否为运算符
            switch (Precede(GetTop(OPTR), ch)) { // 判断栈顶和此运算符的优先关系
                case '<': // 栈顶优先级小于此运算符,将此运算符压入栈内
                    Push(OPTR, ch);
                    scanf("%c", &ch);
                    break;
                case '=':
                    Pop(OPTR, x); // 相等,则只出栈不进行其他操作
                    scanf("%c", &ch);
                    break;
                case '>': // 大于,那么将操作数栈最顶的两个拿出和运算符栈最顶的一个拿出,进行运算,结果放进操作数栈,需要注意的是两个操作数的先后关系。
                    Pop(OPTR, theta);
                    Pop(OPND, b);
                    Pop(OPND, a);
                    Push(OPND, Operate(a, theta, b));
                    break;
            }
        } else {
            printf("ERROR ! \n");
            ch = '0';
            continue;
        }
    }
    
    return GetTop(OPND);
}

六、运行示例

混合运算示例(实验内容3):
表达式:3*(6-2*(5+2))#
输出:-24

在这里插入图片描述
在这里插入图片描述

七、实验代码

  • 实验内容(1)-------括号匹配检验:
//FileName:code3.1.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef struct SqNode
{
	char data;
	struct SqNode* next;
}Sqnode,*Stack;

void InitStack(Stack& S)
{
	S = (Stack)malloc(sizeof(Sqnode));
	S = NULL;
}

int Push(Stack& S, char e)
{
    SqNode* p = (Stack)malloc(sizeof(SqNode));
    if (!p) return ERROR;
    p->data = e;
    p->next = S;
    S = p;
    return OK;
}
int Pop(Stack& S, char& e)
{
    SqNode* p;
    if (!S)return ERROR;
    e = S->data;
    p = S;
    S = S->next;
    free(p);
    p = NULL;
    return OK;
}
void DestroyStack(Stack& S)
{
    SqNode* p;
    while (S != NULL)
    {
        p = S;
        free(p);
        p = NULL;
        S = S->next;
    }
}
int GetTop(Stack& S)
{
    if (!S)return ERROR;
    return S->data;
}

void Check(Stack& S)
{
    char ch, x;
    int flag = 0;
    scanf("%c", &ch);
    Push(S, ch);
    if (ch != '(' && ch != '[') { printf("NO"); return; }
    while (S != NULL||(flag==0&&ch!=')'&&ch!=']'))
    {
        scanf("%c", &ch);
        flag++;
        if (ch == '(' || ch == '[') { Push(S, ch); continue;}
        else if (ch == ')'||ch==']')
        {
            if (GetTop(S) == '(' && ch == ')') { Pop(S, x); continue; }
            else if (GetTop(S) == '[' && ch == ']') { Pop(S, x); continue; }
            else break;
        }
    }
    if (S == NULL)printf("YES");
    else printf("NO");
}

int main()
{
    Stack S;
    InitStack(S);
    Check(S);
    DestroyStack(S);
}
  • 实验内容(2)------栈实现中心对称字符检验:
//FileName:code3.2.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1

typedef int Status;
typedef struct SqNode
{
	char data;
	struct SqNode* next;
}Sqnode ,*Stack;

void InitStack(Stack& S)
{
	S = NULL;
	return;
}

Status PUSH(Stack& S, char c)
{
	Sqnode* p = (Stack)malloc(sizeof(Sqnode));
	if (!p)return ERROR;
	p->data = c;
	p->next = S;
	S = p;
	return OK;
}

Status POP(Stack& S, char &e)
{
	Stack p;
	if (!S)return ERROR;
	e = S->data;
	p = S;
	S = S->next;
	free(p);
	p = NULL;
	return OK;
}

Status GetTop(Stack& S)
{
	if (!S)return ERROR;
	return S->data;
}

void DestroyStack(Stack& S)
{
	SqNode* p;
	while (S != NULL)
	{
		p = S;
		free(p);
		p = NULL;
		S = S->next;
	}
}

Status Check2(Stack& S)
{
	char ch, x;
	PUSH(S, '#');
	while (1)
	{
		scanf("%c", &ch);
		if (ch == '@')break;
		PUSH(S, ch);
	}
	while (S != NULL)
	{
		scanf("%c", &ch);
		if (ch == GetTop(S)) { POP(S, x); }
		else break;
	}
	if (S == NULL)printf("YES");
	else printf("NO");
	return OK;
}

int main()
{
	Stack S;
	InitStack(S);
	Check2(S);
	DestroyStack(S);
}
  • 实验内容(3)------算符优先法完成算术表达式求值
//FileName:code3.3.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

#define OK 1
#define ERROR 0

typedef struct SqNode
{
    char data;
    struct SqNode* next;
}SqNode, *Stack;

int InitStack(Stack& S) 
{
    S = NULL;
    return OK;
}
int Push(Stack& S, char e)
{
    SqNode* p = (Stack)malloc(sizeof(SqNode));
    if (!p) return ERROR;
    p->data = e;
    p->next = S;
    S = p;
    return OK;
}
int Pop(Stack& S, char& e)
{
    SqNode* p;
    if (!S)return ERROR;
    e = S->data;
    p = S;
    S = S->next;
    free(p);
    return OK;
}
int GetTop(Stack& S)
{
    if (!S)return ERROR;
    return S->data;
}
int In(char ch) 
{
    if (ch == '+' || ch == '-' || ch == '#' || ch == '*' || ch == '/' || ch == '(' || ch == ')')
        return 1;
    else return 0;
}
int Out(char ch) 
{
    if (ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9' || ch == '0')
        return 1;
    else return 0;
}

char Precede(char theta1, char theta2) 
{
    if ((theta1 == '(' && theta2 == ')') || (theta1 == '#' && theta2 == '#')) 
    {
        return '=';
    }
    else if (theta1 == '(' || theta1 == '#' || theta2 == '(' || (theta1 == '+' || theta1 == '-') && (theta2 == '*' || theta2 == '/'))
    {
        return '<';
    }
    else
        return '>';
}
char Operate(char a, char theta, char b) 
{
    switch (theta)
    {
    case '+':
        return (a - '0') + (b - '0') + 48;
    case '-':
        return (a - '0') - (b - '0') + 48;
    case '*':
        return (a - '0') * (b - '0') + 48;
    case '/':
        return (a - '0') / (b - '0') + 48;
    }
    return 0;
}

char EvaluateExpression() 
{
    Stack OPTR, OPND;
    char ch, theta, a, b, x, top;
    InitStack(OPND);
    InitStack(OPTR);
    Push(OPTR, '#');
    scanf("%c", &ch);
    while (ch != '#' || (GetTop(OPTR) != '#'))
    {
        if (Out(ch))
        {
            Push(OPND, ch);
            scanf("%c", &ch);
        }
        else if (In(ch)) 
            switch (Precede(GetTop(OPTR), ch))
            {
            case '<':
                Push(OPTR, ch);
                scanf("%c", &ch);
                break;
            case '=':
                Pop(OPTR, x);
                scanf("%c", &ch);
                break;
            case '>':
                Pop(OPTR, theta);
                Pop(OPND, b);
                Pop(OPND, a);
                Push(OPND, Operate(a, theta, b));
                break;
            }
        else 
        {
            printf("ERROR ! \n");
            ch = '0';
            continue;
        }
    }
    return GetTop(OPND);
}

int main()
{
    printf("Please input Arithmetic Expression(The End is #):\n");
    char x = EvaluateExpression();
    printf("Calculation result:%d",x-'0');
    return 0;
}

八、算法测试结果

  • 括号匹配:

在这里插入图片描述

  • 字符对称:

在这里插入图片描述

  • 算符优先:
    -输入:3*(6-2*(5+2))#——-> 输出:-24

在这里插入图片描述

九、分析与总结

9.1 算法复杂度分析及优、缺点分析

只有进出栈操作、运算操作,没有多重循环参与,所以时间复杂度均为O(n)。此代码结构清晰,容易理解,利用栈辅助操作,效率高;但是没有解决实验内容(3)在输入负数时也能准确得出结果的问题,是主要缺点。

9.2 实验总结

  1. 对栈“后进先出”思想进一步强化应用,本实验报告采用链栈方式,采用顺序栈也可解决问题。
  2. 实验时出现过多次“结果与实际不符”的问题,调试代码发现是 Precede 函数问题, 优先级关系较多,关系容易混乱。
  3. 总的思想就是与运算符栈顶元素比较优先级,比栈顶高,就压入栈;比栈顶低, 就出栈运算,结果放入操作数栈;二者相等,栈顶出栈,不进行其他操作,继续输入比较。如此,直到表达式结束。
  4. 题目要求是整数表达式,本实验未解决含浮点型的算术表达式求值问题。以字符型输入为前提,初步思考是以小数点为界限,前后分别计算,然后整合,详细做法还需进一步讨论。

ps:emmmm…算符优先??这不编译原理嘛…当时好像还没开这门课…

  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值