BUAA数据结构第四次作业2023

BUAA数据结构第四次作业2023

1. 栈操作(栈-基本题)

问题

问题描述

假设给定的整数栈初始状态为空,栈的最大容量为100。从标准输入中输入一组栈操作,按操作顺序输出出栈元素序列。栈操作:1表示入栈操作,后跟一个整数**(不为1、0和-1)为入栈元素**;0表示出栈操作;-1表示操作结束。

输入形式

从标准输入读取一组栈操作,入栈的整数和表示栈操作的整数之间都以一个空格分隔。

输出形式

在一行上按照操作的顺序输出出栈元素序列,以一个空格分隔各元素,最后一个元素后也要有一个空格。如果栈状态为空时进行出栈操作,或栈满时进行入栈操作,则输出字符串“error”,并且字符串后也要有一空格。所有操作都执行完后,栈也有可能不为空。

样例输入

1 3 1 5 1 7 0 0 1 8 0 1 12 1 13 0 0 0 0 1 90 1 89 0 -1

样例输出

7 5 8 13 12 3 error 89

样例说明

入栈元素依次为3、5、7,然后有两次出栈动作,所以先输出7和5,这时栈中只有元素3;之后元素8入栈,又出栈,输出8;随后元素12和13入栈,再进行4次出栈操作,输出13、12和3,这时栈为空,再进行出栈操作会输出error;最后90和89入栈,进行一次出栈操作,输出89,栈中剩余1个元素。

评分标准

该题要求按照操作的顺序输出出栈元素序列,提交程序名为stack.c。

问题分析

如题,本题是栈的基本题,不做解释,直接给出代码:

完整代码

#include <stdio.h>

typedef struct stack{
    int top;
    int data[100];
} stack;

int main()
{
    stack s;
    s.top = 0;
    int op;
    while(scanf("%d",&op)!=EOF){
        if(op==-1)break;
        if(1==op){
            if(s.top==100)
                printf("error ");
            else                 
                scanf("%d",&s.data[s.top++]);
        }else if(op==0){
            if(s.top==0)
                printf("error ");
            else
                printf("%d ",s.data[--s.top]);
        }
    }

    return 0;
}

2. C程序括号匹配检查

问题

问题描述

编写一程序检查C源程序文件中{}、()等括号是否匹配,并输出第一个检测到的不匹配的括号及所对应括号所在的行号(程序中只有一个括号不匹配)。

注意:

  1. 除了括号可能不匹配外,输入的C源程序无其它语法错误
  2. 字符常量、字符串常量及注释中括号不应被处理,注释包括*单行注释//多行/* /注释
  3. 字符常量和字符串常量中不包含转义字符’和"
  4. 程序中出现有意义括号的个数不超过200个;

不匹配判断原则:

  1. 当检测的程序括号为’{‘时,若其前序尚未匹配的括号为’(‘时,输出该’('左括号及所在行号;
  2. 当遇到一个不匹配的右括号’)‘或’}'时,输出该右括号及所在行号;
  3. 当程序处理完毕时,还存在不匹配的左括号时,输出该左括号及所在行号。
输入形式

打开当前目录下文件example.c,查询其括号是否匹配。该文件中每行字符数不超过200

输出形式

若存在括号不匹配时,应输出首先能判断出现不匹配的括号及其所在的行号。当出现括号不匹配时,按下面要求输出相关信息:
without maching at line
其中为‘{’, ‘}’, ‘(’, ‘)’等符号,为该符号所在的行号。
若整个程序括号匹配,则按下面所示顺序输出括号匹配情况,中间没有空格。
(){(()){}}

样例输入1

若当前目录下输入文件example.c中内容如下:

若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf(“{ hello world }\n”); // }
)

样例输出1

without maching ‘)’ at line 4

样例输入2

若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf(“{ hello world }d\n”); /* }*/

样例输出2

