结对项目(高明莹3218004990+刘珮琳3218004993)

一、github地址https://github.com/GMloria/GGMYING/blob/master/%E7%BB%93%E5%AF%B9%E9%A1%B9%E7%9B%AE(%E5%88%98%E7%8F%AE%E7%90%B3%2B%E9%AB%98%E6%98%8E%E8%8E%B9%EF%BC%89

二、结对成员:高明莹3218004990          刘珮琳3218004993

三、项目需求

       自然数:0, 1, 2, …。

  • 真分数:1/2, 1/3, 2/3, 1/4, 1’1/2, …。
  • 运算符:+, −, ×, ÷。
  • 括号:(, )。
  • 等号:=。
  • 分隔符:空格(用于四则运算符和等号前后)。
  • 算术表达式:

e = n | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2 | (e),

其中e, e1和e2为表达式,n为自然数或真分数。

  • 四则运算题目:e = ,其中e为算术表达式。
  1. 使用 -n 参数控制生成题目的个数,例如

           Myapp.exe -n 10      将生成10个题目。

  1. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如

Myapp.exe -r 10将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。

  1. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
  2. 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
  3. 每道题目中出现的运算符个数不超过3个。
  4. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。

生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:

  1. 四则运算题目1
  2. 四则运算题目2

……

其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。

  1. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
  1. 答案1
  2. 答案2

特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。

  1. 程序应能支持一万道题目的生成。
  2. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:

Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt

统计结果输出到文件Grade.txt,格式如下:

Correct: 5 (1, 3, 5, 7, 9)

Wrong: 5 (2, 4, 6, 8, 10)

其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。

四、已完成以及未完成功能

生成题目序号以及题目并写入文件(完成)

控制题目中数值的范围。(完成)  

计算四则运算的答案并写入文件(完成)

 每道题目中出现的运算符个数不超过3个。(完成) 

 生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。(完成)

 程序支持对给定的题目文件和答案文件,判定学生书写答案中的对错并进行数量统计。(完成)

 程序应能支持一万道题目的生成。(完成)

 程序一次运行生成的题目不能重复------------由于在项目开始之前没有考虑到边生成题目边查重,而是先生成题目,再调用查重函数,由于先生成题目之后再查重需要的递归次数随着操作符的增加而增多,所以查重函数的运行效率很低,无法判断是否实现了该功能。

五、效能分析

运行时间

 由于在项目开始之前没有考虑到边生成题目边查重,而是先生成题目,再调用查重函数,由于先生成题目之后再查重需要的递归次数随着表达式个数的增加而增加,所以查重函数的运行效率很低,当两个表达式二叉树的运算符个数不同时,执行效率较高,能够较快得出结果,但当两个表达式二叉树的运算符个数相同时,光标持续闪烁且无输出结果,因此无法判断是否完全实现了该功能。

情况如图:

六、设计实现过程

对答案以及查重的流程图:

其他功能函数:

七、详细代码

随机生成操作符函数

char* caozuofu(char* ziFu,int a)    //a为想要获得的操作符个数
{
    char *p;
    int shuZi[a];
    shuZi[a]= {0};
    for(int j=0; j<a; j++)
    {
        shuZi[j]=rand()%4;
        switch(shuZi[j])
        {
        case 0:
            ziFu[j]='+';
            break;
        case 1:
            ziFu[j]='-';
            break;
        case 2:
            ziFu[j]='*';
            break;
        case 3:
            ziFu[j]='/';
            break;
        default:
            ;
        }
    }
    return ziFu;
}

求两个数公约数函数

int gongyueshu(int x,int y)         //约分用的
{
    int i,p;
    if(x%y==0||y%x==0)
    {
        i=x<=y?x:y;
    }
    else      //x不能整除y或y不能整除x
    {
        if(x<y)
        {
            p=x;
            for(i=p; i>1; i--)
            {
                if(x%i==0&&y%i==0)
                    break;
            }
        }
        else     //x>y
        {
            p=y;
            for(i=p; i>1; i--)
            {
                if(x%i==0&&y%i==0)
                    break;
            }
        }
    }
    return i;
}

将分数化为真分数函数,并将真分数写入txt文件

