每日一题13:多项式的(基于链表实现)简单运算

多项式的每一项需要两个参数,即系数与指数。描述多项式的一种方式是用数组的下标表示项的指数,而用数组存储的元素表示相应项的系数。这样表示的多项式看起来很简单,但是在很多计算中却显得很不方便,这种不方便主要出现”在稀疏的“多项式中(比如x的10000次方加1),如果要输出多项式却要从头到尾扫描数组。另一个缺点是浪费了很多的空间,上面的例子中有效的数组元素仅仅只有两个。所以最好还是用链表表示,每个节点存储一个项的系数和指数,再加上一个指向下一节点的指针。为了以后运算方便时,在构建时就将各项按指数大小排序,如果从小到大排列,那么当输出多项式时就很不方便,并且这样与数学中多项式的写法也不一致,所以最好是从大到小的排序。

#include "stdafx.h"
#include <iostream>

using namespace std;

struct PolynomialItem
{
    float Coeff;
    int Power;
    PolynomialItem* next;
};

struct Polynomial
{
    int ItemCount;
    PolynomialItem *FirstItem;
    //加入一个lastItem指针,方便函数的编写
    //特别是编写O(M^2N)时间复杂度的Multi算法时
    PolynomialItem *LastItem;
};

数据结构定义好之后,再写一些基本的函数,方便后面编写多项式运算的代码:

PolynomialItem* CreatePolyItem(float Coeff,int Power)
{
    if(Coeff == 0.0f) return NULL;
    PolynomialItem* PolyItem = new PolynomialItem;
    PolyItem->Coeff = Coeff;
    PolyItem->Power = Power;
    PolyItem->next = NULL;
    return PolyItem;
}

PolynomialItem* CopyPolyItem(const PolynomialItem* Item)
{
    if(Item == NULL) return NULL;
    return CreatePolyItem(Item->Coeff,Item->Power);
}

Polynomial* CreateEmptyPolynomial()
{
    Polynomial* Poly = new Polynomial;
    Poly->ItemCount = 0;
    Poly->FirstItem = NULL;
    Poly->LastItem = NULL;
    return Poly;
}

//Append表示不考虑输入Item中Power的值,直接连接到多项式的后面
//不保证插入的节点处在正确的位置上
void AppendPolyItem(Polynomial** Poly,PolynomialItem *Item)
{
    if((*Poly)->ItemCount == 0) 
    {
        (*Poly)->FirstItem = Item;
        (*Poly)->LastItem = Item;
        ++(*Poly)->ItemCount;
        return;
    }
    Item->next = NULL;
    (*Poly)->LastItem->next = Item;
    (*Poly)->LastItem = Item;
    ++(*Poly)->ItemCount;
}

void AppendPolyItem(Polynomial** Poly,float Coeff,int Power)
{
    AppendPolyItem(Poly,CreatePolyItem(Coeff,Power));
}

//Insert会将输入Item安插到正确的位置
void InsertPolyItem(Polynomial** Poly,PolynomialItem *Item)
{
    if((*Poly)->ItemCount == 0)
    {
        AppendPolyItem(Poly,Item);
        return;
    }
    if((*Poly)->FirstItem->Power < Item->Power)
    {
        Item->next = (*Poly)->FirstItem;
        (*Poly)->FirstItem = Item;
        ++(*Poly)->ItemCount;
        return;
    }
    PolynomialItem* p = (*Poly)->FirstItem;
    PolynomialItem* q;
    while(p)
    {
        if(p->Power > Item->Power)
        {
            q = p;
            p = p->next;
        }
        else if(p->Power == Item->Power) 
        {
            p->Coeff += Item->Coeff;
            return;
        }
        else
        {
            Item->next = q->next;
            q->next = Item;
            ++(*Poly)->ItemCount;
            return;
        }
    }
    AppendPolyItem(Poly,Item);
    return;
}

void InsertPolyItem(Polynomial** Poly,float Coeff,int Power)
{
    InsertPolyItem(Poly,CreatePolyItem(Coeff,Power));
}

Polynomial* CreatePolynomial(float Coeffs[],int Powers[],int Count)
{
    if(Count <= 0) return NULL;
    Polynomial* Poly = CreateEmptyPolynomial();
    for (int i = 0; i < Count; ++i)
    {
        InsertPolyItem(&Poly,Coeffs[i],Powers[i]);
    }
    return Poly;
}

构建数据结构的函数基本编写完成,可以开始编写运算函数了:

//以下三个函数只负责项与项的运算
PolynomialItem* ItemPlus(const PolynomialItem* item1,const PolynomialItem* item2)
{
    if(item1->Power != item2->Power) return NULL;
    float Coeff = item1->Coeff + item2->Coeff;
    int Power = item1->Power;
    return CreatePolyItem(Coeff,Power);
}

