BUAA数据结构期末考试(21级)

本文详细解析了三道数据结构相关的编程题目,包括计算汉明距离、构建和遍历二叉搜索树以找出出现次数最多的整数,以及实现一个简单的解释系统。每道题目都提供了问题分析、处理过程和完整的C语言代码示例,涵盖了字符串操作、树的构建和遍历、表达式计算等多个方面。
摘要由CSDN通过智能技术生成

BUAA数据结构期末考试(21级)

1. 汉明距离

题目

问题描述

在信息论中,两个等长字符串之间的汉明距离(Harmming Distance)是指两个字符串中对应位置的不 同字符的个数(区分大小写)。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字 符个数。例如:10110101与11011101的汉明距离为3,roses与cotes的汉明距离为2。从标准输入中输 入一组等长字符串,以第一个字符串为基准,统计其与其它字符串的汉明距离,并按汉明距离由小至大 输出字符串对及其汉明距离。

输入形式

首先从标准输入读入字符串的个数n(2<=n<=16),然后分行读入n个等长的字符串(长度小于等于 16)。

输出形式

将结果输出到标准输出,具体要求如下:

  1. 以第一个字符串为基准,按汉明距离由小至大的顺序分行输出字符串对及其汉明距离;字符串及其汉 明距离间都以一个空格分隔。
  2. 若汉明距离相同,则按照字符串对中第二个字符串的字典序(由小到大)输出;由于要区分大小写, 故字典序是指字符的ASCII码序。
样例输入
5
roses
cotes
Roses
coset
rotes
样例输出
roses Roses 1
roses rotes 1
roses coset 2
roses cotes 2
样例说明

输入了5个长度为5的字符串;第一个字符串roses与其它字符串的汉明距离为1或2;先输出汉明距离为1 的字符串对,而且按字典序Roses小于rotes,故先输出roses和Roses的汉明距离。

评分标准

题目要求计算输出第一个字符串与其它字符串之间的汉明距离,提交程序名为hamming.c

问题分析

本题比较简单,大体上需要我们实现这几个功能:

  1. 给定两个等长字符串,计算其汉明距离;
  2. 用合适的结构存储第2-n个数据的字符串以及它和第一个读入的字符串的汉明距离,并排序。

第一个问题计算汉明距离就按照题目要求来就行,第二个问题很显然用结构体数组存储就行,然后qsort排序即可。

具体处理过程

首先定义结构体,并开数组:

// 主函数外
struct result {
    int distance;
    char word[20];
};
struct result result[20];  // 存储结果

主函数中的程序框架比较清晰,应当如下:

int main()
{
    int n;
    scanf("%d", &n);

    char standard_string[20];  // 第一个字符串
    char tmp_string[20];  // 后面的字符串的缓冲区
    scanf("%s", standard_string);  // 先把第一个字符串读进来

    for (int i = 0; i < n - 1; i++) {  // 注意i不是从0开始了(第一个字符串已经被读进来了)
        scanf("%s", tmp_string);  // 把字符串读到缓冲区
        result[i].distance = calculate(standard_string, tmp_string);  // 计算该字符串和第一个字符串之间的汉明距离
        strcpy(result[i].word, tmp_string);
    }

    qsort(result, n - 1, sizeof(result[0]), cmp);  // 排序
    for (int i = 0; i < n - 1; i++) {  // 打印输出
        printf("%s %s %d\n", standard_string, result[i].word, result[i].distance);
    }
    return 0;
}

calculate函数实现如下:

int calculate(char *s1, char *s2)  // 计算两等长字符串的汉明距离
{
    int res = 0;
    for (int i = 0; s1[i]; i++) {
        if ((s1[i] != s2[i])) {
            res++;
        }
    }
    return res;
}

cmp函数按照题目要求的规则去设计就行:

int cmp(const void *e1, const void *e2)
{
    struct result *a = (struct result*) e1;
    struct result *b = (struct result*) e2;
    if (a -> distance != b -> distance) {
        return a -> distance - b -> distance;
    }
    return strcmp(a -> word, b -> word);
}   

完整代码

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

struct result {
    int distance;
    char word[20];
};
struct result result[20];  // 存储结果