void  zhenfenshu(int x,int y,FILE *fp)    //将分数变成真分数
{

    int z,a,r;
    z=x/y;
    a=x%y;
    if(a==0)            //x整除y得时候即该数不为分数
    {
        fprintf(fp,"%d
",z);
    }
    else              //x不整除y
    {
        if(z==0)      // x<y
        {
            fprintf(fp,"%d/%d
",x/gongyueshu(x,y),y/gongyueshu(x,y));
        }
        else         //x>y
        {
            r=x-z*y;
            fprintf(fp,"%d'%d/%d
",z,r/gongyueshu(r,y),y/gongyueshu(r,y));
        }
    }
}

将两个整数约分之后的函数,并返回分子和分母

//整数除法的函数
int* int_chuFa(int n1,int n2,int* num)
{
    int z1,a1,r1;
    z1=n1/n2;
    a1=n1%n2;
    if(a1==0)            //n1整除n2得时候即该数不为分数
    {
        num[0]=z1;
        num[1]=1;
    }
    else              //x不整除y
    {
        int yueshu = gongyueshu(n1,n2);
        num[0]=n1/yueshu;
        num[1]=n2/yueshu;
    }
    return num;
}

对答案的函数

Status checkAnswer(){
    char student[30];
    char answer[30];      //s用来存储学生答案的文件名(含后缀)
    printf("---------------------------------------------IMPORTANT!!!!-----------------------------------------------
");
    printf("****************************请先将学生答案放入文件夹的根目录下再进行下述操作*****************************
");
    printf("
请输入学生答案的文件名(附文件后缀名):");
    scanf("%s",student);//学生答案的文件名
 //   printf("
请输入标准答案的文件名(附文件后缀名):");
 //   scanf("%s",answer);


    FILE *fpStudent = fopen(student,"r");       //读学生答案
    if(fpStudent == NULL)      //无法读取文件的异常情况
    {
        printf("
无法读取学生的答案文件,请重试!
");
        getchar();
        exit(1);
    }
    //默认标准答案的文件名为answer.txt
    FILE *fpAnswer = fopen("answer.txt","r");         //读标准答案
    if(fpAnswer == NULL)
    {
        printf("
无法读取标准答案的文件,请重试!
");
        getchar();
        exit(1);
    }

    int CorrectNum = 0,WrongNum = 0;
    int control1 = 0,control2 = 0,control3 = 0;  //control1:wrongList,control2:correctList,control3:modifyList,control4:lackList
    char *p, *q;    //p指针用来遍历学生答案,q指针用来遍历标准答案
    char *p1, *q1, *p2;
    char temp[30]={0};
    char temp1[30]={0};
    char temp2[30]={0};
    char temp3[30] = {0};
    AnswerList wrongList,correctList,modifyList;
    InitAnswerList(wrongList);    //初始化错误题目的链表
    InitAnswerList(correctList);  //初始化正确题目的链表
    InitAnswerList(modifyList);   //初始化需要修改题目的链表
    char string1[30], string2[30],string3[30], string4[30];

    if(fgetc(fpStudent) == EOF)
    {
        printf("
学生答案的文件是一个空文件!
");
        fclose(fpStudent);
        return 0;
    }
    if(fgetc(fpAnswer) == EOF)
    {
        printf("
标准答案的文件是一个空文件!
");
        fclose(fpAnswer);
        return 0;
    }
    rewind(fpStudent);    //回到文件开头
    rewind(fpAnswer);
    //循环直至读到文件末尾

    /*
    首先对题号进行校对,可能会有漏题、或者题号错误的情况,如果题号出错,直接return
    */

    while(!feof(fpStudent) && !feof(fpAnswer))
    {
        if(fgets(string3,15,fpStudent)==NULL)
            printf("error in string3
");     //把学生答案里的数据读出来存取进字符串string3,且每次读取一行
        if(fgets(string4,15,fpAnswer)==NULL)
            printf("error in string4
");      //把答案中第一行的数据读出来并存进字符串string2中
        p1 = string3;   //p1指向string3
        q1 = string4;   //q1指向string4
        while(*p1!='.')
        {
            if(*p1 == *q1)
            {
                p1++;
                q1++;
            }
            else {      //学生答案中的数字与标准答案的题号对不上(漏题或题号对应错)
                p2 = string3;
                control3 = 0;
                while(*p2!='.')
                {
                    control3++;
                    p2++;
                }
            strncpy(temp2,string3,control3);
            //将.之前的字符转换成字符串再变成int,赋给链表的结点
            InsertInList(modifyList,CreateAnswerNode(temp2));       //将新结点插入错误链表中
            p1++;
            q1++;
            }
        }

     /*   if(*p1=='.' && *q1!='.')
        {

            printf("
学生答案题号有误,可能原因:题号有跳跃或题号对应错误!出现错误的题号如下:
");
            visitAnswerList(modifyList);
            system("pause");
            exit(1);
        }*/
    }
        //判断少题的情况
        if(!feof(fpAnswer) && feof(fpStudent))
        {
            printf("
出现错误!学生答案出现末尾少做题的状况,请重新检查并修改!
");
            exit(1);
        }
        if(!feof(fpStudent) && feof(fpAnswer))
        {
            printf("
出现错误!学生答案中出现比标准答案题数多的情况,请重新检查并修改!
");
            exit(1);
        }

    rewind(fpStudent);
    rewind(fpAnswer);

    /*
    下面是没有漏题或者题号错误的情况下,对答案进行的校对
    */

    while(!feof(fpStudent) && !feof(fpAnswer))                //如果还没有到文件的结尾
    {
        if(fgets(string1,15,fpStudent)==NULL)
            printf("error in string1
");     //把学生答案里的数据读出来存取进字符串string1,且每次读取一行
        //string2出现问题
        if(fgets(string2,15,fpAnswer)==NULL)
            printf("error in string2
");      //把答案中第一行的数据读出来并存进字符串string2中
        p = string1;   //p指向string1
        q = string2;   //q指向string2
        while(*p!='.')
        {
            p++;    //将p指针指向.
        }
        while(*q!='.')
        {
            q++;    //将q指针指向.
        }
        p++;      //p指针指向第一位答案位
        q++;      //q指针指向第一个答案位
        while(*p!='')      //字符串string1都还没结束的时候比较数字位是否相等
        {
            if(*p == *q)  //当前答案位正确,继续比较下一位
            {
                p++;
                q++;
            }
            else {    //p还没到字符串的末尾且p的字符与q的字符不相同(答案错误)
                break;    //发现答案错误,直接不用比较下面的位,跳出循环
            }
        }
        /*跳出循环的原因:
        1、任意一个字符串遍历结束,
        2、发现错误答案*/
        if(*p!=''||(*p=='' && *q!=''))    //错误答案有两种情况:1、某个位上的数字错误,2、数字的位数错误
        {
            p = string1;
            control1 = 0;
            //插入正确的链表
            while(*p!='.')
            {
                control1++;
                p++;
            }
           // printf("%d",control1);
            strncpy(temp,string1,control1);
            //将.之前的字符转换成字符串再变成int,赋给链表的结点
            InsertInList(wrongList,CreateAnswerNode(temp));       //将新结点插入错误链表中
            WrongNum++;
        }
        else if (*p == '' && *q == '')           //正确答案
        {
            p = string1;
            control2 = 0;
            //插入正确的链表
            while(*p!='.')
            {
                control2++;
                p++;
            }
            strncpy(temp1,string1,control2);
            InsertInList(correctList,CreateAnswerNode(temp1));
            CorrectNum++;
        }
    }

         //将正确题号和错误题号都输出:printf();
         printf("
Correct:%d",CorrectNum);
         visitAnswerList(correctList);
         printf("
Wrong:%d",WrongNum);
         visitAnswerList(wrongList);
         if(modifyList->next!=NULL)
         {
            printf("
需要修改的题号为:");
            visitAnswerList(modifyList);
         }
         printf("
");
         fclose(fpStudent);
         fclose(fpAnswer);
         fpStudent = NULL;
         fpAnswer = NULL;
         system("pause");
}

查重函数(加辅助的其他函数):

github地址:https://github.com/GMloria/GGMYING/blob/master/%E7%BB%93%E5%AF%B9%E9%A1%B9%E7%9B%AE%EF%BC%88%E6%9F%A5%E9%87%8D%EF%BC%89

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

#define OK 1
#define ERROR 0


typedef int Status;


//二叉树的结点
typedef struct TreeNode{
    int type;//1表示操作数,2表示运算符
    char op[20];
    char s[20];
    char value[10];//用于存放值,整数或真分数
    struct TreeNode *left;//指向左孩子
    struct TreeNode *right;//指向右孩子
}TreeNode,*TreeList;

//二叉树的数据类型
typedef struct BiTNode{
    TreeList root;//根结点
    int height;//高度
    int opNumber;//包含的运算符数量
}BiTNode,*BiTree;

//二叉树链表的数据类型
typedef struct BiTLNode{
    BiTree T;
    struct BiTLNode *next;
}BiTLNode,*BiTreeList;

    /*
    开始写函数
    */

//初始化一个二叉树链表
BiTreeList InitBiTreeList(){
    BiTreeList B;
    B = (BiTLNode*)malloc(sizeof(BiTLNode));
    if(!B)   return ERROR;
    B->T = NULL;
    B->next = NULL;
    return B;
}

//把构造的二叉树插入二叉树链表
Status InsertBiTreeList(BiTreeList &B,BiTreeList b){
    BiTreeList p;
    p = B;
    if(B == NULL)   return ERROR;
    while(p->next!=NULL)
    {
        p = p->next;
    }
    p->next = b;
    return OK;
}


//初始化一棵二叉树
BiTree InitBiTree(){
    BiTree T;
    T = (BiTNode*)malloc(sizeof(BiTNode));
    if(!T)   return ERROR;
    T->root = NULL;
    T->height = 0;
    T->opNumber = 0;
    return T;
}

//创建一棵二叉树且其根结点
Status CreateBiTree(BiTree &T,TreeList root,int number) {
    if(!T || !root)   return ERROR;
    T->root = root;
    T->height = 0;
    T->opNumber = number;
    return OK;
}

//初始化操作数结点
Status InitNumNode(TreeList &p) {
    p = (TreeList)malloc(sizeof(TreeNode));
    if(p == NULL)   return ERROR;
    p->type = 1;
    strcpy(p->op,"");
    strcpy(p->s,"");
    strcpy(p->value,"");
    p->left = NULL;
    p->right = NULL;
    return OK;
}

//创建操作数结点
Status CreateNumNode(TreeList &p, char value[20]) {
    if(p == NULL)   return ERROR;
    strcpy(p->value,value);
    return OK;
}

//初始化运算符结点
Status InitOpNode(TreeList &p) {
    p = (TreeList)malloc(sizeof(TreeNode));
    if(p == NULL)   return ERROR;
    p->type = 2;
    strcpy(p->op,"");
    strcpy(p->s,"");
    strcpy(p->value,"");
    p->left = NULL;
    p->right = NULL;
    return OK;
}

//创建运算符结点
Status CreateOpNode(TreeList &p, char op) {
    if(p == NULL)   return ERROR;
    char str[1];
    str[0]=op;
    strcpy(p->op,str);
    return OK;
}



//字符串转化为int类型
int StrToInt(char *String) {
    int i,j,pro=1;
    int Count = 0;       //count有可能达到10000
    char temp;
    char *p;
    p = String;
    for(j=0;j<strlen(String);j++)
    {
        pro*=10;    //pro初始化
    }

    for(i=0;i<strlen(String);i++)
    {
        temp = *p;
        pro/= 10;
        Count += (temp-48) * pro;
        p++;
    }
    return Count;
}

//比较两个运算符的优先级,1是a与b同级或a比b高级,2是a比b低级
int testOp(char a, char b) {
    if(a=='+' || a=='-')
    {
        if(b=='*' || b=='/')
        {
            return 0;//a的优先级比b低,+-x/
        }
        else return 1;//a的优先级与b相同+-+-
    }
    else
    {
        if(b == '+' || b == '-')
            return 2;//a是乘或除,优先级最高,*/+-
        else return 1;//*/*/
    }
}

//从分数结构中提取分子和分母
int* fenToInt(char *value) {
    int i,j,k=0,pro=1;
    int *two;
    int *t;
    char temp;
    two = (int*)malloc(sizeof(int)*2);

    for(j=0;j<4;j++)
    {
        two[j]=0;       //多次调用时将数组清空
    }

    for(i=0;i<strlen(value);i++)
    {
        if(value[i]=='/')
            break;
    }
    //初始化j
    for(j=0;j<i;j++)
    {
        pro*=10;
    }
    for(i=0;i<strlen(value);i++)
    {
        if(value[i]=='/')   break; //此时i是有值的,下次从这个i开始即可
        else
        {
            temp = value[i];
            pro/=10;
            two[k] += (temp-48) * pro;

        }
    }
    k++;  //存在int数组的下一个数
    pro = 1;
    i = i + 1; //从除号的下一个数开始算
    for(j=0;j<strlen(value)-i;j++)
    {
        pro*=10;
    }
    for(i;i<strlen(value);i++)
    {
            temp = value[i];
            pro/=10;
            two[k] += (temp-48) * pro;
    }
    t = two;
    return t;
}

//一个整数和一个分数进行比较,返回的是2个分数,通分,这个分数是假分数
int* oneintTotwofen(int a,char *b) {
    int *temp = fenToInt(b);
    int *final;
    final = (int*)malloc(sizeof(int)*4);
    int a1,a2;

    a2 = temp[1];
    a1 = a * a2;
    final[0] = a1;
    final[1] = a2;
    final[2] = b[0];
    final[3] = b[1];
    return final;
}

//比较大小
Status compare(char *Front,char *After)
{
    char *p1,*p2;
    p1 = Front;
    p2 = After;
    int flag1 = 0,flag2 = 0;
    while(*p1 != '')
    {
        if(*p1 == '/')
        {
            flag1 = 1;
            break;
        }
    }
    while(*p2 != '')
    {
        if(*p2 == '/')
        {
            flag2 = 1;
            break;
        }
    }
    //如果两个数都是整数且相等,则返回0
    if(flag1 == 0 && flag2 == 0)
    {
        int integer1 = StrToInt(Front);
        int integer2 = StrToInt(After);
        if(integer1 == integer2)
            return 0;
    }
    //其中一个是分数
    if(flag1 == 0 && flag2 != 0)
    {
        int integer = StrToInt(Front);
        int *temp1 = oneintTotwofen(integer,After);
        if((temp1[0] == temp1[2]) && (temp1[1]==temp1[3]))
            return 0;
    }
    if(flag1 != 0 && flag2 ==0)
    {
        int integer = StrToInt(After);
        int *temp2 = oneintTotwofen(integer,Front);
        if((temp2[0] == temp2[2]) && (temp2[1]==temp2[3]))
            return 0;
    }
    //两个都是分数
    if(flag1 == 1 && flag2 == 1)
    {
        int *temp1 = fenToInt(Front);
        int *temp2 = fenToInt(After);
        if((temp1[0] == temp1[2]) && (temp1[1]==temp1[3]))
            return 0;
    }
    return 1;
}

//判断两个数的大小,真分数转化为假分数,且对两个数相减
int judge(int num1[10],int num2[10]) {
    int temp2;
    temp2 = num1[1];
    num1[0] = num1[0] * num2[1];//此时b2不变,a1改变
    num1[1] = num1[1] * num2[1];//此时b2不变,a2改变
    num2[0] = num2[0] * temp2;
    num2[1] = num2[1] * temp2;
    if(num1[0] >= num2[0])
        return 1;    //第一个数比第二个数大
    else return 0;//第一个数小,需要交换
}



//调整二叉树为左子树永远比右子树大
Status AdjustTree(TreeList &root) {

    if(root == NULL)   return ERROR;
    TreeList temp;
    temp = (TreeNode*)malloc(sizeof(TreeNode));
    if(root->left!= NULL)
    {
        printf("");
        AdjustTree(root->left);
    }
    if(root->right != NULL)
    {
        printf("");
        AdjustTree(root->right);
    }
//    if(root->left->left == NULL && root->left->right == NULL)
//    {
//        printf(" u ");
//       AdjustTree(root->left);
//     }
    if(root->left != NULL && root->right != NULL)
    {
        printf("");
        if(judge(fenToInt(root->left->value),fenToInt(root->right->value))==0)

//        if(compare(root->left->value,root->right->value)==0)
        {
            printf("");
            temp = root->right;
            root->right = root->left;
            root->left = temp;
        }
    }
    return OK;
}

/*
    首先每次一道题都生成一棵二叉树
    查重部分:当新生成一道题时先与链表中已有的所有结点比较树的结构,
    然后如果与树的结构相同再查结点值,如果不同再插入二叉树链表中,
    最后题目是遍历二叉树所得结果
    如果发现父节点是负号,就先将左孩子减右孩子,如果是负数,则左右孩子交换
*/


//打印值的函数
Status PrintElem(char value[20],int m){
   int i;
   if(m == 1)
   {
           for(i = 0;i<strlen(value);i++)
           {
               printf("%c",value[i]);
          }
   }
   if(m == 2)
  {
        printf("%c",value[0]);
   }
   printf(" ");
   return OK;
}


//后序遍历整棵二叉树
Status AfterOrderTraverse(TreeList T,Status (*visit)(char String[20],int i)){
     if(NULL==T)   return OK;
     if(ERROR==AfterOrderTraverse(T->left,visit))
        return ERROR;
     if(ERROR==AfterOrderTraverse(T->right,visit))
        return ERROR;
     if(T->type == 1)
        return visit(T->value,1);
     else if(T->type == 2)
        return visit(T->op,2);
}

//把操作数和运算符构造一棵表达式二叉树,number全部用整数或分数表示
BiTree CreateQues(char *op,char **number,int opnum,int NUM) {
  //  TreeList n1,n2,n3,n4,o1,o2,o3;
      printf("
");

    TreeList *num;
    TreeList *operation;
    num = (TreeList*)malloc(sizeof(TreeList)*4);
    operation = (TreeList*)malloc(sizeof(TreeList)*3);   //指向运算符结点的指针

    BiTree T = InitBiTree();//建一棵二叉树,为T
    int i,j;
    //int numCount = sizeof(number)/sizeof(number[0]);  //获取操作数个数
    int OpNum = opnum;//获取运算符个数
    int numCount = NUM;
    char *formula[20];
  //  printf("%d,%d
",OpNum,numCount);

    TreeList p,temp,temp1;
    /*
        构造操作数结点
    */

    for(i=0;i<numCount;i++)   //numCount最小值是2
    {
        InitNumNode(num[i]);    //num[i]是二叉树结点的指针,初始化结点指针
        CreateNumNode(num[i],number[i]);    //把值赋给num[i]指向结点的value域
       // PrintElem(num[i]->value,1);
    }



    for(j=0;j<OpNum;j++)
    {
        InitOpNode(operation[j]);
        CreateOpNode(operation[j],op[j]);
      //  PrintElem(operation[j]->op,2);
    }

    T->root = operation[0];     //把第一个运算符作为根
    T->root->left = num[0];  //把第一个操作数作为根的左孩子
    T->opNumber = OpNum;

    if(OpNum == 1)
    {
        T->root->right = num[1];
        T->height = 2;
    }

    if(OpNum == 2)
    {
        if(testOp(op[0],op[1])==1 || testOp(op[0],op[1])==2)
        {
            temp = T->root;
            T->root = operation[1];
            T->root->left = temp;
            temp->right = num[1];
            T->root->right = num[2];
        }
        else if(testOp(op[0],op[1])==0)
        {
            T->root->right = operation[1];
            temp = T->root->right;
            temp->left = num[1];
            temp->right = num[2];
        }
        T->height = 3;
    }

    if(OpNum == 3)
    {
        if(testOp(op[0],op[1])==1 || testOp(op[0],op[1])==2)
        {
            temp = T->root;
            T->root = operation[1];
            T->root->left = temp;
            temp->right = num[1];
            T->root->right = num[2];
        }
        else if(testOp(op[0],op[1])==0)
        {
            T->root->right = operation[1];
            temp = T->root->right;
            temp->left = num[1];
            temp->right = num[2];
        }
        T->height = 3;


        //判断第三个运算符
        if(testOp(T->root->op[0],op[2])==1 || testOp(T->root->op[0],op[2])==2)
        {
            temp = T->root;
            T->root = operation[2];
            T->root->left = temp;
            T->root->right = num[3];
            T->height = 4;
        }
        else if(testOp(T->root->op[0],op[2])==0)
        {
            temp = T->root->right;
            T->root->right = operation[2];
            temp1 = T->root->right;   //即temp1指向第三个运算符结点
            temp1->left = temp;
            temp1->right = num[3];

            if(testOp(op[0],op[1])==1 ||testOp(op[0],op[1])==2)
            {
                T->height = 4;
            }
            else if(testOp(op[0],op[1])==0)
            {
                T->height = 3;
            }
        }
    }
        return T;
}





//查重

//比较树的结点
int compareNode(TreeList node1,TreeList node2) {//1是不重复,0是重复 
    if(node1->type != node2->type)
    {
        return 1;
    }
    else
    {
        int l=0,r=0;
        if(node1->left != NULL && node2->left != NULL)
        {
            l = compareNode(node1->left,node2->left);
        }
        if(node1->right != NULL && node2->right != NULL)
        {
            r = compareNode(node1->right,node2->right);
        }
        int *temp1,*temp2;

        if(l == 0 && r == 0 && (compare(node1->value,node2->value)==0))
        {
            return 0;
        }
        return 1;
    }
}

int compareBiTree(BiTreeList T1,BiTreeList T2) {//1是指重复,0是指不重复 
    //树的运算符数量不同
    BiTree T11 = T1->T;
    BiTree T22 = T2->T;
    if(T11->opNumber != T22->opNumber)
    {
        printf("运算符个数不同
");
        return 0;
    }
    //树的高度不同
    if(T11->height != T22->height)
    {
        printf("树的高度不同
");
        return 0;
    }
    if(compareNode(T11->root,T22->root)==1)
    {
        printf("树的运算符个数和高度都相同,但结点不同
");
        return 0;
    }
    return 1;
}

//每生成一道题目,则遍历一遍二叉树链表
int TraverseBiTList(BiTreeList B,BiTreeList b) {
    InsertBiTreeList(B,b);
    BiTreeList p;
    p = B->next;
    int i = 0;
    int number = 0;
    while(p!=NULL)
    {
        int compare = compareBiTree(p,b);
        if(compare == 1)
        {
            number = 1;//1表示重复
            break; 
        }
        if(compare == 0)
            p = p->next;
        i++;
    }
    return number;//0表示不重复  
}

int main() {
    char op1[3]={'+'};
    char *number1[10] = {"1/14","1/12"};
    char op2[3]={'+'};
    char *number2[10] = {"1/14","1/12"};

    BiTree T1,T2;
    
    T1 = CreateQues(op1,number1,1,2);
    AfterOrderTraverse(T1->root,PrintElem);

    T2 = CreateQues(op2,number2,1,2);
    printf("
");
    AfterOrderTraverse(T2->root,PrintElem);

    printf("
");
    AdjustTree(T1->root);
    AfterOrderTraverse(T1->root,PrintElem);

    printf("
");
    AdjustTree(T2->root);
    AfterOrderTraverse(T2->root,PrintElem);
    
    
    //初始化一个二叉树链表
    BiTreeList Que;
    Que = InitBiTreeList();
    
    BiTreeList T1List,T2List;
    T1List = InitBiTreeList();
    T1List->T = T1;
    
    T2List = InitBiTreeList();
    T2List->T = T2;
    
    
    InsertBiTreeList(Que,T2List);
    printf("
compare = %d
",TraverseBiTList(Que,T1List));
    

    return 0;
}

主函数中的代码

运行界面函数

    time_t t;
    srand((unsigned) time(NULL));

    int r;
    printf("请问想要的操作是 1.生成四则运算题目
");
    printf("                 2.对答案
");
    printf("请输入你的选择: ");
    scanf("%d",&r);
    if(r==1)
    {
        int n1,n2,n3,n4,n5,n6,n7,n8;
        int a1,a2,a3,a4,a5,a6,a7,a8;
        char *string2;
        FILE *fp=fopen("题目.txt","w");
        FILE *answerfp=fopen("answer.txt","w");
        int i,count;
        int j;
        int number;
        printf("请输入题数  count = ");
        scanf("%d",&count);           //count道题目
        printf("请输入多少以内四则运算的题目  number = ");
        scanf("%d",&number);           //number以内的四则运算

        int a;
        printf("请输入想要题目的操作符个数  a = ");
        scanf("%d",&a);            //a个操作符
        char ziFu[a];
         ziFu[a]    = {0};

        int* main_chufa;
        int chufa[2]= {0};

         //操作符的数组
        char op[20];
        char src[100];
        char src1[100];
        char src2[100];

        //操作数字符串数组
        char numbers[5][100];
//        char *h;
//        h=numbers;
//当想要整数四则运算的时候
        int x;
        printf("****************************
");
        printf("如果想要整数四则运算请输入1
");
        printf("如果想要包含分数四则运算请输入2
");
        printf("请输入你的选择:");
        scanf("%d",&x);
 

写入题目----由于运算符不同所写入的题目不同 以以下两个为例

一个运算符的整数四则运算(两个和三个运算符的计算在git上)

 for(i=1,j=1; i<=count,j<=count; i++,j++) /*j为题目的序号,i为答案的序号*/
            {
                string2 = caozuofu(ziFu,a);
                n1=rand()%number+1;n2=rand()%number+1;n3=rand()%number+1;n4=rand()%number+1;
                if(a==1)               //一个操作符
                {
                    fprintf(fp,"%d. ",j);//写题目
                    if(string2[0] == '-')
                    {
                        if(n1>=n2)
                        {
                            fprintf(fp,"%d  %c  %d = 
",n1,string2[0],n2);
                        }
                       else
                        {
                           int temp;
                           temp=n1;n1=n2;n2=temp;
                           fprintf(fp,"%d  %c  %d = 
",n1,string2[0],n2);
                        }
                    }
                    else
                    {
                        fprintf(fp,"%d  %c  %d = 
",n1,string2[0],n2);
                    }
                    //整数时一个操作符的答案计算
                    fprintf(answerfp,"%d. ",i);
                    switch(string2[0])
                    {
                    case '+':
                        {
                        fprintf(answerfp,"%d
",n1+n2);
                        break;
                    }
                    case '-':
                        {
                        if(n1<n2)
                        {
                        fprintf(answerfp,"%d
",n2-n1);
                        }
                        else
                        {
                            fprintf(answerfp,"%d
",n1-n2);
                        }
                        break;
                    }
                    case '*':
                        {
                        fprintf(answerfp,"%d
",n1*n2);
                        break;
                        }
                    case '/':
                        {
                        zhenfenshu(n1,n2,answerfp);
                        break;
                        }
                    }//switch的大括号
                    op[0]=string2[0];
                    itoa(n1, src, 10);
                    strcpy(numbers[0],src);
                    itoa(n2, src, 10);
                    strcpy(numbers[1],src);
                }//if(a==1)的大括号
//整数计算--两个运算符时
}

一个运算符的分数四则运算(两个和三个运算符的计算在git上)

            int num[2]= {0};
            int* main_floatchu;
            for(j = 1,i = 1; i <= count,j<=count; i++,j++)
            {
                string2 = caozuofu(ziFu,a);
                n1 = rand()%number+1;
                n2 = rand()%number+1;
                n3 = rand()%number+1;
                n4 = rand()%number+1;
                n5 = rand()%number+1;
                n6 = rand()%number+1;
                n7 = rand()%number+1;
                n8 = rand()%number+1;
                if(a == 1)               //一个操作符string2[0]
                {
                    switch(string2[0])
                    {
                    case '+'://n1/n2 +n3/n4
                    {
                        int fenzi=n1*n4+n3*n2;
                        int fenmu=n2*n4;
                        fprintf(fp,"%d. ",j);
                        main_floatchu=int_chuFa(n1,n2,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," %c ",string2[0]);
                        main_floatchu=int_chuFa(n3,n4,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," = 
");
                        fprintf(answerfp,"%d.",i);
                        zhenfenshu(fenzi,fenmu,answerfp);
                        break;
                    }
                    case '-'://n1/n2-n3/n4
                    {
                        int fenzi=n1*n4-n3*n2;
                        int fenmu=n2*n4;
                        if(fenzi<0)
                        {
                            i--;
                            j--;
                            continue;
                        }
                        else
                        {
                            fprintf(fp,"%d. ",j);
                            main_floatchu=int_chuFa(n1,n2,num);
                            if(num[1]==1)
                            {
                                fprintf(fp,"%d",num[0]);
                            }
                            else
                            {
                                fprintf(fp,"%d/%d",num[0],num[1]);
                            }
                            fprintf(fp," %c ",string2[0]);
                            main_floatchu=int_chuFa(n3,n4,num);
                            if(num[1]==1)
                            {
                                fprintf(fp,"%d",num[0]);
                            }
                            else
                            {
                                fprintf(fp,"%d/%d",num[0],num[1]);
                            }
                            fprintf(fp," = 
");
                            fprintf(answerfp,"%d.",i);
                            zhenfenshu(fenzi,fenmu,answerfp);
                        }
                        break;
                    }
                    case '*'://n1/n2*n3/n4
                    {
                        int fenzi=n1*n3;
                        int fenmu=n2*n4;
                        fprintf(fp,"%d. ",j);
                        main_floatchu=int_chuFa(n1,n2,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," %c ",string2[0]);
                        main_floatchu=int_chuFa(n3,n4,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," = 
");
                        fprintf(answerfp,"%d.",i);
                        zhenfenshu(fenzi,fenmu,answerfp);
                        break;
                    }
                    case '/'://n1/n2/n3/n4
                    {
                        int fenmu=n2*n3*n4;
                        fprintf(fp,"%d. ",j);
                        main_floatchu=int_chuFa(n1,n2,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," %c ",string2[0]);
                        main_floatchu=int_chuFa(n3,n4,num);
                        if(num[1]==1)
                        {
                            fprintf(fp,"%d",num[0]);
                        }
                        else
                        {
                            fprintf(fp,"%d/%d",num[0],num[1]);
                        }
                        fprintf(fp," = 
");
                        fprintf(answerfp,"%d.",i);
                        zhenfenshu(n1,fenmu,answerfp);
                        break;
                    }
                    }//switch  op[0]=string2[0];
                    main_floatchu=int_chuFa(n1,n2,num);
                    itoa(num[0],src1,10);
                    itoa(num[1],src2,10);
                    strcat(src1,"/");
                    strcat(src1,src2);
                    strcpy(numbers[0],src1);
                    op[0]=string2[0]; 
                    main_floatchu=int_chuFa(n3,n4,num);
                    itoa(num[0],src1,10);
                    itoa(num[1],src2,10);
                    strcat(src1,"/");
                    strcat(src1,src2);
                    strcpy(numbers[1],src1);
                }//a==1

调用对答案函数

 if(r==2)
    {
        checkAnswer();
    }
    return 0;

八、测试结果

一个操作符的整数运算:

两个操作符的整数运算:

三个操作符的整数运算:

分数的四则运算

一个操作符的分数运算:

两个操作符的整数运算:

三个操作符的分数运算:

当找不到学生文件时:

生成10000道题时:

整数:

分数:

八、PSP表

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 60

90

· Estimate

· 估计这个任务需要多少时间

 60

 90

Development

开发

 1910

 2717

· Analysis

· 需求分析 (包括学习新技术)

 100

150 

· Design Spec

· 生成设计文档

 40

65 

· Design Review

· 设计复审 (和同事审核设计文档)

 30

32 

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 40

30 

· Design

· 具体设计

 200

210

· Coding

· 具体编码

 1000

1500

· Code Review

· 代码复审

 200

250 

· Test

· 测试(自我测试,修改代码,提交修改)

 300

480 

Reporting

报告

180

225 

· Test Report

· 测试报告

 30

35 

· Size Measurement

· 计算工作量

 30

40 

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 120

150 

合计

 

 2150

 3032

九、项目小结

刘珮琳:

1.成败得失:

  这次重新通过这个项目温习了二叉树,之前在学习数据结构的时候因为基本功打得还是一般,所以这次用二叉树来实现查重功能其实是对我的一个考验,而且C语言经过一个学期之后稍显生疏,这个项目也帮助我重新把C语言捡起来了。

  成功的地方,整体来说是两个人的交流还是很多的,之前我是有想法但不太会表达出来的人,但是因为这次结对的伙伴是熟悉的人,所以很多心里的想法、思路都会及时地分享出来,然后大家一起思考这个方法是否可行。定下了大概思考的方向之后会大大地提高做项目的效率,也让我体验到不是孤军奋战的感觉还是很好的,这是我在这次项目中最深的感受。

  失败的地方也是存在的。比如有时候我觉得这个部分很难,然后会有畏难情绪,情绪一旦无法高涨,效率就会大打折扣,写出来的代码也逻辑凌乱。所以后期在继续做查重这个部分的时候就被这个畏难情绪影响,进展也相对变缓慢了。除此之外,我觉得不足的地方还是对知识、高级语言的熟练程度不够,导致中间在编写代码时有卡顿需要查询资料的。

 

  得的话,应该是再巩固了知识,然后熟悉了结对项目的大致流程,互相的鼓励、陪伴真的很重要。有得必有失,失去的大概是我的头发和我的健康吧,总体来说还是得大于失的。

 

2.分享经验:

·结对的两个人一定要多沟通,互相督促,互相鼓励,互相进步。

·写项目时为了减少在中间重新捡起来原本知识的时间,建议用自己最熟悉的语言来写,写起来真的会快很多,思路也会流畅很多。

·当遇到问题时,先静下心来想想,想不出来就让伙伴一起帮忙顺思路,过程中也有遇到过因为小小的换行符或者空格而导致程序运行出现错误的情况,而写代码的人往往是无意识的或者是记住了错的知识点,这时另外一个人及时的检查就非常有必要了。

·在较大型的项目中,建议是边debug边写,出现问题的时候及时修改,防止因为这个小问题导致后面所有涉及到此处的都会出错。而且如果到最后一运行发现到处是错误的时候,这时候的心态比较容易崩,失去耐心之后想要重新鼓起勇气开始是需要一定时间的。

 

3.总结教训:

 

·好好学、精学高级语言,就算没有相关课程也可以通过日常的小练习来加强对这门语言的熟悉程度。

·发现问题及时沟通,不需要介意提出问题是否会被对方否定,说不定就被采用了。

·边debug边写代码,及时发现错误。

·可以在细心检查代码和代码的规范上下点功夫,如缩进等。合适的缩进会使得代码的可读性大大提高,不同的人的代码风格是不一样的,但是尽量统一,否则阅读起来的时间也会变长。

4.结对感受:

  总体来说挺好的!因为在写的过程中,对方看着你写,或者突然大脑宕机的时候,伙伴会及时提醒,这样子就能迅速回到状态上去。然后感受了一下和别人合作写项目和自己开发项目的区别。当我的热情不高,打码欲望不强烈的时候,伙伴就会及时来找我,然后大家一起写,感觉就会好很多!

 

5.对方的闪光点

  我很喜欢明莹的行动力。因为在家写代码,总感觉积极度很低。然后明莹就经常会突然出现,问我这个部分的思路是不是这样的,和我很认真地讨论某个方法的可行性。并且我觉得她和我的沟通是很好的,大家也没有什么拘着的,思维碰撞的感觉总是让我觉得很棒!她真的是我的榜样,而且有时候我遇到问题的时候她就会来和我一起分析、解决,和我一起理清我跳跃的思路,当我觉得很麻烦的时候她也会让我给点耐心,一步一步来,这次结对项目感觉是次很棒的经历。

 

 

 

高明莹:

1.结对感受:

在本次结对项目中,第一次两个人共同完成一个项目,在项目初期,我们结对成员进行了会议交流,初步确定了该项目需要实现的功能以及对各个功能需要如何实现进行了探讨,并将功能函数的撰写分别分配到两人,一人写几个功能函数,遇到问题两人一起探讨,在两人分别写功能函数的期间,也多次连麦,共享屏幕来说自己的想法以及遇到的问题。在此次结对项目中,我学习到了很多以前不清楚的代码知识,也总结到了在代码的书写过程中要保证细节的准确性,否则无法得到正确结果,多次检查可能还发现不了问题所在。通过此次结对的感受,我发现通过两个人的讨论,思考,会比自己一个人的时候思考的更多,对于代码的理解更全面

2.成败得失:

已解决并学到的知识:

       1.rand()函数随机出来的数字是一样的------通过资料的搜索知道了需要设置时间种子,才可以随机出不同的数字

       2.知道了srand()函数必须放在循环外面,否则也会得到相同的随机数

       3.在整个文件中只能存在一个srand()函数

       4.二维数组的问题,在查重功能的实现中,需要我传递一个操作符的字符数组以及包含操作数的字符串数组,一开始用char *numbers[20]这个数组定义的,然后发现虽然没有报错,但是发现需要写进题目.txt和answer.txt的题目和答案是写不上的,寻找了一段时间的原因尝试了各种方法,然后找到了结对伙伴,两人一起商讨出来了解决办法

       5.学到了itoa()函数的用法,即将一个整数转化为字符串

3.自身的不足:

在书写功能函数方面的不足:由于在书写过程中为了让自己不那么混乱,代码书写的行数较多,想要化简代码的时候发现有的简化不是很可以,所以代码的行数就很多。自己的能力也不是很好,debug也经常找不到问题的出处,所以花费了很多时间在gebug上,在今后我会加强自身的能力,并加以使用。

4.对方的闪光点以及建议:

我从珮琳身上发现了一些好的书写代码习惯,在以后的代码书写中我会加以运用,在此次项目中,我也从珮琳那里学到了如何去测试哪个函数的哪个部分出了问题,由于函数的繁多,有时运行界面会卡住,需要找到是哪里出了问题,否则将无法得到正确答案而且珮琳对待代码真的很有耐心,并且有好的代码书写规范,思考问题思考的比较全面,知识点掌握的很牢固。

原文地址:https://www.cnblogs.com/yyyy118/p/12702717.html