PolynomialItem* ItemMinus(const PolynomialItem* item1,const PolynomialItem* item2)
{
    if(item1->Power != item2->Power) return NULL;
    float Coeff = item1->Coeff - item2->Coeff;
    int Power = item1->Power;
    return CreatePolyItem(Coeff,Power);
}

PolynomialItem* ItemMulti(const PolynomialItem* item1,const PolynomialItem* item2)
{
    float Coeff = item1->Coeff * item2->Coeff;
    int Power = item1->Power + item2->Power;
    return CreatePolyItem(Coeff,Power);
}

//多项式运算函数开始
Polynomial* Plus(const Polynomial* Poly1,const Polynomial* Poly2)
{
    Polynomial* Poly = CreateEmptyPolynomial();
    PolynomialItem* p = Poly1->FirstItem,*q = Poly2->FirstItem;
    while(p && q)
    {
        PolynomialItem* newItem = NULL;
        if(p->Power == q->Power)
        {
            newItem = ItemPlus(p,q);
            p = p->next;
            q = q->next;
        }
        else if(p->Power > q->Power)
        {
            newItem = CopyPolyItem(p);
            p = p->next;
        }
        else
        {
            newItem = CopyPolyItem(q);
            q = q->next;
        }
        AppendPolyItem(&Poly,newItem);
    }
    while(p)
    {
        AppendPolyItem(&Poly,CopyPolyItem(p));
        p = p->next;
    }
    while(q)
    {
        AppendPolyItem(&Poly,CopyPolyItem(q));
        q = q->next;
    }
    return Poly;
}
Polynomial* Minus(const Polynomial* Poly1,const Polynomial* Poly2)
{
    Polynomial* Poly = CreateEmptyPolynomial();
    PolynomialItem* p = Poly1->FirstItem,*q = Poly2->FirstItem;
    while(p && q)
    {
        PolynomialItem* newItem = NULL;
        if(p->Power == q->Power)
        {
            newItem = ItemMinus(p,q);
            p = p->next;
            q = q->next;
        }
        else if(p->Power > q->Power)
        {
            newItem = CopyPolyItem(p);
            p = p->next;
        }
        else
        {
            newItem = CopyPolyItem(q);
            newItem->Coeff = -newItem->Coeff;
            q = q->next;
        }
        AppendPolyItem(&Poly,newItem);
    }
    while(p)
    {
        AppendPolyItem(&Poly,CopyPolyItem(p));
        p = p->next;
    }
    while(q)
    {
        PolynomialItem* newItem = CopyPolyItem(q);
        newItem->Coeff = -newItem->Coeff;
        AppendPolyItem(&Poly,newItem);
        q = q->next;
    }
    return Poly;
}
Polynomial* Multi(const Polynomial* Poly1,const Polynomial* Poly2)
{
    Polynomial* Poly = CreateEmptyPolynomial();
    PolynomialItem* q = Poly2->FirstItem;
    PolynomialItem* p = Poly1->FirstItem;
    while(q)
    {
        AppendPolyItem(&Poly,ItemMulti(p,q));
        q = q->next;
    }
    p = p->next;
    Polynomial* tempPoly = CreateEmptyPolynomial();
    while(p)
    {
        q = Poly2->FirstItem;
        while(q)
        {
            AppendPolyItem(&tempPoly,ItemMulti(p,q));
            q = q->next;
        }
        PolynomialItem* r = tempPoly->FirstItem;
        PolynomialItem* s = Poly->FirstItem,*t = NULL;
        while(s && s->Power >= r->Power) 
        {
            t = s;
            s = s->next;
        }
        while(r && s)
        {
            tempPoly->FirstItem = r->next;
            if(t->Power > r->Power)
            {
                r->next = t->next;
                t->next = r;
                t = r;
                ++Poly->ItemCount;
            }
            else if(t->Power == r->Power)
            {
                t->Coeff += r->Coeff;
                delete r;
            }
            r = tempPoly->FirstItem;
            --tempPoly->ItemCount;
            while(r && s && s->Power >= r->Power)
            {
                t = s;
                s = s->next;
            }
        }
        if(r)
        {
            if(r->Power == Poly->LastItem->Power)
            {
                Poly->LastItem->Coeff += r->Coeff;
                tempPoly->FirstItem = r->next;
                --tempPoly->ItemCount;
                delete r;
            }
            Poly->LastItem->next = tempPoly->FirstItem;
            Poly->LastItem = tempPoly->LastItem;
            Poly->ItemCount += tempPoly->ItemCount; 
        }
        tempPoly->FirstItem = NULL;
        tempPoly->LastItem = NULL;
        tempPoly->ItemCount = 0;
        p = p->next;
    }
    return Poly;
}