int calculate(char *s1, char *s2);
int cmp(const void *e1, const void *e2);


int main()
{
    int n;
    scanf("%d", &n);

    char standard_string[20];  // 第一个字符串
    char tmp_string[20];  // 后面的字符串的缓冲区
    scanf("%s", standard_string);  // 先把第一个字符串读进来

    for (int i = 0; i < n - 1; i++) {  // 注意i不是从0开始了(第一个字符串已经被读进来了)
        scanf("%s", tmp_string);  // 把字符串读到缓冲区
        result[i].distance = calculate(standard_string, tmp_string);  // 计算该字符串和第一个字符串之间的汉明距离
        strcpy(result[i].word, tmp_string);
    }

    qsort(result, n - 1, sizeof(result[0]), cmp);  // 排序
    for (int i = 0; i < n - 1; i++) {  // 打印输出
        printf("%s %s %d\n", standard_string, result[i].word, result[i].distance);
    }
    return 0;
}


int calculate(char *s1, char *s2)  // 计算两等长字符串的汉明距离
{
    int res = 0;
    for (int i = 0; s1[i]; i++) {
        if ((s1[i] != s2[i])) {
            res++;
        }
    }
    return res;
}


int cmp(const void *e1, const void *e2)
{
    struct result *a = (struct result*) e1;
    struct result *b = (struct result*) e2;
    if (a -> distance != b -> distance) {
        return a -> distance - b -> distance;
    }
    return strcmp(a -> word, b -> word);
}   

2. 二叉搜索树

题目

问题描述

编写程序读入一组整数,按输入顺序构造一棵二叉查找树(BST树)来查找并统计相应整数的出现次 数,BST树的构造规则如下:

  1. 在输入整数过程中按照左子结点值小于根结点值、右子结点值大于根结点值的方式构造一棵BST树;
  2. 输入的整数等于BST树中某结点值时,该结点的出现次数加1。

示例详见下面的样例。 对于最终构建的BST树,要求统计如下数据:

  1. 在BST树的创建及查找整数过程中总的比较次数,仅统计输入的整数与结点值进行比较的次数,不统 计指针的判断次数;
  2. 出现次数最多的整数的比较路径;比较路径是指BST树中从根结点到该节点的路径;若出现次数最多 的整数有多个,则只输出按照前序遍历次序访问的第一个出现次数最多的整数的比较路径。
输入形式

先从控制台输入整数的个数n(大于等于1),然后在下一行输入n个整数,以一个空格分隔各个整数。

输出形式

先向控制台输出在BST树的创建及查找整数过程中总的比较次数,然后在下一行输出按照前序遍历次序 访问的第一个出现次数最多的整数的比较路径,用该路径上各结点对应的整数表示,各整数间以一个空 格分隔,最后一个整数后有无空格均可。

样例输入
12
670 1360 1871 921 128 1871 57 -200 1003 552 -200 57
样例输出
26
670 128 57
样例说明

输入了12个整数,根据上述构造规则,建立的BST树如下图所示:

在这里插入图片描述

其中结点中的值为输入的整数值,结点下方的数值为该整数出现的次数。 开始BST树为空,第一个整数670作为根结点,没有与结点值进行比较,比较次数为0;第二个整数1360 与结点670比较了1次;第三个整数1871与结点670和结点1360各比较了1次;第六个整数1871与结点 670、1360和1871各比较了1次;其它整数与上述整数类同;在该BST树的创建及查找整数过程中总的比 较次数为26次。 在该BST树中,出现次数最多的整数有三个,分别为1871、57和-200,其中按照前序遍历次序访问到的 第一个为57,所以要输出结点57的比较路径,即从根结点670到结点57的路径。

评分标准

该题要求根据输入的整数构建BST树,并统计总的比较次数和出现次数最多的整数的比较路径,提交程 序名为bst.c。

问题分析

本题要我们做的可以归结为这三件事:

  1. 建造bst树,并记录建造过程中的比较次数;
  2. 找到前序序列中第一个出现的词频最多的单词;
  3. 输出上述结点的路径(从根结点到该结点所经历的结点)

