2.数据结构详细设计
typedef struct DualNode {
int data;
struct DualNode *prior, *next; }DualNode, *DualList;
双向循环链表的节点由三个部分组成,第一是数据部分data存储此节点的数据,第二是此节点的前驱指针部分*prior指向此节点的前驱,第三是此节点的后继指针部分*next指向此节点的后继。数据部分我们约定它为整形变量,前驱后继指针均为结构体DualNode类型。
3.输入函数与初始化函数设计
DualList InputData() {
int FirstNum = 1, data; char c; DualList L;
L = (DualList)malloc(sizeof(DualNode)); L->next = L->prior = L;
printf(\"Please Input as Format: -1234,1234,1234\\n\"); if ((c = getchar()) == '-') L = InitList(-1); else
L = InitList(1);
if (isdigit(c)) //判断是否为10进制数 // 退格处理 ungetc(c, stdin); do{
scanf(\"%d\
while(data>9999) data=data/10; InsertNodeAtTail(L, data); }while((c = getchar()) != '\\n'); printf(\"Your Input is:\\n\"); PrintList(L); return L; }
该函数在其内创建了一个双向函数链表,通过返回该链表完成初始化,减少函数个数,降低复杂度。L = (DualList)malloc(sizeof(DualNode))为开辟存储空间,L->next = L->prior = L将其前驱、后继都指向L,构成循环。调用的函数InitList()是为了在头结点存放符号。
ungetc()的使用,是因为将本该输入却被取出的数返回到输入流中去,以便正常输入。do-while语句是为了当输入回车时,能够停止。 while(data>9999) data=data/10
该句是为了当输入错误,超过4位时,取前四位当做正常输入存储。PrintList(L)打印存储的链表,以便观察输入的长整数是否符合预期,并在该函数中,解决当输入小于4位的错误的情况,在前补0存储。
4.符号位的存放
DualList InitList(int sign) {
//头结点存放符号位,1为正,-1为负 DualList L;
L = (DualList)malloc(sizeof(DualNode)); L->next = L->prior = L; L->data = sign; return L; }
建立一个双向循环链表,并将其返回。在头结点存储其符号位,1为正,-1为负。
5.符号位的初步判断
DualList AddList(DualList a, DualList b) //判断符号 {
DualList c;
if (a->data * b->data > 0) {
c = InitList(a->data); Add(a, b, c); } else {
c=InitList(b->data); Sub(a, b, c); }
return c; }
当a,b符号相乘大于0时,即相同时,运算后c的符号一定等于a的符号,执行Add()函数。
当a,b符号相乘小于0时,即相异时,运算后c的符号不一定等于b的符号。 因为|a|可能大于|b|,则需在Sub()函数,进一步进行判断。
6.尾插用于输入数据的存储
void InsertNodeAtTail(DualList L, int data) //在链表最后插入
{
//尾插,用于存储数据的输入 DualNode *s;
s = (DualList)malloc(sizeof(DualNode)); s->data = data; s->next = L;
s->prior = L->prior; L->prior->next = s; L->prior = s; }
例如:输入1,2345
先是1被存入一个新的结点,在插入L的末尾,之后2345再存入结点,插入末尾,如此一来,方便数据的调用。
7.头插用于运算后数据的存储
void InsertNodeAtHead(DualList L, int data) {
// 即插在头结点之后,用于计算结果的存储 DualNode *s;
s = (DualList)malloc(sizeof(DualNode)); s->data = data; s->next = L->next; s->prior = L; L->next->prior = s; L->next = s; }
例如:a=1234,5678 b=1,0001。
在本程序中,无论加减运算都从最后四位向前运算,则在头结点后存储,即为顺序存储,符合人的思维逻辑。即先在头结点存入5679,再在头结点后存入1235, 这样存储着为:1235,5679。
8.打印
void PrintList(DualList L) {
//打印
int FirstTime = 1; DualNode *p = L;
if (p->data == -1&&p->next->data!=0) printf(\"-\"); p = p->next; while(p != L) {
if (FirstTime) {
FirstTime = 0; printf(\"%d\ } else {
printf(\ }
p = p->next;
}
printf(\"\\n\"); }
if (p->data == -1&&p->next->data!=0) printf(\"-\")
首先判断头结点的符号并将其输出。FirstTime的作用是判断是否为前四位,当FirstTime=0时,则需要在前输出逗号。在本函数中,printf(\p->data)解决了当输入小于4位时的输入错误,通过在前补0的方法。
9.加预算
void Add(DualList a, DualList b, DualList c) {
DualList pa, pb; int carry = 0, tmp; pa = a->prior; pb = b->prior;
while((pa != a) && (pb != b)) {
tmp = pa->data + pb->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; }else
carry = 0;
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior;
}
while(pa != a) {
// pb = b
tmp = pa->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; } else
carry = 0;
InsertNodeAtHead(c, tmp); pa = pa->prior;
}
while(pb != b) {
// pa = a
tmp = pb->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; } else
carry = 0;
InsertNodeAtHead(c, tmp); pb = pb->prior;
}
if (carry != 0)
InsertNodeAtHead(c, 1); }
本函数主要由3个while语句和一个if语句构成。
首先int carry = 0, tmp; carry代表着进位,开始为0。tmp为存储运行中的结果。 pa = a->prior; pb = b->prior;
因为a,b为双向循环链表,pa,pb指向其前驱,则为a,b的末尾。 例如:a=1,1111 b=2,2222 此时pa->data=1111,pb->data=2222,符合运算顺序。
第一个while语句:
while((pa != a) && (pb != b))
这是用来将a,b从后开始相同长度的结点数据进行运算。 tmp = pa->data + pb->data + carry; 考虑进位的正常加法 if (tmp >= 10000)
{
carry = 1; tmp -= 10000; }else
carry = 0;
当运算结果大于等于10000时,进位carry=1,因为进位,运算结果减-10000。 否则进位为0。
InsertNodeAtHead(c, tmp); 将运算结果插入到c的头结点后。 pa = pa->prior; pb = pb->prior; 前移 第二个while语句:
while(pa != a) 则说明a的长度大于等于b,这是需将a剩余的结点导入c中,需要考虑进位。 第三个while语句:
while(pa != a) 则说明b的长度大于a,这是需将b剩余的结点导入c中,需要考虑进位。 If语句
当a,b中的数据结点都运算后,还存在进位,则需要在c的头结点插入1。
10.减运算
void Sub(DualList a, DualList b, DualList c) {
DualList pa, pb, pc; int borrow = 0,tmp; pa = a->prior; pb = b->prior;
while((pa != a) && (pb != b)) //判断a>b还是aif (pa->data >= pb->data + borrow) borrow = 0; else
borrow = 1; pa = pa->prior; pb = pb->prior; }
if (pa != a || (pa == a && pb == b && borrow == 0)) //还是a// a >= b
c->data = a->data; }
pa = a->prior; pb = b->prior;
if(c->data!=b->data){ //a>b情况 borrow=0;
判断a>b
while((pa != a) && (pb != b)) //将b中与a等大的相减导入c {
if (pa->data >= pb->data + borrow) //不存在借位 {
tmp = pa->data - pb->data - borrow; borrow = 0; } else {
tmp = 10000 + pa->data - pb->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior; }
while(pa != a) //把a中剩余导入c {
if (pa->data >= borrow) {
tmp = pa->data - borrow;
borrow = 0; } else {
tmp = 10000 - pa->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; } }
else{ //cc->data=b->data; borrow=0;
while((pa != a) && (pb != b)) //将a中与b等大导入c {
if (pb->data >= pa->data + borrow) {
tmp = pb->data - pa->data - borrow; borrow = 0;
} else {
tmp = 10000 + pb->data - pa->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior; }
while(pb != b) //导入b中剩余 {
if (pb->data >= borrow) {
tmp = pb->data - borrow; borrow = 0; } else {
tmp = 10000 - pb->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp);
pb = pb->prior; } }
pc = c->next;
while(pc->next !=c && pc->data == 0) //为了防止头因借位变为0的情况 {
pc = pc->next; DelNode(c, pc->prior); } }
本函数主要由1个while与2个if语句构成。
int borrow = 0,tmp; borrow表示借位,tmp同加函数。 第一个while语句:
while((pa != a) && (pb != b)) //判断a>b还是aif (pa->data >= pb->data + borrow) borrow = 0; else
borrow = 1; pa = pa->prior; pb = pb->prior; }
通过不断地指向前驱,以判断a和b谁先到达头结点,以判断a和b的绝对值谁大,从而判断符号是否正确。
第一个if语句:
if (pa != a || (pa == a && pb == b && borrow == 0)) {
// a >= b
c->data = a->data; }
判断a>b还是a第二个if语句:
if(c->data!=b->data){ //|a|>|b|情况 borrow=0; //重新置借位为0 while((pa != a) && (pb != b)) //不为符号位时进行 {
if (pa->data >= pb->data + borrow) //因为|a|>|b|,应该用大数减小数 {
tmp = pa->data - pb->data - borrow; //考虑借位的减法 borrow = 0; //借位置0 } else {
tmp = 10000 + pa->data - pb->data - borrow; //需向前一位借1
borrow = 1;
}
InsertNodeAtHead(c, tmp); //在c的头结点后插入
pa = pa->prior; pb = pb->prior; }
while(pa != a) //把a中剩余导入c,因为|a|>|b,则只可能a有剩余 {
if (pa->data >= borrow) {
tmp = pa->data - borrow; borrow = 0; } else {
tmp = 10000 - pa->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; } }
Else 语句:大体内容同上,只是|a|<|b|的区别。 DelNode()函数的调用:
pc = c->next;
while(pc->next !=c && pc->data == 0) {
pc = pc->next; DelNode(c, pc->prior); }
这是为了防止开头因借位变为0的情况。 例如:a=1,0000 b=-1
运算后为0,9999 ,这时0时多余的,需要删除。
11.删除多余0:
void DelNode(DualList L, DualNode *p) {
p->prior->next = p->next; p->next->prior = p->prior; free(p); } 删除结点
12.主函数:
int main() {
while(1) {
//多组输入
DualList a, b, c; a = InputData(); b = InputData(); c = AddList(a, b); printf(\"The result is:\"); PrintList(c);
printf(\"********************************************************************************\\n\");
}
return 0; }
为了满足实验要求,采用while(1)实现多组输入。
四、调试分析
1.遇到的问题
在实验中,主要是在减函数中遇到的问题。首先我并没有对a和b的长度进行比较,而是直接开始对其相同长度的部分进行减法运算,对其借位进行的是borrow = borrow?0:1;的操作,但在实现的过程中遇到了问题,借位总是会发生错误,导致结果出错。在最后通过先判断其大小,再进行减法这一符合正常算法的方式,解决了这个问题。
在调试时,并没有考虑多组输入问题,一个一个输入输入十分麻烦,采用while(1)的方法。
在输入的时候,经常会多按或稍按几个数,导致其调试十分麻烦,则在程序中添加算法解决这个麻烦。
2.算法的时空分析
在本次实验,主要是考虑长整数的加减法问题,并没有太大的时间复杂度。 其主要来源于输入,输入的输出,加减法运算,和输出。
由于这并不是一个很大的时间复杂度,改进的方式只能从链表的存储下手,减少存入和调出的次数已减少时间复杂度。
3.经验体会
本次实验是求两个无限长整数加减运算。由于没有长度和必须使用
链表来表示两个长整数,这样来求它们的加减运算。在开始的时候,不知道怎么去做。最后,明白应该用模块化,将程序分成一个个小部分,在将他们串联起来,才能完成。虽然中间遇到了许多困难,但同样复习了数据结构的知识和复习链表。
五、用户使用说明
用户在使用该程序时,只需按照程序提示进行即可实现长整数的加减运算,具体使用步骤如下:
1)按照输入格式输入 2)此时会输出你刚才的数值 3)按照输入格式输入第二个长整数 4)此时会输出你刚才的数值 5)此时会输出运算结果 6)可重复上述操作,多次输入
六、测试结果与情况
①两长整数a=b=0
②b>a>0 a=1,1111,1111,1111 b=9,9999,9999,9999
③a>b>0 a=9999,9999,9999 b=2
④ b⑤a<0,b>0,|a|>|b| a=-1,0000,00001 b=2⑥a<0,b>0,|a|<|b| a=-9999 b=1,0000
⑦a>0,b<0,|a|>|b| a=1,0000,0000 b=-9999
⑧a>0,b<0,|a|<|b| a=1 b=-1,0000,0000
⑨错误输入(例:输入超过四位,则自动取其前四位进行运算) a=1,00000 b=-99998,01234
⑩错误输入(例:非第一次输入少于四位,则在输入前加0补足四位进行运算)
a=1,000 b=-1,11
附录(源代码):
#include #include #include typedef struct DualNode {
int data;
struct DualNode *prior, *next; }DualNode, *DualList;
DualList InitList(int sign) {
//头结点存放符号位,1为正,-1为负 DualList L;
L = (DualList)malloc(sizeof(DualNode)); L->next = L->prior = L; L->data = sign; return L; }
void InsertNodeAtTail(DualList L, int data) //{
//尾插,用于存储数据的输入 DualNode *s;
s = (DualList)malloc(sizeof(DualNode)); s->data = data;
在链表最后插入 s->next = L; s->prior = L->prior; L->prior->next = s; L->prior = s; }
void InsertNodeAtHead(DualList L, int data) {
// 即插在头结点之后,用于计算结果的存储 DualNode *s;
s = (DualList)malloc(sizeof(DualNode)); s->data = data; s->next = L->next; s->prior = L; L->next->prior = s; L->next = s; }
void PrintList(DualList L) {
//打印结果
int FirstTime = 1; DualNode *p = L;
if (p->data == -1&&p->next->data!=0) printf(\"-\"); p = p->next;
while(p != L) {
if (FirstTime) {
FirstTime = 0; printf(\"%d\ } else {
printf(\ }
p = p->next; }
printf(\"\\n\"); }
DualList InputData() { int count=0;
int FirstNum = 1, data; char c; DualList L;
L = (DualList)malloc(sizeof(DualNode)); L->next = L->prior = L; if ((c = getchar()) == '-') L = InitList(-1);
else
L = InitList(1);
if (isdigit(c)) //判断是否为10进制数 // 退格处理 ungetc(c, stdin); do{
scanf(\"%d\
while(data>9999) data=data/10; InsertNodeAtTail(L, data); }while((c = getchar()) != '\\n'); printf(\"您的输入结果为:\\n\"); PrintList(L); return L; }
void DelNode(DualList L, DualNode *p) {
p->prior->next = p->next; p->next->prior = p->prior; free(p); }
void Add(DualList a, DualList b, DualList c) {
DualList pa, pb;
int carry = 0, tmp; pa = a->prior; pb = b->prior;
while((pa != a) && (pb != b)) {
tmp = pa->data + pb->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; }else
carry = 0;
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior; }
while(pa != a) {
// pb = b
tmp = pa->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; }
else
carry = 0;
InsertNodeAtHead(c, tmp); pa = pa->prior; }
while(pb != b) {
// pa = a
tmp = pb->data + carry; if (tmp >= 10000) {
carry = 1; tmp -= 10000; } else
carry = 0;
InsertNodeAtHead(c, tmp); pb = pb->prior; }
if (carry != 0)
InsertNodeAtHead(c, 1); }
void Sub(DualList a, DualList b, DualList c) {
DualList pa, pb, pc; int borrow = 0,tmp; pa = a->prior; pb = b->prior;
while((pa != a) && (pb != b)) //判断a>b还是aif (pa->data >= pb->data + borrow) borrow = 0; else
borrow = 1; pa = pa->prior; pb = pb->prior; }
if (pa != a || (pa == a && pb == b && borrow == 0)) //判断a>b还是a// a >= b
c->data = a->data; }
pa = a->prior; pb = b->prior;
if(c->data!=b->data){ //a>b情况 borrow=0;
while((pa != a) && (pb != b)) //将b中与a等大的相减导入c {
if (pa->data >= pb->data + borrow) //不存在借位 {
tmp = pa->data - pb->data - borrow; borrow = 0; } else {
tmp = 10000 + pa->data - pb->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior; }
while(pa != a) //把a中剩余导入c {
if (pa->data >= borrow) {
tmp = pa->data - borrow;
borrow = 0; } else {
tmp = 10000 - pa->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; } }
else{ //cc->data=b->data; borrow=0;
while((pa != a) && (pb != b)) //将a中与b等大导入c {
if (pb->data >= pa->data + borrow) {
tmp = pb->data - pa->data - borrow; borrow = 0;
} else {
tmp = 10000 + pb->data - pa->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp); pa = pa->prior; pb = pb->prior; }
while(pb != b) //导入b中剩余 {
if (pb->data >= borrow) {
tmp = pb->data - borrow; borrow = 0; } else {
tmp = 10000 - pb->data - borrow; borrow = 1; }
InsertNodeAtHead(c, tmp);
pb = pb->prior; } }
pc = c->next;
while(pc->next !=c && pc->data == 0) //0的情况 {
pc = pc->next; DelNode(c, pc->prior); } }
DualList AddList(DualList a, DualList b) //{
DualList c;
if (a->data * b->data > 0) {
c = InitList(a->data); Add(a, b, c); } else {
为了防止头因借位变为判断符号 c=InitList(b->data); Sub(a, b, c); } return c; }
int main() {
while(1) {
//多组输入 DualList a, b, c;
printf(\"请按照如下形式输入第一个长整数,每四位一组: -1234,1234,1234\\n\"); a = InputData();
printf(\"请按照如下形式输入第二个长整数: -1234,1234,1234\\n\"); b = InputData(); c = AddList(a, b); printf(\"您的运算结果为:\"); PrintList(c);
printf(\"********************************************************************************\\n\"); }
return 0; }