问题描述
目的:使用C++模板设计单链表的抽象数据类型(ADT)。并在此基础上,使用单链表ADT的基本操作,设计并实现单链表的应用算法设计。
内容:(1)请使用模板设计单链表的抽象数据类型。(由于该环境目前仅支持单文件的编译,故将所有内容都集中在一个源文件内。在实际的设计中,推荐将抽象类及对应的派生类分别放在单独的头文件中。参考网盘中的ADT原型文件。)
(2)ADT的应用:使用该ADT设计并实现单链表应用场合的算法设计。
应用:假设2个任意长度的整数x、y分别由带头结点的单链表A和B存储,现要求设计一个算法,实现任意长的整数进行加法运算。
参考函数原型:
template<class ElemType>
void Long_Int_Add( LinkList<ElemType> &A, LinkList<ElemType> &B, string &result, const int &len_A, const int &len_B );
辅助函数原型:
(1)从长整数的低位开始拆分(4位为一组,即不超过9999的非负整数),依次存放在单链表的每个结点的数据域中;头结点的数据域存放正负数标志(正数或0:1,负数:-1)。
template<class ElemType>
void Input_Int_Division( LinkList<ElemType> &L, string &str, int &length ); (length:长整数分割后的block数,恰为存储用单链表的长度)
(2)计算结果中间位格式控制
string Int_String( int result );
(3)两个长整数的绝对值大小比较(x>y 返回值为1;x<y 返回值为2;x=y 返回值为0;)
template<class ElemType>
int Two_LongNum_Compare( LinkList<ElemType> &A, LinkList<ElemType> &B, const int &len_A, const int &len_B );
(4)单链表ADT基本操作:单链表的逆置(实际计算时,鉴于单链表的顺序查找的特性,存储在单链表中的长整数需逆置,由原始的高位到低位排列,逆置为低位到高位降序排列。)
template<class ElemType>
void LinkList<ElemType>::ListReverse();
输入说明
第一行:长整数x
第二行:长整数y
输出说明
第一行:格式化后的长整数x(从低位到高位每4位用","分开)
第二行:格式化后的长整数y(从低位到高位每4位用","分开)
第三行:空行
第四行:格式化后的计算结果(从低位到高位每4位用","分开)
(输入与输出之间用一空行分隔)
输入范例
-53456467576846547658679870988098
435643754856985679
输出范例
-5345,6467,5768,4654,7658,6798,7098,8098
43,5643,7548,5698,5679
-5345,6467,5768,4611,2014,9250,1400,2419
思路分析
- 题目重点
- 用单链表保存任意长度的长整数
- 实现整数的加法运算
- 题设附加:
- 长整数低位拆分,数据域存放数据,4位一组
- 链表头结点的数据域存放符号位
- 可以调用两个长整数的绝对值比较大小
- 根据单链表的连接顺序,建议逆序保存
* 原数:高位->低位
* 保存:低位->高位
尝试一
- 题目倒是好理解,但是不是很理解所给的几个函数原型
关于参考函数原型
template<class ElemType>
void Long_Int_Add( LinkList<ElemType> &A, LinkList<ElemType> &B, string &result, const int &len_A, const int &len_B );
参数介绍:
LinkList<ElemType> &A, LinkList<ElemType> &B:表示保存好的两个链表
string &result:用来保存最终的结果
const int &len_A, const int &len_B :两个的长整数的位数
关于辅助函数原型
- 拆分函数
template<class ElemType>
void Input_Int_Division( LinkList<ElemType> &L, string &str, int &length );
参数介绍
length:长整数分割后的block数,恰为存储用单链表的长度
LinkList<ElemType> &L:最终用来存放分割之后的长整数的链表
string &str:外界用来传递的数组
- 计算结果中间格式控制
string Int_String( int result );
result:是某一段的结点,将某一段进行加减运算之后的int型转成对应的字符串型
-
个人认为大可不必,出现这个函数是因为最终的链表的结点的数据域确认是字符串型的,不是整型。其实使用整型也是可以的,只是处理确定的位数就比较费劲
-
大可不必看我上面一段的论述,因为我上网搜了一下,发现从string转成int只要一句话,但是从int转成对应的string要好几句话,而且居然要额外的导入相关的包。具体的方法,看下面的额外知识补充
-
绝对值比较大小
template<class ElemType>
int Two_LongNum_Compare( LinkList<ElemType> &A, LinkList<ElemType> &B, const int &len_A, const int &len_B );
参数介绍
LinkList<ElemType> &A, LinkList<ElemType> &B:两个存放长整数的链表
const int &len_A, const int &len_B:两个存放长整数链表的长度
- 单链表的逆置
template<class ElemType>
void LinkList<ElemType>::ListReverse();
- reverse在以前的题目中已经实现,在这里给出相关的连接reverse
基本的知识补充
- string类型和int类型的相互转换函数
- string转int
std::string str = "123";
int n = atoi(str.c_str());
- int转string
#include<string>
#include<sstream>
using namespace std;
int main(){
int n = 0;
stringstream ss;
string str;
ss<<n;
ss>>str;
}
- string类型的切片函数
#include <string>
string substr( size_type index, size_type num = npos );
string s("What we have here is a failure to communicate");
string sub = s.substr(21);
cout << "The original string is " << s << endl;
cout << "The substring is " << sub << endl;
输出
The original string is What we have here is a failure to communicate
The substring is a failure to communicate
功能描述:
生成一个新的子串,子串的从index开始截取的,num个字符。若不写num,则默认是到字符串的末尾
注意,这函数是左闭右开的,起步是0,步长是4,是取包括0在内的四个字符,不包括第五个
同时不用担心,如果不够四个会怎么办。到了末尾会自动结束的
基本的框架构成
- 将字符串进行四个一组的切片,然后生成对应的链表
- 将对应的链表进行逆置(方便低位向高位进行进位或者是退位)
- 先看两者的符号是否相同,相同就不要比较绝对值,不同就比较绝对值
- 两者转成对应的int型进行运算,然后转成将结果转成对应的string生成相关的结果链表
源码——这个老师已经将源码放到了ppt,就肆无忌惮一次吧
/*
description:convert int to string
*/
string Int_String( int result )
{
stringstream ss;
string resString;
ss<<result;
ss>>result;
return resString;
}
/*
description:compare the ABS of the two long int
return :X>Y return 1
:X<Y return 2
:X=Y return 0
*/
int Two_LongNum_Compare( LinkList<ElemType> &A, LinkList<ElemType> &B, const int &len_A, const int &len_B )
{
LinkNode<ElemType> *temp1 = A.GetHead()->next;
LinkNode<ElemType> *temp2 = B.GetHead()->next;
int times = 0;
//compare simple by the length of the stirng
if(len_A < len_B)
{
return 2;
}
if(len_A > len_B)
{
return 1;
}
//on the condition that the length of them are equal
while(times < len_A && times < len_B)
{
if(temp1->data < temp2->data)
{
return 2;
}
if(temp1->data > temp2->data)
{
return 1;
}
temp1 = temp1->next;
temp2 = temp2->next;
times ++;
}
//the value and the length of the linklist are the same
return 0;
}
/*
description:divide the whole string into pieces
*/
void Input_Int_Division( LinkList<ElemType> &L, string &str, int &length )
{
int index = 0,step = 4,temp1;
string substract = "";
LinkNode<ElemType> *temp = L.getTail();
int str_length= str.length();
//tackle the singal of the number
if(str.at(0) == '-')
{
L.GetHead()->data = -1;
index = 1;
str_length -= 1;
}
else
{
L.GetHead()->data = 1;
}
//tacle the situatio with different numbers of numbers
if(str_length% 4 == 1)
{
substract = str.substr(index,1);
temp1 = atoi(substract.c_str());
temp->next = new LinkNode<ElemType>(temp1);
temp = temp->next;
index += 1;
length ++;
}
if(str_length % 4 == 2)
{
substract = str.substr(index,2);
temp1 = atoi(substract.c_str());
temp->next = new LinkNode<ElemType>(temp1);
temp = temp->next;
index += 2;
length ++;
}
if(str_length % 4 == 3)
{
substract = str.substr(index,3);
temp1 = atoi(substract.c_str());
temp->next = new LinkNode<ElemType>(temp1);
temp = temp->next;
index += 3;
length ++;
}
//tackle other numbers
while(index < str.length())
{
substract = str.substr(index,step);
temp1 = atoi(substract.c_str());
temp->next = new LinkNode<ElemType>(temp1);
temp = temp->next;
index += 4;
length ++;
}
}
void get_Sub(LinkList<ElemType> &res,LinkList<ElemType> &big
,LinkList<ElemType> &small)
{
//create the revelent linklist to store the result
LinkNode<ElemType> *temp = res.GetHead();
//define two nodes to traverse two linkedlists
LinkNode<ElemType> *temp1 = big.GetHead()->next;
LinkNode<ElemType> *temp2 = small.GetHead()->next;
//to store the temporary sum of two nodes
int tempSub = 0;
//to represents the carry from the low position
int carry = 0;
while(temp1 && temp2)
{
if((temp1->data + carry) < temp2->data)
{
tempSub = temp1->data - temp2->data + 10000;
carry = -1;
}
else
{
tempSub = temp1->data - temp2->data + carry;
carry = 0;
}
temp1 = temp1->next;
temp2 = temp2->next;
temp->next = new LinkNode<ElemType>(tempSub);
temp = temp->next;
}
//deal with the surplus part of the numbers
while(temp1) {
//the situation that temp1 is not NULL
temp->next = new LinkNode<ElemType>(temp1->data);
temp1 = temp1->next;
temp = temp->next;
}
res.ListReverse();
temp = res.GetHead()->next;
while(temp)
{
if(temp->data == 0)
{
res.GetHead()->next = temp->next;
}
temp = temp->next;
}
}
void get_Sum(LinkList<ElemType> &res,LinkList<ElemType> &A
,LinkList<ElemType> &B)
{
//define two nodes to traverse two linkedlists
LinkNode<ElemType> *temp1 = A.GetHead()->next;
LinkNode<ElemType> *temp2 = B.GetHead()->next;
LinkNode<ElemType> *temp = res.GetHead();
//to store the temporary sum of two nodes
int tempSum = 0;
//to represents the carry from the low position
int carry = 0;
//add up two numbers
//determine the signal of the sum of two positive numbers
if(A.GetHead()->data == -1)
{
res.GetHead()->data = -1;
}
else
{
res.GetHead()->data = 1;
}
//traverse two linklist to get the sum of the common parts
while(temp1 && temp2)
{
tempSum = temp1->data + temp2->data + carry;
if(tempSum >= 10000)
{
tempSum -= 10000;
//substract the highest position of the number
carry = 1;
}
else
{
carry = 0;
}
temp1 = temp1->next;
temp2 = temp2->next;
temp->next = new LinkNode<ElemType>(tempSum);
temp = temp->next;
}
//deal with the surplus part of the numbers
while(temp1)
{
//the situation that temp1 is not NULL
temp->next = new LinkNode<ElemType>(temp1);
temp1 = temp1->next;
temp = temp->next;
}
while(temp2)
{
//the situation that temp1 is not NULL
temp = new LinkNode<ElemType>(temp2);
temp2 = temp2->next;
temp = temp->next;
}
res.ListReverse();
if(carry == 1)
{
temp = new LinkNode<ElemType>(1);
temp->next = res.GetHead()->next;
res.GetHead()->next = temp;
}
}
void Long_Int_Add( LinkList<ElemType> &A, LinkList<ElemType> &B
, string &result, const int &len_A, const int &len_B )
{
//create the revelent linklist to store the result
LinkList<ElemType> resLinkList;
LinkNode<ElemType> *temp = resLinkList.GetHead();
//reverse two list to somplify the calculation
A.ListReverse();
B.ListReverse();
//deal with the situation with the same signal
if(A.GetHead()->data == B.GetHead()->data)
{
get_Sum(resLinkList,A,B);
}
else
{
int flag = Two_LongNum_Compare(A,B,len_A,len_B);
if(flag == 0)
{
//A and B are opposite number
temp->next = new LinkNode<ElemType>(0);
}
else if(flag == 1)
{
//A is bigger than B
resLinkList.GetHead()->data = A.GetHead()->data;
get_Sub(resLinkList,A,B);
}
else if(flag == 2)
{
//A is smaller than B
resLinkList.GetHead()->data = B.GetHead()->data;
get_Sub(resLinkList,B,A);
}
}
A.ListReverse();
B.ListReverse();
temp = resLinkList.GetHead();
string tempString = "";
if(temp->data == -1)
{
tempString = "-";
result.append(tempString);
}
while(temp)
{
tempString = Int_String(temp->data);
result.append(tempString);
temp = temp->next;
}
resLinkList.ListTraverse();
}
比较绝对值的大小
/*
description:compare the ABS of the two long int
return :X>Y return 1
:X<Y return 2
:X=Y return 0
*/
template<class ElemType>
int Two_LongNum_Compare( LinkList<ElemType> &A, LinkList<ElemType> &B, const int &len_A, const int &len_B )
{
LinkNode<ElemType> *temp1 = A.GetHead()->next;
LinkNode<ElemType> *temp2 = B.GetHead()->next;
int times = 0;
//compare simple by the length of the linklist
if(len_A < len_B)
{
return 2;
}
if(len_A > len_B)
{
return 1;
}
//on the condition that the length of them are equal
while(times < len_A && times < len_B)
{
if(atoi(temp1->data) < atoi(temp2->data))
{
return 2;
}
if(atoi(temp1->data) > atoi(temp2->data))
{
return 1;
}
}
//the value and the length of the linklist are the same
return 0;
}
将长字符串进行切片,并生成链表
/*
description:divide the whole string into pieces
*/
void LinkList::Input_Int_Division( LinkLis &L, string &str, int &length )
{
index = 0,step = 4;
string substract = "";
LinkNode *temp = L.getTail();
while(index < length)
{
substract = str.substr(index,step);
temp->next = new LinkNode(substract);
temp = temp->next;
index += 4;
}
}
车祸现场
第一次提交
这个,错误的样例多的有一点发麻,我一时间有点不知所措,索性就自己去先试试几个通栏的样例
样例一:两个极大的正数 456,131,546,315,411 和165,489,646,161,206
由结果可知,是正确的
样例二、极小的两个数进行加减5+3=8
- 错了,如下图
- 分析:很明显,连遍历输出都有问题,所以先看看遍历输出函数
- 思路清理:
- 如果链表存在,直接输出第一个结点
- 是否输出“,”,则取决于是否存在下一个结点,如果存在就输出,不存在就不输出
修改之后得测试
- 输出没有问题
第二次提交
- 还是有问题,仍旧需要自拟样例进行猜测
样例二、一个正数一个负数,以不同的次序输入
- 自拟测试样例,一个正数123456789,一个负数-235461,分别交换顺序进行输出。
- 正数在上,负数在样例通过
- 正数在下,负数在上。通过测试
样例三、极大的两个负数-123456789 和-123456789
- 通过样例
样例四、两个极大的正数123456798 和123456789
- 通过测试
不知道,只能花分数去买了
- 没有通过样例
- 原因:相等的代码段没有写完,忘记写迭代条件了
- 通过样例
第三次提交
- 老师点了一下,还有一个地方没有认识到,那就是5000,和5000最终会生成新的结点,这都没有考虑到。同样的,还有一个退位没有考虑到,11000和-10000
- 针对负数的零结点处理方法
- 针对正数相加求和的进位
第三次提交
- 我只看了一个样例,真的!过啦
样例假设方法的总结
- 根据两个数字的大小进行比较,一大一小,一小一大,两者相等,三大类情况。(本热自己没有考虑到如何处理相等的情况)
分析与总结
-
什么时候需要反转?
- 比较绝对值大小是不需要的,因为一定要是从最高位开始比较的,逐渐向小的。链表长度不一致不用比较,直接出来
- 当计算两个链表的和时候,是需要的,一因为涉及到处理进位和退位的信息传递
-
关于补零:
- 将长整数拆成若干固定长度的结点及逆行保存,已经破坏了原有的逻辑结构,所以要去弥补。主要有两个方面需要去弥补:
- 中间结点的补零:一方面是作为一个独立的数字,另外一方面又是作为长正数的一部分。1 0001 0001 用链表表示:1,1,1
- 最高位的进位:
- 5000 + 5000 = 1 0000,新生成了一个结点,1,容易被忽略
- 15000-10000 = 5000,消失了一个结点,就意味着要删除对应的结点
-
注意还涉及到补位的问题,如果某一个段结果是小于四位数,那就要在对应的数字前面补零才能输出。最大值又不需要补零,
-
关于字符串的切片,真的不想再吐槽什么了。如果一开始就四个字符四个字符的切片,最高位不足四位是不同补齐的,但是从最高位开始切,怎么判断会多几位,只能傻傻的用条件判定语句的。字符串又不好反转
这道题写的我想吐 -
我都不知道我脑子再抽什么,居然会这样想
temp = LInkList.GetHead();
temp = temp->next;
temp = new LinkNode();
//这完全就是鬼扯,怎么能连起来,你还费了那么多时间!
//temp指向的是一个空的地址,你只是改变了这个指针的值,并没有改变结点的逻辑结构
//temp=temp->next,此时的temp已经和head没有任何的关系了
//难道不应该这样吗
//没有看清,就在那里胡乱赋值,真的是
temp = LInkList.GetHead();
temp->next = new LinkNode();
- 既然是将数字转成对应的单个节点,就意味着的某个结点的数字的范围,同时还有各个结点的补零问题