第一个问题建树是第五次作业前两道题着重考察过的,具体可以参考这篇文章;对第二个问题,我们只需要在建树的时候就记录下来最多出现的单词的次数,然后前序遍历时,当找到一个结点的单词出现次数和它相等时,就找到了该结点,接下来不再遍历了;对第三个问题,可以有很多思路去解决,其实最容易想到的思路应当是用三叉链表建树(即多了一个指向双亲结点的指针),输出时可以顺藤摸瓜一路找到根结点。

具体处理过程

我选择用三叉链表来建树(方便最后输出),所以声明结点如下:

typedef struct node{
    int data_num;  // 数据域
    int occurence_number;  // 出现次数
    struct node *lchild;
    struct node *rchild;
    struct node *parent;  // 双亲结点的地址
} node;

我们还需要这些全局变量:记录总的比较次数的变量,以及记录树种出现次数最多的单词的出现次数(套娃?),所以定义全局变量如下:

int cmp_time;  // 总比较次数
int max_time;  // 最多次数

主函数中的框架非常清晰:读数据,建树,找到目标结点,打印该结点的路径,代码如下:

int main()
{
    max_time = 1;
    node *root = NULL;
    int n;
    int tmp;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &tmp);
        root = insert(root, NULL, tmp);  // 插入树
    }
    printf("%d\n", cmp_time);
    node *target;
    preorder(root, &target);  // 找到目标结点
    print_path(target);  // 打印该结点路径
    
    return 0;
}

insert函数可以参考第五次作业(上面那篇超链接文章),稍微改动的地方是我们现在用三叉链表去建树,所以每次除了把根结点传进函数外,还要把根结点的双亲结点传进去,实现如下:

node* insert(node *root, node *par, int index)
{
    if (root == NULL) {
        root = (node*) malloc(sizeof(node));
        root -> data_num = index;
        root -> occurence_number = 1;
        root -> lchild = root -> rchild = NULL;
        root -> parent = par;
        return root;
    }
    // 接下来的每一种情况都要把cmp_num加一
    if (index < root -> data_num) {
        cmp_time++;
        root -> lchild = insert(root -> lchild, root, index);
    } else if (index > root -> data_num) {
        cmp_time++;
        root -> rchild = insert(root -> rchild, root, index);
    } else {
        cmp_time++;
        root -> occurence_number++;
        if (root -> occurence_number > max_time) {
            max_time = root -> occurence_number;
        }
    }
    return root;
}

preorder函数我习惯用递归去实现(简单啊),但是我们如何能实现找到第一个结点后不再去找别的结点了呢?由于这是递归函数,所以直接再函数中return并不能结束所有的函数进程,所以我选择设置一个全局变量flag,初始值为0,当遍历时找到目标结点后把其置1,每次调用函数时先判断一下是否已经找到目标了(flag是否等于1),代码如下:

void preorder(node *root, node **target)
{
    if (!flag) {
        if (root -> occurence_number == max_time) {
            flag = 1;
            *target = root;
        }
        if (root -> lchild != NULL) {
            preorder(root -> lchild, target);
        }
        if (root -> rchild != NULL) {
            preorder(root -> rchild, target);
        }
    }
}

print_path函数就简单了——因为我们用的是三叉链表嘛,直接递归打印就好:

void print_path(node *target)
{
    if (target == NULL) return;
    print_path(target -> parent);
    printf("%d ", target -> data_num);
}

完整代码

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

typedef struct node{
    int data_num;  // 数据域
    int occurence_number;  // 出现次数
    struct node *lchild;
    struct node *rchild;
    struct node *parent;  // 双亲结点的地址
} node;

int cmp_time;  // 总比较次数
int max_time;  // 最多次数
int flag;

node* insert(node *root, node *par, int index);
void preorder(node *root, node **target);
void print_path(node *target);

int main()
{
    max_time = 1;
    node *root = NULL;
    int n;
    int tmp;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &tmp);
        root = insert(root, NULL, tmp);  // 插入树
    }
    printf("%d\n", cmp_time);
    node *target;
    preorder(root, &target);  // 找到目标结点
    print_path(target);  // 打印该结点路径
    
    return 0;
}


