一、课程设计题目
任意长整数的加减乘运算 说明:设计一个程序实现两个任意长整数的加减乘运算。
操作环境:Visual Studio2019或Code::Blocks16.01
二、课程设计主要内容和要求
1.利用双向循环链表,设计一个实现任意长的整数进行加减乘运算的演示程序。
2.要求输入和输出每四位一组,组间用逗号隔开。如:1,0000,0000,0000,0000。
3.计算器的仿真界面。
三、详细设计
3.1 数据结构
实现任意长整数的加减乘运算操作,为方便未知长度的长整数进行运算和存储,所以使用双向循环链表来存储任意长整数,例一个存储长整数“+123”的链表结构如下: 由图3-1可知,双向循环链表的第一个结点,即“头”结点存储数据的符号位(整型),头结点的后一个结点为第一位数据,头节点的前一个结点为双向循环链表的“尾”,存储的是数据的最后一位。
链表的每一个结点是一个结构体,包含数据部分、前向指针和后向指针三部分。结点结构如下图所示:结构体详细定义如下:
typedef struct ListNode{
//结构体,双向循环链表结点
int data; //结点数据(特别地符号“+”存储为整型43,“-”存储为整型45)
struct ListNode *pre; //前向指针
struct ListNode *next; //后向指针
}Node, *NodeList;
3.2 功能模块划分
长整数的加减乘运算,主要分为四个大的功能模块,分别为主调程序模块、预处理模块、输入、输出模块和主计算模块。其中,主调程序模块的主要功能就是控制程序的执行流程和界面的输出;预处理模块的主要作用就是一些执行主要操作过程中会调用到的一些功能的实现;输入、输出模块主要包括输入模块和输出模块,实现长整数的输入和输出以及其规范化;主计算模块包括加法操作模块、减法操作模块和乘法操作模块,实现长整数的加减乘运算。
3.3 系统包含的函数
系统主要包含的函数如下:
//预处理模块
int length(NodeList longint) //计算链表长度的函数,参数为链表,返回值为数据部分结点个数;
int compare(NodeList &longint1, NodeList &longint2) //比较两个链表绝对值的大小;
void removen(NodeList &longint) //删除链表(析构);
//输入、输出模块
int check(char a[], int m) //检查键入字符串格式;
void input(NodeList &longint) //输入字符串型长整数,去掉分隔符后在链表中存储,并引用带回;
void output(NodeList &longint) //将参数链表中的数据每四位一组分组,加上分隔符后打印输出;
//主运算模块
void same_add(NodeList &longint1, NodeList &longint2, NodeList &result)
//基本加法操作;
void same_sub(NodeList &longint1, NodeList &longint2, NodeList &result)
//基本减法操作;
void add(NodeList &longint1, NodeList &longint2, NodeList &result)
//有符号的加法操作;
void sub(NodeList &longint1, NodeList &longint2, NodeList &result) //有符号的减法操作;
void multiply(NodeList &longint1, NodeList &longint2, NodeList &result) //乘法操作;
//主调程序模块
void welcome() //打印欢迎界面
void print() //打印操作界面
void handle_do() //主处理函数
int main() //主函数
函数间调用关系如下:
3.4 功能模块详细介绍
3.4.1 系统主要执行流程如下:
3.4.2 输入模块
输入模块的主要功能是实现长整数的键入并将其规范化后存储在链表中,主要流程为先输入字符串型的长整数,以字符数组形式存放,并检查输入格式是否正确,不正确的话提示重新输入,直至输入格式正确为止,然后在将字符数组中除分隔符外的符号位和数据部分存储在双向循环链表中(不存储分隔符是为了后续计算的方便性)。最后进行一些异常数据的处理,如果输入的仅有符号位和分隔符,如(“+,”)则在链表中存储为有符号的0(“+0”或“-0”),如果输入的数据以多位0开头(如“+0000001”)则将数据前的0删除后再存储在链表中(链表中存储“+1”)。
3.4.3 输出模块
输出模块实现的功能主要是将参数链表中的数据加上分隔符以四位一组的形式输出,主要解决的是数据规范化的问题。我的思路是遍历输出链表中的数据,标记需输出分隔符的地方并打印分隔符“,”即可。
3.4.4 加、减法模块
由于加减法代码基本类似且思路相同,这里仅以加法操作为例详细介绍:
加法操作对输入的长整数类型不同共有8中不同情况,假设输入的两个长整数为L1、L2,则:
当两个操作数(L1、L2)符号相同的情况下都为正数时,结果为+(L1+L2);都为负数时,结果为-(L1+L2);
当两个操作数(L1、L2)符号相反L1符号为正、L2符号为负 L1的绝对值大于L2的绝对值,相当于+(L1-L2);L1的绝对值小于L2的绝对值,相当于-(L2-L1);L1的绝对值等于L2的绝对值,结果为0。
当两个操作数(L1、L2)符号相反L1符号为负、L2符号为正且L1的绝对值大于L2的绝对值,相当于-(L1-L2);L1的绝对值小于L2的绝对值,相当于+(L2-L1);L1的绝对值等于L2的绝对值,结果为0。
3.4.5 乘法模块
乘法计算模块实现的功能就是两个长整数的相乘,当两个长整数中有一个存储数据为0时,结果为0。否则,当两个长整数符号相同时结果符号位为+,符号相反时,结果符号位为-,然后定义两个结构体指针,按照我们通常乘法计算的习惯,使orp1指向两个操作数中绝对值较大的一个,orp2指向另一个,然后从orp2的最后一位开始,逐个乘orp1的每一位,将结果存入链表pp1中,使用合适的逻辑,每次循环将pp1适当移位后累加即可得到乘法计算的结果。
四、总结
我觉得双向循环链表的作用大概就是,在进行加减乘法操作的时候,需从两个存放大整数的链表的最后一位开始进行加减乘操作,得到的结果存入结果新链表(事先未知长度)的最后一位。使用s = p->pre,可以直接将结构体指针s定位到链表p最后一位,而不用去遍历链表p,也不用像数组一样事先分配空间,有效的节约了空间和时间。其中,循环的作用体现在,可以操纵事先未知长度的链表;双向的作用体现为插入、删除结点的方便性。
五、附录(代码)
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;
#define MAX 1000
typedef struct ListNode{
//结构体,双向循环链表
int data; //结点数据
struct ListNode *pre; //前向指针
struct ListNode *next;//后向指针
}Node, *NodeList;
//============================================输入======================================================
//-----------------检查键入字符串格式-------------------------
//第一个字符为符号位,其余每一位均在0-9之间或者是逗号,多个分隔符与一个相同
int check(char a[], int m){
if(a[0] == '+' || a[0] == '-'){
for(int i = 1; i < m; i++){
if((int(a[i]) <= 57 && int(a[i]) >= 48) || a[i] == ','){
if(i == m-1)
return 1;
}
else
return -1;
}
}
else
return -1;
}
//-------------------------------输入字符串型长整数,去掉分隔符后存入链表中---------------------------------------
void input(NodeList &longint){
longint = new Node;
longint -> pre = longint; //初始化结构体变量
longint -> next = longint;
int flag1 = 0;
int num = 0;
int len = 0;//字符串长度
string str;
char arr[MAX];//字符数组来存放长整型
cin >> str;//输入一个字符串
len = str.length();
for(int i = 0; i < len; i++){
//字符串赋值给字符数组,调用check函数
arr[i] = str[i];
}
flag1 = check(arr, len);
while(flag1 == -1){
//当输入格式不正确时提示,直至输入格式正确
cout << "请重新按照格式输入这个长整数! " << endl;
cin >> str;
len = str.length();
for(int i = 0; i < len; i++){
arr[i] = str[i];
}
flag1 = check(arr, len);
}//while
if(arr[0] == '+'){
longint->data = int('+');//字符'+'存储为整型43
}
if(arr[0] == '-'){
longint->data = int('-');//字符'-'存储为整型45
}
NodeList prenode = longint;
for(int i = 1; i < len; i++){
//将字符串中每一位使用循环列表存放
if(arr[i] != ','){
num = arr[i] - 48;//字符型转换为整型
NodeList newn = new Node;
newn -> data = num;
newn -> pre = prenode;
newn -> next = longint;
prenode -> next = newn;
longint