【数据结构与算法】利用单链表实现一元多项式的求和,简单易懂

本蒟蒻第一次写出来对我来说很难的题目,所以发个文章记录一下思路和一些错误。如有问题欢迎斧正,也请不要嘲笑(我知道我真的很垃圾)。

(或许也可以给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基本上就是书上的代码,我修改了一部分以实现我想要的功能。

改动:

  1. 添加了一个replace函数。

  1. 将insert函数和append函数的参数改为两个,一个系数,一个指数。

  1. 将getValue函数改成两个,getValuec得到系数,getValuee得到指数(虽然后一个没用上)。

  1. 添加了一个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;
};

链表的物理实现

这个也基本上是书上的代码,也作出了一些改动:

  1. Link类中的元素和Link构造函数的参数改为两个,系数coef和指数expn。

  1. LList类中,replace函数的实现:将当前结点的系数替换为传入的参数(没有替换指数是因为没必要)

  1. LList类中,print函数的实现:输出当前结点储存的系数值和指数值,并将curr指针指向下一结点。

  1. 其他的就是一些针对一个元素变两个元素作出的小改动了。

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个小时写出的题啊!我可是上次实验一的链表实现函数都没能写出来的学渣啊!真的很开心,也从不断的修改、查错中学习到了一些东西。感觉虽然时间花的久,但还是挺值的。

这是我的第一篇文章,但我希望它不是最后一篇。

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值