without maching ‘{’ at line 2

####样例输入3

若当前目录下输入文件example.c中内容如下:
#include<stdio.h>
int main(){
printf(“{ hello world }d\n”); /* }*/
}

样例输出3

(){()}

样例说明

样例1:在注释部分和字符串中的括号不考虑,在将程序处理之后得到的括号序列是(){()),遇到右括号时与最近的左括号匹配,发现最后一个小括号和大括号不匹配。
样例2:处理之后的括号序列是(){(),在最后缺少了右大括号,那么应该输出与之相对应的左括号不匹配。

评分标准

通过所有测试点得满分。

问题分析

本题最核心的思路显然要靠栈来实现:把程序中有效的(非字符串、注释等中的)括号存储起来,每入栈一个括号,就判断一下它是否是和其最近的括号相匹配,若是,则令栈顶元素出栈。

思路虽简单,但具体处理时,有以下这几个细节需要处理:

  1. 记录各个括号所在行数。这一点可以靠结构体来实现:定义一个结构数组作为栈,数组中每一个元素有两个域,分别记录括号和所在的行数;
  2. 对程序中注释、字符、字符串特判,忽略其中的括号。对此可以用一个变量记录下当前的状态;
  3. 如果程序中括号没有问题,需要输出有效括号的序列。如果我们在操作中就进行出栈的操作,最后就无法记录原始的序列了。这里可以再另开一个字符数组把有效的括号都记录下来,如果需要,最后直接打印这个序列即可。

具体实现过程

首先进行结构的定义与变量的初始化:

#include <stdio.h>

typedef struct node {
    char data;
    int row_num;
} node;

typedef struct stack {
    node order[512];
    int top;
} stack;

int main()
{

    stack s;
    s.top = 0;
    int cur_num = 0;//记录当前行数
    char all[512] = {0};//记录完整的有效括号

    FILE* in = fopen("example.c", "r");
    char exc[512] = {0};//暂存每行的字符
    int flag = 0;//记录当前状态。0表示正常匹配状态,1表示单字符状态,2表示字符串状态,3表示多行注释状态
    while(fgets(exc,512,in) != NULL){
        cur_num++;
        
    }
    return 0;
}

接下来在主函数中判断当前状态,并做对应输出:

while(fgets(exc,512,in) != NULL){
        if(out)break;
        cur_num++;
        for(int i=0;exc[i]!='\0';i++){
            if(flag!=0){//当前非正常匹配状态
                if(flag==1&&exc[i]=='\''){//单字符状态
                    flag = 0;
                    continue;
                }
                if(flag==2&&exc[i]=='\"'){//字符串状态
                    flag = 0;
                    continue;
                }
                if(flag==3&&exc[i]=='*'&&exc[i+1]=='/'){//多行注释状态
                    flag = 0;
                    i++;
                    continue;
                }
                //不改变状态,无效字符,跳过
                continue;
            }

            //正常状态,开始匹配
            //匹配左括号
            if(exc[i]=='('||exc[i]=='['||exc[i]=='{'){
                node temp;
                temp.data = exc[i];
                temp.row_num = cur_num;
                all[all_num++] = exc[i];
                s.order[s.top++] = temp;
            }

            //匹配右括号
            if(exc[i]==')'||exc[i]==']'||exc[i]=='}'){
                all[all_num++] = exc[i];
                if(ismatch(s.order[s.top-1].data,exc[i])){
                    s.top--;
                }
                else {
                    printf("without maching \'%c\' at line %d",exc[i],cur_num);
                    out = 1;
                    break;
                }
            }
            
            //匹配改变状态的字符
            if(exc[i]=='\''){
                flag = 1;
                continue;
            }
            if(exc[i]=='\"'){
                flag = 2;
                continue;
            }
            if(exc[i]=='/'&&exc[i+1]=='*'){
                flag = 3;
                i++;
                continue;
            }
            if(exc[i]=='/'&&exc[i+1]=='/'){
                break;
            }
        }

    }
    if(!out){
        if(s.top!=0){
            printf("without maching \'%c\' at line %d",s.order[s.top-1].data,s.order[s.top-1].row_num);
        }
        else {
            printf("%s",all);
        }
    }

但是这样处理后交上去有两个点没过。这是因为根据C语言的语法规则,在小括号“()”中不可能出现大括号“{}”,也即题面中“不匹配判断规则”中所说的,所以对左括号匹配改写如下:

            //匹配左括号
            if(exc[i]=='('||exc[i]=='['||exc[i]=='{'){
                if(exc[i]=='{'&&s.order[s.top-1].data=='('){//特判
                    printf("without maching \'%c\' at line %d",'(',s.order[s.top-1].row_num);
                    out = 1;
                    break;
                }
                node temp;
                temp.data = exc[i];
                temp.row_num = cur_num;
                all[all_num++] = exc[i];
                s.order[s.top++] = temp;
            }

改写后还有一处错误。经过与助教的沟通,发现其实题目中所说的“编写一程序检查C源程序文件中{}、()括号是否匹配”一句的“等”可以忽略,只需要匹配小括号和大括号,而不必匹配中括号,删除掉对应的代码即可。

完整代码

#include <stdio.h>

typedef struct node {
    char data;
    int row_num;
} node;

typedef struct stack {
    node order[512];
    int top;
} stack;

int ismatch(char c1,char c2);

int main()
{

    stack s;
    s.top = 0;
    int cur_num = 0;//记录当前行数
    char all[512] = {0};//记录完整的有效括号
    int all_num = 0;//记录完整的括号的个数
    int out = 0;//若out改为1,说明已经找到出错括号,退出程序

    FILE* in = fopen("example.c", "r");
    char exc[512] = {0};//暂存每行的字符
    int flag = 0;//记录当前状态。0表示正常匹配状态,1表示单字符状态,2表示字符串状态,3表示多行注释状态
    while(fgets(exc,512,in) != NULL){
        if(out)break;
        cur_num++;
        for(int i=0;exc[i]!='\0';i++){
            if(flag!=0){//当前非正常匹配状态
                if(flag==1&&exc[i]=='\''){//单字符状态
                    flag = 0;
                    continue;
                }
                if(flag==2&&exc[i]=='\"'){//字符串状态
                    flag = 0;
                    continue;
                }
                if(flag==3&&exc[i]=='*'&&exc[i+1]=='/'){//多行注释状态
                    flag = 0;
                    i++;
                    continue;
                }
                //不改变状态,无效字符,跳过
                continue;
            }

            //正常状态,开始匹配
            //匹配左括号
            if(exc[i]=='('||exc[i]=='{'){
                if(exc[i]=='{'&&s.order[s.top-1].data=='('){//特判
                    printf("without maching \'%c\' at line %d",'(',s.order[s.top-1].row_num);
                    out = 1;
                    break;
                }
                node temp;
                temp.data = exc[i];
                temp.row_num = cur_num;
                all[all_num++] = exc[i];
                s.order[s.top++] = temp;
            }

            //匹配右括号
            if(exc[i]==')'||exc[i]=='}'){
                all[all_num++] = exc[i];
                if(ismatch(s.order[s.top-1].data,exc[i])){
                    s.top--;
                }
                else {
                    printf("without maching \'%c\' at line %d",exc[i],cur_num);
                    out = 1;
                    break;
                }
            }
            
            //匹配改变状态的字符
            if(exc[i]=='\''){
                flag = 1;
                continue;
            }
            if(exc[i]=='\"'){
                flag = 2;
                continue;
            }
            if(exc[i]=='/'&&exc[i+1]=='*'){
                flag = 3;
                i++;
                continue;
            }
            if(exc[i]=='/'&&exc[i+1]=='/'){
                break;
            }
        }

    }
    if(!out){
        if(s.top!=0){
            printf("without maching \'%c\' at line %d",s.order[s.top-1].data,s.order[s.top-1].row_num);
        }
        else {
            printf("%s",all);
        }
    }
    return 0;
}


//判断两括号是否匹配
int ismatch(char c1,char c2)
{
    if(c1=='('&&c2==')')
        return 1;
    if(c1=='{'&&c2=='}')
        return 1;
    return 0;
}

3. 计算器(表达式计算-后缀表达式实现,结果为浮点)

问题

问题描述

从标准输入中读入一个算术运算表达式,如:24 / ( 1 + 5/3 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 )= ,计算表达式结果,并输出。

要求:

1、表达式运算符只有+、-、*、/,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
2、表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;

3、表达式中出现的操作数都是十进制整数常量但要求运算结果为浮点型,例如:5/2结果应为2.5。

4、要求采用逆波兰表达式来实现表达式计算。

输入形式

从键盘输入一个以=结尾的算术运算表达式。操作符和操作数之间可以有空格分隔。

输出形式

在屏幕上输出计算结果,小数点后保留两位有效数字

样例输入

24 / ( 1 + 5/3 + 36 / 6 / 2 - 2) * ( 12 / 2 / 2 ) =

样例输出

19.64

样例说明

按照运算符及括号优先级依次计算表达式的值。

评分标准

该题要求采用逆波兰表达式实现表达式运算,提交程序名为cal.c。

问题分析

本题要求采用逆波兰表达式(栈)的方式计算表达式。有关逆波兰表达式的内容可以自行百度搜索一下。这里我们分析出除了实现逆波兰表达式之外,还需要解决这样几个问题:

  • 空格的处理:我们可以先把整串字符串读进来,再对其中每一位进行处理,读到空格时直接continue即可。

  • 数字的读入:由于题目中输入的时一串字符串,其中的数字可能不止有一位,比如样例中的24,所以我们要解决多位数字的识别。这可以用队列的思想来处理:读到数字字符就将对应的数字入队,直到遇到运算符。

  • 逆波兰表达式存储问题:由于逆波兰表达式中即有数字又有操作符,所以普通的整数或字符数组无法存储。这里我们选择用联合的方式来存储即可。

具体实现过程

首先定义联合类型,并设置一个数组,以便我们知道表达式中的每一位是数字还是操作符:

#include <stdio.h>
#include <math.h>
typedef union node{
    int num;
    char op;
} node;//联合类型,用于存储逆波兰表达式的每一位
int option[1024];//记录逆波兰表达式中各位为数字还是运算符,1表示数字,0表示运算符

主函数中的流程应当如下:

int main()
{
    char str[1024] = {0};
    gets(str);


    char opera[1024] = {0};//记录操作数
    node representation[1024] = {0};//记录逆波兰表达式
    int rep_num = change(str,representation,opera);//转化为逆波兰表达式,rep_num记录表达式中有多少位

    //求解逆波兰表达式
    double stack[1024] = {0};//栈
    int stack_num = 0;
    for(int j=0;j<rep_num;j++){
        if(option[j]==1){//该位是数字,直接入栈
            stack[stack_num++] = representation[j].num;
        }else {//该位是操作符
            switch (representation[j].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;           
            }           
        }
    }
    printf("%.2lf",stack[0]);

    return 0;
}

其中change函数把中缀表达式(str)转化为逆波兰表达式(representation),并返回该表达式有多少位,以便后面遍历,现在我们来实现change函数:

int change(char *str,node* representation,char* opera)
{
    int op_num = 0;
    int rep_num = 0;

    //暂存各位数字以及数字的位数
    int tmp_num[100];
    int tmp = 0;

        
    for(int i=0;str[i]!='=';i++){
        if(str[i]==' ')continue;//如果是空格,直接跳过

        if(!(str[i]>='0'&&str[i]<='9')){
            if(tmp!=0){//如果当前不是数字,并且队中还有数字,则现将数字算出来存储到表达式数组中
                int number = 0;
                for(int j=0;j<tmp;j++){
                    number += tmp_num[j]*pow(10,tmp-j-1);
                }
                option[rep_num] = 1;
                representation[rep_num++].num = number;               
                tmp = 0;
            }

        }

        //如果当前位是数字
        if(str[i]>='0'&&str[i]<='9'){
            tmp_num[tmp++] = str[i] - '0';
            continue;
        }

        //左右括号特判
        if(str[i]=='('){
            opera[op_num++] = str[i];
            continue;
        }
        if(str[i] == ')'){
            while(opera[op_num-1]!='('){
                option[rep_num] = 0;
                representation[rep_num++].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函数判断加减乘除四个运算符的优先级
            option[rep_num] = 0;
            representation[rep_num++].op = opera[op_num-1];
            op_num--;
        }
        opera[op_num++] = str[i];//该操作符入栈
    }
    // 最后将栈清空输出到逆波兰表达式中
    for(int i=op_num-1;i>=0;i--){
        option[rep_num] = 0;
        representation[rep_num++].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;
}

完整代码

#include <stdio.h>
#include <math.h>
typedef union node{
    int num;
    char op;
} node;
int option[1024];//记录表达式中各位为数字还是运算符,1表示数字,0表示运算符
int change(char *str,node* representation,char* opera);
int ishigh(char c1,char c2);

int main()
{
    char str[1024] = {0};
    gets(str);


    char opera[1024] = {0};//记录操作数
    node representation[1024] = {0};//记录逆波兰表达式
    int rep_num = change(str,representation,opera);//转化为逆波兰表达式,rep_num记录表达式中有多少位

    //求解逆波兰表达式
    double stack[1024] = {0};//栈
    int stack_num = 0;
    for(int j=0;j<rep_num;j++){
        if(option[j]==1){//该位是数字,直接入栈
            stack[stack_num++] = representation[j].num;
        }else {//该位是操作符
            switch (representation[j].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;           
            }
                 

        }
    }
    printf("%.2lf",stack[0]);

    return 0;
}

int change(char *str,node* representation,char* opera)
{
    int op_num = 0;
    int rep_num = 0;

    //暂存各位数字以及数字的位数
    int tmp_num[100];
    int tmp = 0;

        
    for(int i=0;str[i]!='=';i++){
        if(str[i]==' ')continue;//如果是空格,直接跳过

        if(!(str[i]>='0'&&str[i]<='9')){
            if(tmp!=0){//如果当前不是数字,并且队中还有数字,则现将数字算出来存储到表达式数组中
                int number = 0;
                for(int j=0;j<tmp;j++){
                    number += tmp_num[j]*pow(10,tmp-j-1);
                }
                option[rep_num] = 1;
                representation[rep_num++].num = number;               
                tmp = 0;
            }

        }

        //如果当前位是数字
        if(str[i]>='0'&&str[i]<='9'){
            tmp_num[tmp++] = str[i] - '0';
            continue;
        }

        //左右括号特判
        if(str[i]=='('){
            opera[op_num++] = str[i];
            continue;
        }
        if(str[i] == ')'){
            while(opera[op_num-1]!='('){
                option[rep_num] = 0;
                representation[rep_num++].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函数判断加减乘除四个运算符的优先级
            option[rep_num] = 0;
            representation[rep_num++].op = opera[op_num-1];
            op_num--;
        }
        opera[op_num++] = str[i];//该操作符入栈
    }
    // 最后将栈清空输出到逆波兰表达式中
    for(int i=op_num-1;i>=0;i--){
        option[rep_num] = 0;
        representation[rep_num++].op = opera[i];
    }
    return rep_num;
}

//判断加减乘除运算符的优先级
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;
}

4. 文本编辑操作模拟(简)a

问题

问题描述

编写一程序模拟文本编辑操作。首先从标准输入读取一行字符串(字符个数不超过512),该行字符串是已经过n(大于0,小于等于10)步编辑操作后的结果。然后从下一行读取n,以及已发生过的n步编辑操作,编辑操作分行输入,输入格式为:

op pos str

其中op为编辑操作命令编码(在此只有插入和删除操作,1表示插入或2表示删除操作);pos表示插入或删除的位置;str表示已经插入或删除的字符串(中间没有空格)。各数据间以一个空格分隔。

然后在空一行后,再分行输入当前将要进行的编辑操作,包括如下四种操作(操作编码分别为:1表示插入,2表示删除操作,3表示撤销(即undo操作),-1表示结束):

1 pos str

表示将在pos位置插入字符串str(中间没有空格),各数据间以一个空格分隔;

2 pos n

表示将从pos位置开始删除n个字符(各数据间以一个空格分隔),若要删除的字符个数多于已有字符个数(即在文本中从pos开始的字符个数小于n),则按实际字符数删除即可。(提示:为了能够撤销删除操作,应按“2 pos str”形式保存命令。)

3

表示撤销最近执行的插入或删除操作,可以进行多次撤销操作,注意:也可以撤销之前已经发生过的n步编辑操作中的操作。

-1

表示退出编辑操作,在屏幕上输出最终编辑后的文本。

要求:

1、上述所有输入的编辑操作中的字符串str都不包含空白字符(空格符、制表符或换行符);

2、插入操作中的位置pos大于等于0,并且小于等于当前文本的字符个数;0位置表示文本第一个字符的位置;若pos为当前文本的字符个数,则表示在文本最后插入字符串;

3、删除操作中的位置pos大于等于0,并且小于当前文字的字符个数;

4、若已无操作可撤销,则再进行撤销操作无效;

5、文本在编辑过程中,总字符个数不会超过512

输入形式

先从键盘输入一行字符串,表示已经经过n步编辑操作后的文本串,然后在下一行输入一个正整数n,并分行输入n步插入或删除操作(表示按时间先后顺序已进行的操作),格式如上所述。随后空一行,再分行输入将要进行的编辑操作,格式如上所述。直到输入-1操作为止。

输出形式

在屏幕上输出最终编辑后的文本内容。

样例输入

A Stack is a container of objects that are inserted and removed according to the last-in first-out (LIFO) principle.???

4

1 20 ainer

2 0 ???

1 85 -

1 99 (LIFO)

3

2 110 10

1 110 Objects

2 98 1

2 0 1

2 108 10

3

3

3

-1

样例输出

A Stack is a container of objects that are inserted and removed according to the last-in first-out principle.Objects

样例说明

第一行输入的文本串是先后经过下面4次编辑操作后得到的:先在20位置插入了字符串ainer,然后删除了开始位置的字符串???,随后在85位置插入了一个字符-,最后在99位置插入了字符串(LIFO)。

随后输入了撤销操作,即撤销先前最后进行的“1 99 (LIFO)”操作,也就是将99位置的6个字符删除;

2 110 10:将文本串最后的字符串???删除;

1 110 Objects:在文本串末尾插入字符串Objects;

随后执行了三次删除操作,又执行了三次撤销操作,最后输入的-1表示编辑操作结束,在屏幕上输出最终编辑后的文本串。

评分标准

该程序要求编程模拟编辑操作,提交程序文件名为edit.c。

问题分析

本题最棘手的地方在于撤销操作和前面已经经历过n个操作配合在一起。试想如果没有这两个条件,本题将变得异常简单:读入一个操作就进行一个操作即可。而如果只增加一个撤销操作(相当于保证不能撤销前n步的操作),本题难度也并没有实质性的增加:我们可以设置一个“操作队”,当读入的操作是插入或删除时,就将相应的操作信息入队;而当读入的是撤销操作时,只需要队头元素出队即可。

分析至此,我们不仅找到了本题最核心的要处理的地方,而且想到了一种似乎可行的处理方法——我们只要在上面的分析思路中加上对“撤销了前n个操作”的情况处理即可。为此,我们可以把对插入和删除的撤销操作看成另外两个操作——即插入/删除的逆操作,在恰当的时刻将他们也入队即可。

具体处理过程

首先定义操作的结构:

#include <stdio.h>
#include <string.h>
typedef struct execute{
    int op;//代表是什么操作
    int pos;//位置
    char str[600];//操作的字符串
    int n;//操作字符的长度
} execute;

并不是每一种操作都有这四个域,但是方便起见,我们直接用一个结构定义出来。对于op的值,我们不妨设插入操作的op为1,删除为2,删除为3(这三个是题目给出的),而“逆”插入为4(读入时为1,我们手动+3即可),“逆”删除为5

接下来,在主函数中,程序框架应当如下:

int main()
{
    execute stack[15];//记录前n个操作
    char now_text[600] = {0};//现在的字符串
    gets(now_text);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d %s",&stack[i].op,&stack[i].pos,stack[i].str);
        stack[i].op+=3;//+3以和后面的操作区分开
    }

    execute shed[100];//记录从现在往后的操作(这里是队列)
    int top = 0;
    int op;
    int after = 0;//记录在前n个操作之后的操作数(用来判断什么时候将“逆”操作入队)
    while((scanf("%d",&op)!=EOF)&&op!=-1){
        if(op==1){
            scanf("%d %s",&shed[top].pos,shed[top].str);
            shed[top].op = 1;
            top++;
            after++;
        }
        else if(op==2){
            scanf("%d %d",&shed[top].pos,&shed[top].n);
            shed[top].op = 2;
            top++;
            after++;
        }
        else if(op==3){
            if(top>0&&after>0){
                top--;
                after--;
            }else {               
                shed[top++] = stack[n-1];
                n--;
            }
        }
    }
    
    //读取完毕,现在按顺序出队即可
    for(int i=0;i<top;i++){
        if(shed[i].op==1){
            insert(now_text,shed[i].pos,shed[i].str);
        }
        else if(shed[i].op==2){
            delete(now_text,shed[i].pos,shed[i].n);
        }
        else if(shed[i].op==4){
            anti_insert(now_text,shed[i].pos,shed[i].str);
        }
        else if(shed[i].op==5){
            anti_delete(now_text,shed[i].pos,shed[i].str);
        }
    }
    printf("%s",now_text);
    
    return 0;
}

接下来我们只要根据具体的操作细节,编写这四个操作函数即可:

在这里插入图片描述

这都是很基础的函数操作了,对应代码如下:

void insert(char *s,int pos,char *str)//op==1
{
    int str_len = strlen(str);
    char tmp[600] = {0};
    strcpy(tmp,s+pos);
    strcpy(s+pos,str);
    strcpy(s+pos+str_len,tmp);
}
void delete(char *s,int pos,int n)//op==2
{
    int str_len = strlen(s);
    if(pos+n > str_len){
        *(s+pos) = '\0';
        return ;
    }       
    char tmp[600] = {0};
    strcpy(tmp,s+pos+n);
    strcpy(s+pos,tmp);
}
void anti_insert(char*s,int pos,char*str)//op==4
{
    int len = strlen(str);
    char tmp[600] = {0};
    strcpy(tmp,s+pos+len);
    strcpy(s+pos,tmp);
}
void anti_delete(char*s,int pos,char*str)//op==5
{
    insert(s,pos,str);
}

完整代码

#include <stdio.h>
#include <string.h>
typedef struct execute{
    int op;//代表是什么操作
    int pos;//位置
    char str[600];//操作的字符串
    int n;//操作字符的长度
} execute;

void insert(char *s,int pos,char *str)//op==1
{
    int str_len = strlen(str);
    char tmp[600] = {0};
    strcpy(tmp,s+pos);
    strcpy(s+pos,str);
    strcpy(s+pos+str_len,tmp);
}
void delete(char *s,int pos,int n)//op==2
{
    int str_len = strlen(s);
    if(pos+n > str_len){
        *(s+pos) = '\0';
        return ;
    }       
    char tmp[600] = {0};
    strcpy(tmp,s+pos+n);
    strcpy(s+pos,tmp);
}
void anti_insert(char*s,int pos,char*str)//op==4
{
    int len = strlen(str);
    char tmp[600] = {0};
    strcpy(tmp,s+pos+len);
    strcpy(s+pos,tmp);
}
void anti_delete(char*s,int pos,char*str)//op==5
{
    insert(s,pos,str);
}

int main()
{
    execute stack[15];//记录前n个操作
    char now_text[600] = {0};//现在的字符串
    gets(now_text);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d %s",&stack[i].op,&stack[i].pos,stack[i].str);
        stack[i].op+=3;//+3以和后面的操作区分开
    }

    execute shed[100];//记录从现在往后的操作(这里是队列)
    int top = 0;
    int op;
    int after = 0;//记录在前n个操作之后的操作数(用来判断什么时候将“逆”操作入队)
    while((scanf("%d",&op)!=EOF)&&op!=-1){
        if(op==1){
            scanf("%d %s",&shed[top].pos,shed[top].str);
            shed[top].op = 1;
            top++;
            after++;
        }
        else if(op==2){
            scanf("%d %d",&shed[top].pos,&shed[top].n);
            shed[top].op = 2;
            top++;
            after++;
        }
        else if(op==3){
            if(top>0&&after>0){
                top--;
                after--;
            }else {               
                shed[top++] = stack[n-1];
                n--;
            }
        }
    }
    
    //读取完毕,现在按顺序出队即可
    for(int i=0;i<top;i++){
        if(shed[i].op==1){
            insert(now_text,shed[i].pos,shed[i].str);
        }
        else if(shed[i].op==2){
            delete(now_text,shed[i].pos,shed[i].n);
        }
        else if(shed[i].op==4){
            anti_insert(now_text,shed[i].pos,shed[i].str);
        }
        else if(shed[i].op==5){
            anti_delete(now_text,shed[i].pos,shed[i].str);
        }
    }
    printf("%s",now_text);
    
    return 0;
}

5. 银行排队模拟(生产者-消费者模拟) - 分类别

问题

问题描述

一个系统模仿另一个系统行为的技术称为模拟,如飞行模拟器。模拟可以用来进行方案论证、人员培训和改进服务。计算机技术常用于模拟系统中。

生产者**-**消费者(Server-Custom)是常见的应用模式,见于银行、食堂、打印机、医院、超等提供服务和使用服务的应用中。这类应用的主要问题是消费者如果等待(排队)时间过长,会引发用户抱怨,影响服务质量;如果提供服务者(服务窗口)过多,将提高运管商成本。(经济学中排队论)

假设某银行网点有五个服务窗口,分别为三个对私、一个对公和一个外币窗口。银行服务的原则是先来先服务。通常对私业务人很多,其它窗口人则较少,可临时改为对私服务。假设当对私窗口等待服务的客户(按实际服务窗口)平均排队人数超过(大于或等于)7人时,等待客户将可能有抱怨,影响服务质量,此时银行可临时将其它窗口中一个或两个改为对私服务,当平均排队客户少于7人时,将立即恢复原有业务。设计一个程序用来模拟银行服务。

说明:

  1. 增加服务窗口将会增加成本或影响其它业务,因此,以成本增加或影响最小为原则来增加服务窗口,即如果增加一个窗口就能使得按窗口平均等待服务人数小于7人,则只增加一个窗口。一旦按窗口平均等待服务人数小于7人,就减少一个所增加的窗口。
  2. 为了简化问题,假设新到客户是在每个服务周期开始时到达。
  3. 根据客户业务服务时间将客户分为3类:1(简单业务)、2(普通业务)、3(复杂业务),分别对应花费1-3个时间周期。
  4. 当等待服务人数发生变化时(新客户到达或有客户已接受服务),则及时计算按实际服务窗口平均等待服务人数,并按相应策略调整服务窗口数(增加或减少额外的服务窗口,但对私窗口不能减少)。注意:只在获取新客户(不管到达新客户数是否为0)时,才按策略调整服务窗口数。进一步讲,增加服务窗口只在有客户到达的周期内进行(也就是说增加窗口是基于客户的感受,银行对增加窗口是不情愿的,因为要增加成本,一旦不再有新客户来,银行是不会再增加服务窗口的);一旦有客户去接受服务(即等待客户减少),银行将根据策略及时减少服务窗口,因此,在每个周期内,有客户去接受服务后要马上判断是否减少服务窗口(因为能减少成本,银行是积极的)。

本问题中假设对公和对外币服务窗口在改为对私服务时及服务期间没有相应因公或外币服务新客户到达(即正好空闲),同时要求以增加成本或影响最小为前提,来尽最大可能减少对私服务客户等待时间。

输入形式

首先输入一个整数表示时间周期数,然后下一行输入每个时间周期到达的客户人数;再依次分行输入每个时间周期到达的因私业务的客户类别。注:一个时间周期指的是银行处理一笔业务的平均处理时间,可以是一分钟、三分钟或其它。每行中的整数之间以一个空格分隔。

例如:

6

2 5 8 11 15 6

1 3

2 2 1 3 2

说明:共有6个时间周期,第1个周期来了2个客户(序号分别为1、2,业务分别为简单业务和复杂业务),第2个时钟周期来了5人(序号分别为3、4、5、6、7,业务分别为普通业务、普通业务、简单业务、复杂业务和普通业务),以此类推。

输出形式

每个客户等待服务的时间周期数。输出形式如下:

用户序号 : 等待周期数

说明:客户序号与等待周期数之间用英文冒号:分隔,冒号(:)两边各有一个空格,等待周期数后直接为回车。

样例输入

4

2 5 13 11

1 3

2 2 1 3 2

1 1 1 1 3 3 2 2 1 2 3 1 1

3 3 2 1 3 1 1 3 1 3 3

样例输出

1 : 0

2 : 0

3 : 0

4 : 0

5 : 2

6 : 2

7 : 2

8 : 1

9 : 2

10 : 3

11 : 3

12 : 4

13 : 4

14 : 4

15 : 6

16 : 7

17 : 7

18 : 8

19 : 8

20 : 9

21 : 8

22 : 9

23 : 10

24 : 11

25 : 12

26 : 12

27 : 12

28 : 13

29 : 13

30 : 14

31 : 15

样例说明

样例输入表明有四个时间周期,第一个周期来了2人(序号1-2);第二个周期来了5人(序号3-7);第三个周期来了13人(序号8-20);第四个周期来了11人(序号21-31)。由于第一个时间周期内只来了2人,银行(有三个服务窗口)能及时提供服务,因此客户等待时间为0;第二个时间周期内来了5人,银行一个周期内一次只能服务3人,而且第一个周期内有一位办理复杂业务的客户,所以这时只有两个空闲窗口可以提供服务,前2人等待时间为0,另外3人需要等待;其它类推。

评分标准

通过所有测试点得满分。

问题分析

本题是一个大模拟,具体实现起来应该没什么难度,也有不少种思路去写,唯一容易出错的点是对题意理解有偏差,以下是几点需要注意的地方:

  1. 一个窗口在不在接待客户和一个窗口是否开放没有直接的关系。一个窗口可以在接待客户时关闭。
  2. 队列人数增加->判断是否应该增加窗口
    队列人数减少->判断是否应该减少窗口
    题目并未要求每次服务过程中平均等待人数均少于7人。
  3. 开放窗口可能一下开放两个(一次来特别多的人),关闭窗口理论上也相同,但由于一次最多只能走5个人,所以关闭窗口都是一个一个关的。
  4. 注意不在有人来之后,窗口只会关闭而不会再打开,但是原来打开的窗口并不是立刻关闭了。

完整代码

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

typedef struct cus{
    int left_time;
    int waiting_time;
} customer;//客户类型

typedef struct shed{
    customer row[1000];
    int bot;
    int top;
} shed;//客户等待队列

shed s;
int t4_activate = 0,t5_activate = 0;
int t1_time = 0,t2_time = 0,t3_time = 0,t4_time = 0,t5_time = 0;

double average_num()//计算平均人数
{
    double all = s.top - s.bot;
    double num = 3;
    if(t4_activate)num++;
    if(t5_activate)num++;
    return all/num;  
}
void change()//有人来时的操作
{
    if(average_num()>=7){
        if(!t4_activate){
            t4_activate = 1;
        }else {
            t5_activate = 1;
        }
    }
    if(average_num()>=7){
        if(!t4_activate){
            t4_activate = 1;
        }else {
            t5_activate = 1;
        }
    }//这个要判断两次,因为有可能一下开放两个窗口
    if(t1_time==0&&s.top!=s.bot){
        t1_time = s.row[s.bot++].left_time;
    }
    if(t2_time==0&&s.top!=s.bot){
        t2_time = s.row[s.bot++].left_time;
    }
    if(t3_time==0&&s.top!=s.bot){
        t3_time = s.row[s.bot++].left_time;
    }
    if(t4_time==0&&s.top!=s.bot&&t4_activate){
        t4_time = s.row[s.bot++].left_time;
    }
    if(t5_time==0&&s.top!=s.bot&&t5_activate){
        t5_time = s.row[s.bot++].left_time;
    }
    if(average_num()<7){
        if(t5_activate){
            t5_activate = 0;
        }else {
            t4_activate = 0;
        }
    } 
}
void change1()//没有人来后的操作
{
    if(t1_time==0&&s.top!=s.bot){
        t1_time = s.row[s.bot++].left_time;
    }
    if(t2_time==0&&s.top!=s.bot){
        t2_time = s.row[s.bot++].left_time;
    }
    if(t3_time==0&&s.top!=s.bot){
        t3_time = s.row[s.bot++].left_time;
    }
    if(t4_time==0&&s.top!=s.bot&&t4_activate){
        t4_time = s.row[s.bot++].left_time;
    }
    if(t5_time==0&&s.top!=s.bot&&t5_activate){
        t5_time = s.row[s.bot++].left_time;
    }
    if(average_num()<7){
        if(t5_activate){
            t5_activate = 0;
        }else {
            t4_activate = 0;
        }
    }
}
int main()
{
    s.top = 0;
    s.bot = 0;

    int n;
    scanf("%d",&n);
    int *num = (int*)malloc(sizeof(int)*n);

    for(int i=0;i<n;i++){
        scanf("%d",&num[i]);
    }

    for(int i=0;i<n;i++){
        for(int j=0;j<num[i];j++){
            int time;
            scanf("%d",&time);
            customer cus;
            cus.left_time = time;
            cus.waiting_time = 0;
            s.row[s.top++] = cus;
        }
        if(t1_time>0)t1_time--;
        if(t2_time>0)t2_time--;
        if(t3_time>0)t3_time--;
        if(t4_time>0)t4_time--;
        if(t5_time>0)t5_time--;
        change();
        for(int i=s.bot;i<s.top;i++){
            s.row[i].waiting_time++;
        }         
    }
    
    while(s.bot!=s.top){
        if(t1_time>0)t1_time--;
        if(t2_time>0)t2_time--;
        if(t3_time>0)t3_time--;
        if(t4_time>0)t4_time--;
        if(t5_time>0)t5_time--;
        change1();
        for(int i=s.bot;i<s.top;i++){
            s.row[i].waiting_time++;
        }        
    }
    for(int i=0;i<s.top;i++)printf("%d : %d\n",i+1,s.row[i].waiting_time);

    return 0;
}

6. 函数调用关系(选做,不计分)

问题

问题描述

给定某能正常运行结束的用户函数调用栈信息(当一个函数被调用时将入栈,当调用返回时,将出栈)。编写程序,对函数调用栈信息进行分析,依据函数入栈和出栈信息,分析函数调用关系,即一个函数调用了哪些不同函数。并按运行时调用序输出调用关系

说明:

  1. 在一个函数中,同一函数有可能被调用多次,输出调用关系时只输出一次;若一个函数没有调用其它函数,则不输出调用关系
  2. 函数运行时调用序是指函数在调用栈中的出现序
  3. 程序中不存在递归调用。函数名符合C语言标识符的规定,函数名长度不超过20,每个函数最多调用不超过10个不同函数,程序中用户定义的函数个数不超过100

算法提示:当一个函数入栈时,它就是当前栈顶函数调用的一个函数

输入形式

假设用8表示函数入栈操作;用0表示当前函数出栈。当操作为8(入栈)时,输入形式为:

<操作> <函数名>

当操作为0(出栈)时,输入形式为:

<操作>

所有入栈操作和出栈操作都是从标准输入分行输入,假设调用栈中函数个数最多不超过200。开始时,调用栈为空,当调用栈再次为空时,输入结束

输出形式

运行时调用先后顺序输出函数调用关系到标准输出,每行为一个函数的调用关系信息,包括:函数名及被调用函数,函数与被调用函数间用一个英文冒号“:”分隔,被调用函数间用一个英文逗号“,”分隔,最后一个函数名后跟一个回车。若一个函数没有调用其它函数,则不输出

样例输入

8 main

8 input

0

8 mysqrt

0

8 findA

0

8 findB

8 area

8 mysin

0

8 mycos

0

8 mysqrt

0

0

0

8 findC

8 area

8 mysin

0

0

8 mysqrt

8 max

0

0

0

8 output

0

0

样例输出

main:input,mysqrt,findA,findB,findC,ouput

mysqrt:max

findB:area

area:mysin,mycos,mysqrt

findC:area,mysqrt

样例说明

按照运行时调用函数的先后顺序,依次输出了main、mysqrt、findB、area和findC的函数调用关系。其中main函数调用了6个函数,按照运行时调用序依次输出。注意:mysqrt函数先于findB等函数出现在栈中,虽然mysqrt调用max较晚,但要先输出其调用关系。

评分标准

该题要求对函数调用栈信息进行分析,提交程序名为stack.c

问题分析

本题简直是一道裁缝题,虽然每一步都不难,但是需要我们有清醒的头脑,一步一步地去实现。本题的解决步骤大概如下:

  1. 以某种有效且恰当的方式存储输入的信息,并对其进行组织,使我们能清楚地知道每一个函数中都调用了哪些函数;
  2. 把相同的函数放到一起(比如主函数中先调用了input函数,input函数内部又调用了a函数,然后主函数中又调用了一堆别的函数之后,再次调用了input函数,此时input函数内部调用了b函数,那么我们此时应当把第二次调用的input和第一次调用的input合并到一起,即最后输出input函数中调用的函数时,要在一行同时输出a和b)
  3. 我们要解决一个函数中多次调用同一个函数的情况(比如在某个input函数中,连续3次调用了a函数,那么最后输出时只输出一个a)

具体处理过程

首先我们要找到一种合适的方式组织数据。我想到的是在总栈中创建函数分栈:总栈中的每一个元素都是一个分栈,其中的每一个分栈的第一个元素是按输入顺序出现的函数,而分栈中的其它元素是该分栈的第一个元素代表的函数所直接调用的函数。举个例子,比如如果输入是

8 main

8 input

0

8 input

8 a

8 b

0

0

0

那么我们构建的栈应当是这样的:

-------------------------------下面的分栈是总栈的第五个元素-------------------------
b
-------------------------------下面的分栈是总栈的第四个元素-------------------------
b
a
-------------------------------下面的分栈是总栈的第三个元素-------------------------
a
input
-------------------------------下面的分栈是总栈的第二个元素-------------------------
input
-------------------------------下面的分栈是总栈的第一个元素-------------------------
input
input
main

我们每次读入一个数据,首先要把它存入总栈中栈顶位置的分栈中的第0个位置,然后我们要找到是哪个函数(记为b)直接调用了该函数(记为a),并将a存入b所在分栈的栈顶。其实如何去找,题目中已经给我们提示了“算法提示:当一个函数入栈时,它就是当前栈顶函数调用的一个函数”,但由于我们的总栈并没有设置“出栈”功能,所以可以对总栈中的每一个分栈都记用一个int型变量记录其是否出栈了,这样每当总栈读入了一个新元素时,只需要从栈顶向下去寻找第一个没“出栈”的元素即可,代码实现如下:

#include <stdio.h>
#include <string.h>
typedef struct fun{
    char partial_stack[205][25];
    int is_finished;
} fun;  // 每个函数的分栈结构

fun total_stack[205];  // 函数总栈
int stack_num[205];  // 每个分栈中的函数数量
int main()
{
    int top = 0;
    int op;
    for(int i = 0; i < 205 ; i++){
        total_stack[i].is_finished = 0;
    }  // 初始化

    while(scanf("%d", &op) != EOF){
        if(op==8){
            scanf(" %s", total_stack[top].partial_stack[0]);  // 在总栈中读入
            stack_num[top]++;  // 总栈中该位置的函数分栈中的元素个数加一
            if(top!=0){  // 读入的不是第一个main
                int start = top - 1;  // 从总栈中的上一个函数开始找,是哪个函数直接调用了现在这个函数
                while(total_stack[start].is_finished){
                    start --;
                }
                strcpy(total_stack[start].partial_stack[stack_num[start]++], total_stack[top].partial_stack[0]);
                
            }
            top++;
        }else if(op==0){
            int start = top - 1;
            while(total_stack[start].is_finished){
                start --;
            }
            if(start==0){  // main函数出栈了,程序结束了
                break;
            }
            total_stack[start].is_finished = 1;  // 总栈中直接调用的函数出栈
        }      
    }

    // for(int i=0;i<top;i++){
    //     for(int j=0;j<stack_num[i];j++){
    //         printf("%s ", total_stack[i].partial_stack[j]);
    //     }
    //     printf("\n");
    // }

    return 0;
}

最后的注释那几行可以把总栈中的每一个分栈打印出来,以检验我们做的对不对。

现在,数据的读入与基本处理完成,我们接下来要实现问题分析中的第二步。对这个问题,只需要我们在fun结构中加一个int型变量,从0开始递增,当读入的某个数据与已经读入过的数据名字相同时,赋予其相同的序号,最后用qsort排序即可。代码实现如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct fun{
    char partial_stack[205][25];
    int is_finished;
    int num;
} fun;  // 每个函数的分栈结构

fun total_stack[205];  // 函数总栈
int stack_num[205];  // 每个分栈中的函数数量
int fun_number = 0;  // 函数的编号

int cmp(const void *a, const void *b)
{
    int e1 = ((fun*)a) -> num;
    int e2 = ((fun*)b) -> num;
    return e1-e2;
}

int main()
{
    int top = 0;
    int op;
    for(int i = 0; i < 205 ; i++){
        total_stack[i].is_finished = 0;
    }  // 初始化

    while(scanf("%d", &op) != EOF){
        if(op==8){
            scanf(" %s", total_stack[top].partial_stack[0]);  // 在总栈中读入
            // 给总栈中的这个函数编号
            int is_used = 0;  // 判断该函数之前是否调用过
            for(int i=0;i<top;i++){
                if(strcmp(total_stack[i].partial_stack[0], total_stack[top].partial_stack[0]) == 0){
                    is_used = 1;
                    total_stack[top].num = total_stack[i].num;
                    break;
                }
            }
            if(!is_used){
                total_stack[top].num = fun_number++;
            }
            stack_num[top]++;  // 总栈中该位置的函数分栈中的元素个数加一
            if(top!=0){  // 读入的不是第一个main
                int start = top - 1;  // 从总栈中的上一个函数开始找,是哪个函数直接调用了现在这个函数
                while(total_stack[start].is_finished){
                    start --;
                }
                strcpy(total_stack[start].partial_stack[stack_num[start]++], total_stack[top].partial_stack[0]);
                
            }
            top++;
        }else if(op==0){
            int start = top - 1;
            while(total_stack[start].is_finished){
                start --;
            }
            if(start==0){  // main函数出栈了,程序结束了
                break;
            }
            total_stack[start].is_finished = 1;  // 总栈中直接调用的函数出栈
        }        
    }

    qsort(total_stack, top, sizeof(fun), cmp);  // 排序
    // for(int i=0;i<top;i++){
    //     for(int j=0;total_stack[i].partial_stack[j][0];j++){
    //         printf("%s ", total_stack[i].partial_stack[j]);
    //     }
    //     printf("\n");
    // }
    return 0;
}

但此时我们用最后注释的那几行打印出来,却发现了一个问题:输出是这样的:

main input mysqrt findA findB findC output
input
mysqrt
mysqrt
mysqrt max
findA
findB area
area mysin
area mysin mycos mysqrt
mysin
mysin
mycos
findC area mysqrt
max
output

area函数这两行,在后面合并时我们并不知道哪个是第一个调用的,哪个是第二个调用的,也就导致两个分栈合并时我们不知道谁的元素应该放在前面,解决这个问题的办法也很简单,fun中的num域记录了每个函数的“相对顺序”,即同名函数的num是相同的,我们只需要再设置一个actual_num域记录所有函数的真实顺序,qsort排序设置cmp函数时改写为双条件排序即可:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct fun{
    char partial_stack[205][25];
    int is_finished;
    int num;
    int actual_num;
} fun;  // 每个函数的分栈结构

fun total_stack[205];  // 函数总栈
int stack_num[205];  // 每个分栈中的函数数量
int fun_number = 0;  // 函数的编号
int tol_num = 0; //函数总数
int cmp(const void *a, const void *b)
{
    int e1 = ((fun*)a) -> num;
    int e2 = ((fun*)b) -> num;
    if(e1!=e2)return e1-e2;
    return ((fun*)a) -> actual_num - ((fun*)b) -> actual_num;
}

int main()
{
    int top = 0;
    int op;
    for(int i = 0; i < 205 ; i++){
        total_stack[i].is_finished = 0;
    }  // 初始化

    while(scanf("%d", &op) != EOF){
        if(op==8){
            scanf(" %s", total_stack[top].partial_stack[0]);  // 在总栈中读入
            total_stack[top].actual_num = tol_num++;
            // 给总栈中的这个函数编号
            int is_used = 0;  // 判断该函数之前是否调用过
            for(int i=0;i<top;i++){
                if(strcmp(total_stack[i].partial_stack[0], total_stack[top].partial_stack[0]) == 0){
                    is_used = 1;
                    total_stack[top].num = total_stack[i].num;
                    break;
                }
            }
            if(!is_used){
                total_stack[top].num = fun_number++;
            }
            stack_num[top]++;  // 总栈中该位置的函数分栈中的元素个数加一
            if(top!=0){  // 读入的不是第一个main
                int start = top - 1;  // 从总栈中的上一个函数开始找,是哪个函数直接调用了现在这个函数
                while(total_stack[start].is_finished){
                    start --;
                }
                strcpy(total_stack[start].partial_stack[stack_num[start]++], total_stack[top].partial_stack[0]);
                
            }
            top++;
        }else if(op==0){
            int start = top - 1;
            while(total_stack[start].is_finished){
                start --;
            }
            if(start==0){  // main函数出栈了,程序结束了
                break;
            }
            total_stack[start].is_finished = 1;  // 总栈中直接调用的函数出栈
        }       
    }

    qsort(total_stack, top, sizeof(fun), cmp);
    return 0;
}

而后我们延续前面几次作业解决“合并输出”时相同的思路,设置一个输出队列,同时在每个元素进队时判断一下当前队列中是否已经有该元素即可。

完整代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct fun{
    char partial_stack[205][25];
    int is_finished;
    int num;
    int actual_num;
} fun;  // 每个函数的分栈结构

fun total_stack[205];  // 函数总栈
int stack_num[205];  // 每个分栈中的函数数量
int fun_number = 0;  // 函数的编号
int tol_num = 0; //函数总数
int cmp(const void *a, const void *b)
{
    int e1 = ((fun*)a) -> num;
    int e2 = ((fun*)b) -> num;
    if(e1!=e2)return e1-e2;
    return ((fun*)a) -> actual_num - ((fun*)b) -> actual_num;
}

int main()
{
    int top = 0;
    int op;
    for(int i = 0; i < 205 ; i++){
        total_stack[i].is_finished = 0;
    }  // 初始化

    while(scanf("%d", &op) != EOF){
        if(op==8){
            scanf(" %s", total_stack[top].partial_stack[0]);  // 在总栈中读入
            total_stack[top].actual_num = tol_num++;
            // 给总栈中的这个函数编号
            int is_used = 0;  // 判断该函数之前是否调用过
            for(int i=0;i<top;i++){
                if(strcmp(total_stack[i].partial_stack[0], total_stack[top].partial_stack[0]) == 0){
                    is_used = 1;
                    total_stack[top].num = total_stack[i].num;
                    break;
                }
            }
            if(!is_used){
                total_stack[top].num = fun_number++;
            }
            stack_num[top]++;  // 总栈中该位置的函数分栈中的元素个数加一
            if(top!=0){  // 读入的不是第一个main
                int start = top - 1;  // 从总栈中的上一个函数开始找,是哪个函数直接调用了现在这个函数
                while(total_stack[start].is_finished){
                    start --;
                }
                strcpy(total_stack[start].partial_stack[stack_num[start]++], total_stack[top].partial_stack[0]);
                
            }
            top++;
        }else if(op==0){
            int start = top - 1;
            while(total_stack[start].is_finished){
                start --;
            }
            if(start==0){  // main函数出栈了,程序结束了
                break;
            }
            total_stack[start].is_finished = 1;  // 总栈中直接调用的函数出栈
        }       
    }

    qsort(total_stack, top, sizeof(fun), cmp);

    //输出
    char output_shed[205][25] = {{0}};
    int output_num = 0;
    for(int i=0;i<top;i++){
        if(!output_num){  // 现在队列为空
            for(int j=0;total_stack[i].partial_stack[j][0];j++){
                int is_in = 0;  // 判断当前这个元素是否已经在输出队列中了
                for(int k=0;k<output_num;k++){
                    if(strcmp(output_shed[k], total_stack[i].partial_stack[j])==0){
                        is_in = 1;
                        break;
                    }
                }
                if(!is_in){
                    strcpy(output_shed[output_num++], total_stack[i].partial_stack[j]);
                }
            }
        }else if(strcmp(total_stack[i].partial_stack[0], output_shed[0])==0){
            for(int j=1;total_stack[i].partial_stack[j][0];j++){
                int is_in = 0;
                for(int k=0;k<output_num;k++){
                    if(strcmp(output_shed[k], total_stack[i].partial_stack[j])==0){
                        is_in = 1;
                        break;
                    }
                }
                if(!is_in){
                    strcpy(output_shed[output_num++], total_stack[i].partial_stack[j]);
                }
            }
        }
        else {
            if(output_num >1){
                printf("%s:", output_shed[0]);
                for(int j=1;j<output_num;j++){
                    printf("%s",output_shed[j]);
                    if(j!=output_num-1)printf(",");
                }
                printf("\n");
            }
            output_num = 0;
            for(int j=0;total_stack[i].partial_stack[j][0];j++){
                int is_in = 0;
                for(int k=0;k<output_num;k++){
                    if(strcmp(output_shed[k], total_stack[i].partial_stack[j])==0){
                        is_in = 1;
                        break;
                    }
                }
                if(!is_in){
                    strcpy(output_shed[output_num++], total_stack[i].partial_stack[j]);
                }               
            }
        }
    }
    // 最后再清空一次栈
    if(output_num >1){
        printf("%s:", output_shed[0]);
        for(int j=1;j<output_num;j++){
            printf("%s",output_shed[j]);
            if(j!=output_num-1)printf(",");
        }
        printf("\n");
    }
    return 0;
}
  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
BUAA数据结构第四次编程是关于栈操作的目。目要求使用数组模拟栈,并按照给定的操作顺序进行入栈和出栈操作。入栈操作表示将给定的数字压入栈中,出栈操作表示弹出栈顶元素并输出。如果栈状态为空时进行出栈操作,或栈已满时进行入栈操作,则输出"error"。最后,需要按照操作的顺序输出出栈元素序列,元素之间用空格分隔。如果栈最终不为空,则最后一个元素后也要有一个空格。 解思路:使用一个数组来实现栈的数据结构,通过维护一个指针top来表示栈顶元素的位置。根据给定的操作进行相应的入栈和出栈操作。对于入栈操作,将给定的数字存入数组中,并将top指针加1。对于出栈操作,判断栈是否为空,若为空则输出"error",否则输出栈顶元素并将top指针减1。最后,按照操作的顺序输出出栈元素序列。 以下是参考代码: ``` #include<stdio.h> #include<string.h> #include<math.h> #include<stdlib.h> #include<ctype.h> struct stack{ int top; int data<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [BUAA(2021春)栈操作(栈-基本)](https://blog.csdn.net/zhouEnshen/article/details/116305600)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值