本蒟蒻第一次写出来对我来说很难的题目,所以发个文章记录一下思路和一些错误。如有问题欢迎斧正,也请不要嘲笑(我知道我真的很垃圾)。
(或许也可以给hnu的学弟学妹们提供点帮助呢)
(毕竟我们刚开始的时候也到处找题解找不到合适的)
题目
在数学上,一个一元n次多项式Pn(x)可按降序写成:

它是由n+1个系数唯一确定。因此,在计算机里它可以用一个线性表P来表示:
P=(Pn, Pn-1, …, P1, Po)
一元多项式的运算包括加法、减法和乘法,而多项式的减法和乘法都可以用加法来实现。
利用线性表ADT,设计和实现两个一元多项式的加法运算。
提示:用有序线性表表示一元多项式。表内元素按照多项式的次数递减的次序排列。
第一个多项式:3x^5-2x+4;则在计算机中输入:
3
3 5
-2 1
4 0
第二个多项式:2x^3-x^2+x+3;则在计算机中输入:
4
2 3
-1 2
1 1
3 0
输出计算结果:3x^5+2x^3-x^2-x+7;则在计算机中输出:
3 5
2 3
-1 2
-1 1
7 0
————————————————
【输入形式】
输入第一行包含一个整数n,代表第一个多项式有n项。
第二行起的包含n行整数对,每行的2个整数之间使用一个空格分隔;
接下来一行包含一个整数m,代表第二个多项式有m项。
此下包含m行整数对,每行的2个整数之间使用一个空格分隔;
【输出形式】
输出多行整数对,表示答案。
【样例输入】
3
3 5
-2 1
4 0
4
2 3
-1 2
1 1
3 0
【样例输出】
3 5
2 3
-1 2
-1 1
7 0
【样例说明】
多项式3x^5-2x+4与多项式2x^3-x^2+x+3相加,计算结果:3x^5+2x^3-x^2-x+7。
【样例输入】
2
3 5
3 1
0
【样例输出】
3 5
3 1
【样例说明】
多项式3x^5+3x与多项式0相加,计算结果:3x^5+3x。
思路
构造一个链表来表示多项式,元素域储存多项式的系数和指数。
将两个链表化为等长度的链表,不存在的项系数为0;
然后将指数相同的项系数相加,用和系数替换原多项式一的系数(其实也可以另建一个链表储存和多项式的系数和指数,但我觉得这样或许简单一点);
最后输出和多项式的系数和指数,系数为0的项不输出。
代码实现
线性表ADT
这个ADT基本上就是书上的代码,我修改了一部分以实现我想要的功能。
改动:
添加了一个replace函数。
将insert函数和append函数的参数改为两个,一个系数,一个指数。
将getValue函数改成两个,getValuec得到系数,getValuee得到指数(虽然后一个没用上)。
添加了一个print函数。
template <typename E> class List {
private:
void operator =(const List&) {}
List(const List&) {}
public:
List() {} // Default constructor
virtual ~List() {} // Base destructor
// Clear contents from the list, to make it empty.
virtual void clear() = 0;
virtual void replace(const E& item) =0;
// Insert an element at the current location.
// item: The element to be inserted
virtual void insert(const E& item1,const E& item2) = 0;
// Append an element at the end of the list.
// item: The element to be appended.
virtual void append(const E& item1,const E& item2) = 0;
// Remove and return the current element.
// Return: the element that was removed.
virtual E remove() = 0;
// Set the current position to the start of the list
virtual void moveToStart() = 0;
// Set the current position to the end of the list
virtual void moveToEnd() = 0;
// Move the current position one step left. No change
// if already at beginning.
virtual void prev() = 0;
// Move the current position one step right. No change
// if already at end.
virtual void next() = 0;
// Return: The number of elements in the list.
virtual int length() const = 0;
// Return: The position of the current element.
virtual int currPos() const = 0;
// Set current position.
// pos: The position to make current.
virtual void moveToPos(int pos) = 0;
// Return: The current element.
virtual const E& getValuec() const = 0;
virtual const E& getValuee() const = 0;
virtual void print() =0;
};
链表的物理实现
这个也基本上是书上的代码,也作出了一些改动:
Link类中的元素和Link构造函数的参数改为两个,系数coef和指数expn。
LList类中,replace函数的实现:将当前结点的系数替换为传入的参数(没有替换指数是因为没必要)
LList类中,print函数的实现:输出当前结点储存的系数值和指数值,并将curr指针指向下一结点。
其他的就是一些针对一个元素变两个元素作出的小改动了。
void Assert(bool val,string s){
if(!val){
cout<<"断言失败:"<<s<<endl;
}
}
template <typename E> class Link {
public:
E coef; //系数
E expn; //指数
Link *next; // Pointer to next node in list
// Constructors
Link(const E& elemval, const E& element, Link* nextval =NULL) {
coef=elemval;
expn=element;
next=nextval;
}
Link(Link* nextval =NULL) {
next=nextval;
}
};
template <typename E> class LList: public List<E> {
private:
Link<E>* head; // Pointer to list header
Link<E>* tail; // Pointer to last element
Link<E>* curr; // Access to current element
int cnt; // Size of list
void init() { // Intialization helper method
curr=tail=head=new Link<E>;
cnt=0;
}
void removeall() { // Return link nodes to free store
while(head!=NULL){
curr=head;
head=head->next;
delete curr;
}
}
public:
LList(int size=100) {
init(); // Constructor
}
~LList() {
removeall(); // Destructor
}
void replace(const E& it){
curr -> next -> coef = it;
}
void print( ){
cout<<curr->next->coef<<" "<<curr->next->expn<<endl;
curr=curr->next;
}
void clear() {
removeall(); // Clear list
init();
}
// Insert "it" at current position
void insert(const E& it,const E& eit) {
curr->next = new Link<E>(it,eit, curr->next);
if (tail == curr) tail = tail->next;
cnt++;
}
void append(const E& it,const E& eit) {
tail = tail->next = new Link<E>(it,eit, NULL);
cnt++;
}
E remove() {
Assert(curr->next != NULL, "No element"); //如当前元素不存在,将直接报错,并终止程序
E it =curr->next->coef;
Link<E>*ltemp=curr->next;
if(tail==curr->next) tail=curr;
curr->next=curr->next->next;
delete ltemp;
cnt--;
return it;
}
// Place curr at list start
void moveToStart() {
curr=head;
}
// Place curr at list end
void moveToEnd() {
curr=tail;
}
// Move curr one step left; no change if already at front
void prev() {
if (curr == head) return;
Link<E>* temp = head;
while (temp->next!=curr) temp=temp->next;
curr = temp;
}
// Move curr one step right; no change if already at end
void next() {
if (curr != tail) curr = curr->next;
}
// Return length
int length() const {
return cnt;
}
// Return the position of the current element
int currPos() const {
Link<E>* temp = head;
int i;
for (i=0; curr != temp; i++)
temp = temp->next;
return i;
}
// Move down list to "pos" position
void moveToPos(int pos) {
Assert ((pos>=0)&&(pos<=cnt), "Position out of range");
curr = head;
for(int i=0; i<pos; i++) curr = curr->next;
}
// Return current element
const E& getValuec() const {
Assert(curr->next != NULL, "No value");
return curr->next->coef;
}
const E& getValuee() const {
Assert(curr->next != NULL, "No value");
return curr->next->expn;
}
};
main函数的实现
基本都是对称的,毕竟两链表交换一下位置没有任何区别。
int cnt1,cnt2;
//实现两个非空链表的相加
void app(LList<int> &list1,LList<int> &list2)
{
int pos=0;
int v1=0,v2=0,sum=0;
//v1为链表1的系数元素,v2为链表2的系数元素,sum为和多项式的系数
int l1=list1.length(),l2=list2.length();
//两链表长度
//下面通过在头部插入系数为0的结点使两链表等长
if(l1>l2)
{
list2.moveToStart();
for(int j=l1-1;j>=l2;j--){
list2.insert(0,j);
list2.next();
}
}
else if(l1<l2)
{
list1.moveToStart();
for(int j=l2-1;j>=l1;j--){
list1.insert(0,j);
list1.next();
}
}
int l=l1>l2?l1:l2; //取两链表中较长的一个链表长度(即为修改后的链表长度)
list1.moveToStart();
list2.moveToStart();
for(pos=0;pos<l;pos++) //各项求和并输出结果
{
v1=list1.getValuec();
v2=list2.getValuec();
sum=v1+v2;
list1.replace(sum);
if(sum!=0)
list1.print();
else
list1.next();
list2.next();
}
}
int main() {
LList <int>list1;
LList <int>list2;
int n1,n2;
//构建链表1——多项式1
cin>>n1;
if(n1!=0)
{
int a[n1][2]; //储存输入数据
for(int i=0;i<n1;i++)
{
for(int j=0;j<2;j++)
{
cin>>a[i][j];
}
}
cnt1=a[0][1]; //最高指数
int k=0;
for(int j=cnt1;j>=0;j--) //构建链表,链表的结点数比最高指数多一个
{
if(a[k][1]==j&&k<n1) //输入的项,系数非0
{
list1.append(a[k][0],j);
k++;
}
else
{
list1.append(0,j);
}
}
}
//构建链表2——多项式2 (基本同上)
cin>>n2;
if(n2!=0)
{
int b[n2][2];
for(int i=0;i<n2;i++)
{
for(int j=0;j<2;j++)
{
cin>>b[i][j];
}
}
cnt2=b[0][1];
int k=0;
for(int j=cnt2;j>=0;j--)
{
if(b[k][1]==j&&k<n2)
{
list2.append(b[k][0],j);
k++;
}
else
{
list2.append(0,j);
}
}
}
if(n1==0) //一个多项式加0当然还是原来的多项式,直接输出
{
list2.moveToStart();
for(int i=0;i<=cnt2;i++)
{
if(list2.getValuec()!=0) //系数不为0,直接输出
list2.print();
else //系数为0,指针后移
list2.next();
}
}
if(n2==0) //同上
{
list1.moveToStart();
for(int i=0;i<=cnt1;i++)
{
if(list1.getValuec()!=0)
list1.print();
else
list1.next();
}
}
if(n1&&n2)
app(list1,list2);
return 0;
}
写的过程出现的一些错误
(来自一个马大哈想提醒自己不要再犯而敲下的一些很傻的错误)
1.在拆成多文件的时候,Link类和LList本身就是实现,不需要再拆。
2.在ADT中定义的纯虚函数没有物理实现而报错。
3.将moveToStart函数放在循环体里面导致每次输出都是第一个结点的数。
4.如果系数非0就打印却忘记了系数为0的时候指针也得向后移一个。
5.刚开始没有考虑到两个链表长度不相等的问题以至于getValuec函数读不到值。
6.insert函数后没有next函数以至于添加结点的时候一直在前面添加。
一些没有意义的絮叨(不用看)
本蒟蒻第一次花6个小时写出的题啊!我可是上次实验一的链表实现函数都没能写出来的学渣啊!真的很开心,也从不断的修改、查错中学习到了一些东西。感觉虽然时间花的久,但还是挺值的。
这是我的第一篇文章,但我希望它不是最后一篇。