node* insert(node *root, node *par, int index)
{
    if (root == NULL) {
        root = (node*) malloc(sizeof(node));
        root -> data_num = index;
        root -> occurence_number = 1;
        root -> lchild = root -> rchild = NULL;
        root -> parent = par;
        return root;
    }
    // 接下来的每一种情况都要把cmp_num加一
    if (index < root -> data_num) {
        cmp_time++;
        root -> lchild = insert(root -> lchild, root, index);
    } else if (index > root -> data_num) {
        cmp_time++;
        root -> rchild = insert(root -> rchild, root, index);
    } else {
        cmp_time++;
        root -> occurence_number++;
        if (root -> occurence_number > max_time) {
            max_time = root -> occurence_number;
        }
    }
    return root;
}


void preorder(node *root, node **target)
{
    if (!flag) {
        if (root -> occurence_number == max_time) {
            flag = 1;
            *target = root;
        }
        if (root -> lchild != NULL) {
            preorder(root -> lchild, target);
        }
        if (root -> rchild != NULL) {
            preorder(root -> rchild, target);
        }
    }
}


void print_path(node *target)
{
    if (target == NULL) return;
    print_path(target -> parent);
    printf("%d ", target -> data_num);
}

思路二

上面的代码是用三叉链表实现打印结点路径的。当然,用二叉链表(即课件上我们熟悉的方式)也没问题,只需要在前序遍历函数进行时动态地记录到当前为止的路径,并且在找到第一个出现次数最多的结点时,给一个全局变量赋值,记录路径有多少位即可,代码如下:

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

typedef struct node{
    int data_num;  // 数据域
    int occurence_number;  // 出现次数
    struct node *lchild;
    struct node *rchild;
} node;

int cmp_time;  // 总比较次数
int max_time;  // 最多次数
int flag;
int path[20];  // 路径
int path_len;  // 路径长度


node* insert(node *root, int index);
void preorder(node *root, int height);
void print_path(node *target);

int main()
{
    max_time = 1;
    node *root = NULL;
    int n;
    int tmp;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &tmp);
        root = insert(root, tmp);  // 插入树
    }
    printf("%d\n", cmp_time);
    preorder(root, 0);  // 找到目标结点

    for (int i = 0; i < path_len; i++) {  // 输出路径前path_len位
        printf("%d ", path[i]);
    }
    return 0;
}


node* insert(node *root, int index)
{
    if (root == NULL) {
        root = (node*) malloc(sizeof(node));
        root -> data_num = index;
        root -> occurence_number = 1;
        root -> lchild = root -> rchild = NULL;
        return root;
    }
    // 接下来的每一种情况都要把cmp_num加一
    if (index < root -> data_num) {
        cmp_time++;
        root -> lchild = insert(root -> lchild, index);
    } else if (index > root -> data_num) {
        cmp_time++;
        root -> rchild = insert(root -> rchild, index);
    } else {
        cmp_time++;
        root -> occurence_number++;
        if (root -> occurence_number > max_time) {
            max_time = root -> occurence_number;
        }
    }
    return root;
}


void preorder(node *root, int height)
{
    if (!flag) {
        if (root == NULL) return;  // 遍历到头了

        path[height] = root -> data_num;
        if (root -> occurence_number == max_time) {  // 找到目标结点了
            flag = 1;
            path_len = height + 1;
            return;
        } 

        preorder(root -> lchild, height + 1);
        preorder(root -> rchild, height + 1);
    } 
}

3. 解释系统

题目

问题描述

有一min解释语言,其只有整型常量、变量、赋值语句、算术表达式语句及打印语句组成。编写一程 序,实现该系统。 规则:

  1. 变量仅由单个小写字母组成。

  2. 只有三种语句:print、exit和赋值语句。一行只有一个语句,语句中的字符个数不会超过200,每 条语句的末尾都有换行符。 语句格式为:

    ​ a) 赋值语句:<变量> = <算术表达式>。<算术表达式>是由十进制整型常量(末尾不带后缀)、变 量、算术运算符(+, -, *, /)及 小括号组成;赋值语句中没有空白符。

    ​ b) print语句:print <变量列表>。<变量列表>为由空格分隔的变量序列,print和变量间由空格分 隔。功能是输出变量列表中各变量 的值。

    ​ c) exit语句:exit。退出解释系统。

  3. 在进行赋值运算、算术运算、打印输出时,相应变量都会有具体值,不会出现语法错误。在计算过 程中结果数据类型为浮点型,输出时保留小数点后2位。