上面三个函数,加和减是比较简单,加法中,因为两个多项式都是按指数排序的,所以先各从一个多项式中取出一项,比较指数的大小,只有两个项的指数相同时才进行两项的加法,否则只是将指数较大的一项复制到结果多项式中,然后拥有较大指数项的多项式取下一项,而拥有较小项的多项式还是讲刚才取出来的一项与另一个多项式中取出来的新项进行比较,当任何一个多项式遍历完后,直接将另一个多项式剩余的项复制到结果多项式中就可以了;减法与加法是一样的,注意“减数多项式”的符号就可以了。乘法如果用简单的方法,即就像人做多项式惩罚一样,先计算出M*N个项来,然后再进行同内项合并或没计算出一个新项,就将它插入到结果多项式中正确的位置,这样编码,比较方便,但是时间复杂度是O(M*M*N*N),其中M*N是不可避免的,重要的是如何减少另一个M*N,简单算法中使得另一个M*N存在是因为没计算出一个新项,查找它的正确位置做了很多重复计算,假设用第一个多项式的I项依次去成另一个多项式的J1、J2…Jn项,得到一个结果项K1、K2…Kn项,简单算法中,对于每一个K都从已经得到的结果多项式开头去查找它的正确位置,由于最终结果多项式会有(<=M*N)项,但是(<=)并不影响复杂度的计算,所以总的复杂度就变为了O(M*M*N*N),但是实际上K1到Kn也是有序的,它们也是按指数从大到小依次排列的,如果能够用上这个信息,那么必然会减少许多计算,利用这个思想,整个乘法的复杂度变为O(M (N + M N)),即O(M*M*N),因为多项式记录了它的项数,所以很容易使M为两个多项式中较小的一个。
当然对于乘法还有一种方法是复杂度变为O(M*N*LogM*N),但是我认为这个复杂度下的算法应该要改变实现链表的数据结构(比如两个数组或(平衡)二叉树才能达到,大概是利用了二分查找,没有具体实现,想了一下),后两种复杂度很难说那个更好,这取决于M、N的相对大小。
在我写完这些代码的时候,对于C++封装具有安全性的理解更进一步,以两个Append函数作为例子,这两个函数只是为了方便编写其它函数而存在的,所以它可以不保证插入的节点不处在正确的位置上,大部分情况下这并不是用户想要的结果,而现在它暴露在外面,任何人都可以调用,所以很不安全;另一个比如链表数据结构也是暴露在外面的,任何人都可以随意修改其中指针的指向,而这种修改很容易造成致命的错误,绝不可以暴露在外面的,必须隐藏起来,但是C语言是做不到这些的(我不知道C语言是否可以做到,即使可以,那也要花费很大的力气吧)。而在C++中protected、private关键字可以帮助我们在一定程度上轻松地做到不让用户看到不应该看到的东西、不让用户做不应该做的操作。这也是这两个关键字存在的理由之一吧,应该是很重要的一个。
另一个感受是,这些代码接口的定义大致能表达它的功能,但是内部实现是变量命名就比较随意了,不利于阅读,有很重的臭味,另一点是Multi函数里处理的特殊情况较多,很可能是设计上出了问题或是逻辑并没有弄得很清楚。
最后附上简单求解多项式乘法的代码:

Polynomial* Multi(const Polynomial* Poly1,const Polynomial* Poly2)
{
    Polynomial* Poly = new Polynomial;
    Poly->ItemCount = 0;
    Poly->Items = NULL;
    PolynomialItem* p = Poly1->Items;
    while(p)
    {
        PolynomialItem* q = Poly2->Items;
        while(q)
        {
            //这个函数里的查找过程很费时间
            InsertPolyItem(&Poly,Multi(p,q));
            q = q->next;
        }
        p = p->next;
    }
    return Poly;
}

再贴一段简单显示多项式的代码,没有处理系数、指数为0,或者是指数为负数的情况,今天之内没有找到比较好的逻辑使代码尽量简单放弃了。

void Display(const Polynomial* Poly)
{
    if(Poly->ItemCount == 0) return;
    cout<<Poly->Items->Coeff<<"X^"<<Poly->Items->Power;
    PolynomialItem* p = Poly->Items->next;
    while(p)
    {
        if(p->Coeff > 0)
        {
            cout<<'+'<<p->Coeff<<"x^"<<p->Power;
        }
        else if(p->Coeff < 0)
        {
            cout<<p->Coeff<<"x^"<<p->Power;
        }
        p = p->next;
    }
}

遍历链表时比较容易忘记推进指针,从而造成无限循环,所以最好一写完while(p)语句马上加一句p = p->next;可以节省不少等待时间哦(改了代码得重新编译的%>_<%)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章