输入形式

从标准输入读入解释系统的待执行语句,每条语句独占一行;最后一条语句为exit。注意:所有输入中 出现的常量都是不带后缀的十进制整数常量。

输出形式

在解释系统执行过程中,每条print语句执行后,print后各变量的值将会输出到标准输出,输出时保留小 数点后2位,各数据间以一个空格分隔,最后一个数据后没有空格,有换行符。

样例输入与输出
a=10
b=20
c=(a+b)/4
print a b c
10.00 20.00 7.50
d=a*(b-c)
print d
125.00
exit
样例说明

第一条语句将10赋值给变量a;第二条语句将20赋值给变量b;第三条语句需要先计算表达式(a+b)/4的 值,然后将结果赋值给变量c,这时c的值为7.50;第四条语句要打印变量a、b和c的值,于是下一行便会 输出当前a、b和c的值,分别为10.00、20.00和7.50;第五条语句需要先计算表达式a*(b-c)的值,然后 将结果赋值给变量d,这时d的值为125.00;第六条语句要打印变量d的值,于是在下一行输出125.00; 第七条语句为exit,执行后退出解释系统。

评分标准

该题要求实现解释系统的语句读入和执行,提交程序名为inter.c

问题分析

本题想起来挺复杂,但是题目已经帮我们简化了很多了:

  1. 变量只由单个小写字母表示,故变量值可以用一个26位数组去存储,取变量值时直接用下标索引到就行;
  2. 一共只有三种语句,而且特点鲜明,加上题目保证了语句没有空格,故容易判断。比如将一条语句以字符串形式读到缓冲区后,赋值语句的特点就是第二个字符是’=';exit语句就更不用说了,判断strcmp(buf, "exit")就行;而剩下的语句自然就是print语句了!

综上所述,本题看起来花里胡哨,但核心问题仍是实现计算器,和作业题不同的是本题的表达式中会出现变量,所以用栈将表达式转化为后缀表达式时遇到单个小写字母字符时,要取其值当做浮点数来处理。

具体处理过程

在全局变量中开一个double型的数组,记录a-z变量的值,对任意一个变量ch,其值应为variable[ch - 'a'],同时,按照求表达式值的逻辑,声明结构与一系列变量(可以参考第四次作业):

typedef struct {
    int type;  // 记录是数还是操作符的标识。0为数,1为操作符
    union {
        double num;
        char op;
    } data;
} node;

node representation[100];  // 记录后缀表达式
char opera[100];  // 操作符缓冲区
double variable[26];  // 记录变量的值

主函数中的框架也十分简单:

int main()
{
    char buf[100];  // 每一行语句的缓冲区
    while (gets(buf) != NULL) {
        if (buf[1] == '=') {  // 是赋值语句
            // 计算表达式的值,并赋值给变量
            int rep_num = change(buf + 2);
            variable[buf[0] - 'a'] = calculate(buf + 2, rep_num);
        } else if (strcmp(buf, "exit") == 0) {   // 退出
            break;
        } else {  // print语句
            for (int i = 6; buf[i]; i++) {
                if (buf[i] >= 'a' && buf[i] <= 'z') {
                    printf("%.2lf ", variable[buf[i] - 'a']);
                }
            }
            printf("\n");
        }
    }
    return 0;
}

change函数实现将中缀表达式转化为后缀表达式,这里的操作过程和上面那篇文章中所写的唯一不同就在于本题表达式中可能有变量,需要对这种情况特判一下:

int change(char *str)  // 转化为后缀表达式
{
    int rep_num = 0;  // 记录表达式有多少位
    int op_num = 0;
    int tmp = 0;  // 暂存数字
    int flag = 0;  // 当前是否是数字的标识

    for (int i = 0; str[i]; i++) {
        if (str[i] >= '0' && str[i] <= '9') {  // 当前这位是数字
            if (flag) {  // 前面已经有数字了
                tmp = tmp * 10 + str[i] - '0';
            } else {
                flag = 1;
                tmp = str[i] - '0';
            }
        } else if (str[i] >= 'a' && str[i] <= 'z') {  // 当前这位是变量
            representation[rep_num].type = 0;
            representation[rep_num++].data.num = variable[str[i] - 'a'];
        } else {  // 当前这位是操作符
            if (flag) {  // 如果前面是数字
                representation[rep_num].type = 0;
                representation[rep_num++].data.num = tmp;
                flag = 0;
                tmp = 0;               
            }

            //左右括号特判
            if(str[i]=='('){
                opera[op_num++] = str[i];
                continue;
            }
            if(str[i] == ')'){
                while(opera[op_num - 1] != '(') {
                    representation[rep_num].type = 1;
                    representation[rep_num++].data.op = opera[op_num - 1]; 
                    op_num--;
                }
                op_num--;
                continue;
            }

            // 当前位是加减整除运算符,先将opera中优先级高于自己的操作符出栈
            while(op_num != 0 && ishigh(opera[op_num - 1], str[i]) && opera[op_num-1] != '(') {
                // ishigh函数判断加减乘除四个运算符的优先级
                representation[rep_num].type = 1;
                representation[rep_num++].data.op = opera[op_num - 1];
                op_num--;
            }
            opera[op_num++] = str[i];//该操作符入栈
        }
    }
    
    if (flag) {  // 如果前面是数字,输出出来
        representation[rep_num].type = 0;
        representation[rep_num++].data.num = tmp;
        flag = 0;
        tmp = 0;               
    }
    // 最后将栈清空输出到逆波兰表达式中
    for(int i = op_num - 1; i >= 0; i--) {
        representation[rep_num].type = 1;
        representation[rep_num++].data.op = opera[i];
    }
    return rep_num;   
}

ishigh函数实现比较两运算符的优先级,实现如下:

int ishigh(char c1,char c2)  //判断加减乘除运算符的优先级
{
    int a1,a2;
    if(c1=='+'||c1=='-')a1=0;
    else a1 = 1;
    if(c2=='+'||c2=='-')a2=0;
    else a2 = 1;
    if(a1>=a2)return 1;
    return 0;
}

然后计算后缀表达式的值,和作业题无异,代码如下:

double calculate(char *str, int rep_num)  // 计算表达式的值
{
    //求解逆波兰表达式
    double stack[1024] = {0};  //栈
    int stack_num = 0;
    for(int j = 0; j < rep_num; j++) {
        if(representation[j].type == 0) {  //该位是数字,直接入栈
            stack[stack_num++] = representation[j].data.num;
        }else {  //该位是操作符
            switch (representation[j].data.op){
                case '+':
                    stack[stack_num-2] = stack[stack_num-2] + stack[stack_num-1];
                    stack_num--;
                    break;
                case '-':
                    stack[stack_num-2] = stack[stack_num-2] - stack[stack_num-1];
                    stack_num--;
                    break;
                case '*':
                    stack[stack_num-2] = stack[stack_num-2] * stack[stack_num-1];
                    stack_num--;
                    break;  
                case '/':
                    stack[stack_num-2] = stack[stack_num-2] / stack[stack_num-1];
                    stack_num--;
                    break;           
            }
        }
    }
    return stack[0];
}

完整代码

# include <stdio.h>
# include <string.h>

typedef struct {
    int type;  // 记录是数还是操作符的标识。0为数,1为操作符
    union {
        double num;
        char op;
    } data;
} node;

node representation[100];  // 记录后缀表达式
char opera[100];  // 操作符缓冲区
double variable[26];  // 记录变量的值

int ishigh(char c1,char c2);
int change(char *str);
double calculate(char *str, int rep_num);


int main()
{
    char buf[100];  // 每一行语句的缓冲区
    while (gets(buf) != NULL) {
        if (buf[1] == '=') {  // 是赋值语句
            // 计算表达式的值,并赋值给变量
            int rep_num = change(buf + 2);
            variable[buf[0] - 'a'] = calculate(buf + 2, rep_num);
        } else if (strcmp(buf, "exit") == 0) {   // 退出
            break;
        } else {  // print语句
            for (int i = 6; buf[i]; i++) {
                if (buf[i] >= 'a' && buf[i] <= 'z') {
                    printf("%.2lf ", variable[buf[i] - 'a']);
                }
            }
            printf("\n");
        }
    }
    return 0;
}


int ishigh(char c1,char c2)  //判断加减乘除运算符的优先级
{
    int a1,a2;
    if(c1=='+'||c1=='-')a1=0;
    else a1 = 1;
    if(c2=='+'||c2=='-')a2=0;
    else a2 = 1;
    if(a1>=a2)return 1;
    return 0;
}


int change(char *str)  // 转化为后缀表达式
{
    int rep_num = 0;  // 记录表达式有多少位
    int op_num = 0;
    int tmp = 0;  // 暂存数字
    int flag = 0;  // 当前是否是数字的标识

    for (int i = 0; str[i]; i++) {
        if (str[i] >= '0' && str[i] <= '9') {  // 当前这位是数字
            if (flag) {  // 前面已经有数字了
                tmp = tmp * 10 + str[i] - '0';
            } else {
                flag = 1;
                tmp = str[i] - '0';
            }
        } else if (str[i] >= 'a' && str[i] <= 'z') {  // 当前这位是变量
            representation[rep_num].type = 0;
            representation[rep_num++].data.num = variable[str[i] - 'a'];
        } else {  // 当前这位是操作符
            if (flag) {  // 如果前面是数字
                representation[rep_num].type = 0;
                representation[rep_num++].data.num = tmp;
                flag = 0;
                tmp = 0;               
            }

            //左右括号特判
            if(str[i]=='('){
                opera[op_num++] = str[i];
                continue;
            }
            if(str[i] == ')'){
                while(opera[op_num - 1] != '(') {
                    representation[rep_num].type = 1;
                    representation[rep_num++].data.op = opera[op_num - 1]; 
                    op_num--;
                }
                op_num--;
                continue;
            }

            // 当前位是加减整除运算符,先将opera中优先级高于自己的操作符出栈
            while(op_num != 0 && ishigh(opera[op_num - 1], str[i]) && opera[op_num-1] != '(') {
                // ishigh函数判断加减乘除四个运算符的优先级
                representation[rep_num].type = 1;
                representation[rep_num++].data.op = opera[op_num - 1];
                op_num--;
            }
            opera[op_num++] = str[i];//该操作符入栈
        }
    }
    
    if (flag) {  // 如果前面是数字,输出出来
        representation[rep_num].type = 0;
        representation[rep_num++].data.num = tmp;
        flag = 0;
        tmp = 0;               
    }
    // 最后将栈清空输出到逆波兰表达式中
    for(int i = op_num - 1; i >= 0; i--) {
        representation[rep_num].type = 1;
        representation[rep_num++].data.op = opera[i];
    }
    return rep_num;   
}


double calculate(char *str, int rep_num)  // 计算表达式的值
{
    //求解逆波兰表达式
    double stack[1024] = {0};  //栈
    int stack_num = 0;
    for(int j = 0; j < rep_num; j++) {
        if(representation[j].type == 0) {  //该位是数字,直接入栈
            stack[stack_num++] = representation[j].data.num;
        }else {  //该位是操作符
            switch (representation[j].data.op){
                case '+':
                    stack[stack_num-2] = stack[stack_num-2] + stack[stack_num-1];
                    stack_num--;
                    break;
                case '-':
                    stack[stack_num-2] = stack[stack_num-2] - stack[stack_num-1];
                    stack_num--;
                    break;
                case '*':
                    stack[stack_num-2] = stack[stack_num-2] * stack[stack_num-1];
                    stack_num--;
                    break;  
                case '/':
                    stack[stack_num-2] = stack[stack_num-2] / stack[stack_num-1];
                    stack_num--;
                    break;           
            }
        }
    }
    return stack[0];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值