目录
一、前言与思考
思考:f(x)=4+3x+x^2,如何保存?
1.顺序表:只保存系数,指数就是下标,如图所示。
有效数据为3,总长度为5(5个格子)
问题:如果多项式为h(x)=1+3x^100+4x^200,这样的多项式过于稀疏,存储起来会浪费内存,顺序表长度至少为201,但是只存储了3个数字。因此选用链表
2. 链表:每个节点数据保存系数+指数,如图所示。
好处:多项式主要是用来进行运算,链表的插入和删除操作相对于顺序表更方便
二、实现基本操作
多项式的加法、减法、插入、删除、查找、输出。
定义:
// 定义
typedef struct
{
float coef;//系数
int expn;//指数
} ElemType;//类型名 ElemType为Linklist的数据对象名
typedef struct PNode
{
ElemType data;//数据 (系数+指数)
struct PNode* next;//后继指针
}PNode, * Poly;
初始化:
跟链表操作一样,断言不为空,令next=NULL;
//初始化多项式:数据域置空
void InitPoly(Poly po)
{
assert(po != NULL);
if (po == NULL)
return;
po->next = NULL;
}
输出:
循环遍历,输出data域
//输出多项式
void Show(Poly po)
{
for (PNode* p = po->next; p != NULL; p = p->next)
{
printf("%4.1fx^%d + ", p->data.coef, p->data.expn);//4.1保存一位小数
}
printf("\n");
}
查找函数:
//查找指数相同的结点,并返回该节点
以下为待完善代码,完整版代码在最后面:
static PNode* Search(Poly po, int expn)
{
for (pNode* p = po->next; p != NULL; p = p->next)
{
if (p->data.expn == expn)
return p;//找到了
else if (p->data.expn > expn)
return NULL;
}
return NULL;
}
问题:
1.合并同类项:系数为0->删除该结点(删除依赖于前驱)
因此,修改Search函数为查找前驱函数
//查找结点前驱函数
static PNode* SearchPrior(Poly po, int expn)
{
for (pNode* p = po->next; p->next != NULL; p = p->next)
{
if (p->next->data.expn == expn)
return p;//找到了
else if (p->next->data.expn > expn)
return NULL;
}
return NULL;
}
问题:
- 中间插入结点:若指数位于已有的指数中间项,需插入结点到两节点之间的位置。根据上述代码,会执行else if内函数,返回NULL。需要修改的是将返回值返回为当前结点,return p;
- 尾部插入:若指数比已有的指数大,需插入节点到尾部节点之后。根据上述代码,会跳出for循环,执行return NULL。需要修改代码返回当前结点,return p;
注意:
查找函数是为了后面插入函数而铺垫的,因为需要考虑对插入函数的三种情况:
- 如果待插入结点和查找的结点系数一致,应返回查找结点前驱结点
- 如果待插入结点比查找的结点系数小,应返回查找结点前驱结点,把待插入结点放在该节点前驱与该节点之间位置
- 如果待插入结点比查找的结点系数大,返回该链表最后一个结点,把带插入节点放在最后一个结点后面。
最终函数完整版为:
//在po中查找指数为expn的节点前驱,如果存在返回节点地址,不存在返回NULL
static PNode* SearchPrior(Poly po, int expn)
{
PNode* p;
for (p = po; p->next != NULL; p = p->next)
{
if (p->next->data.expn == expn)
return p;
else if (p->next->data.expn > expn)
return p;//return NULL;
}
return p;
}
插入函数:
说明:
调用SearchPrior函数返回查找后的结点,就不必再循环查找结点,直接将该结点进行判断即可
1.如果指数相同p->data.expn == val.expn,合并同类项:
系数相加减p->data.coef+=val.coef,如果加减之后系数之和为0,剔除该节点->剔除结点需要该节点的前驱,引入新结点PNode* q = p->next。
2.如果指数不同p->data.expn != val.expn:
判断该节点与已有结点的关系:如果该节点的指数位于已有节点之间,将该节点插入在中间;如果该节点的指数大于已有的最大结点的指数,将该结点插入在尾部。
补充:
1.C语言中浮点数有误差问题,要判断很小的范围以内,定义宏#define EPS 0.0000001,含义:在这个数以内,视为0
2.函数设计:引入flag的原因,后面有对多项式进行加减操作,调用该函数,flag用来保存符号位
//往po多项式插入数据val,按指数升序
bool Insert(Poly po, ElemType val, int flag)//flag标记正负号
{
#define EPS 0.0000001//在这个数以内,视为0
if (-EPS <= val.coef && val.coef <= EPS)
return false;
//PNode* p;
PNode* p = SearchPrior(po, val.expn);//调用上面SearchPrior函数返回查找后的结点
PNode* q = p->next;//剔除结点需要前驱
/*调用SearchPrior函数返回查找后的结点,就不必再循环查找结点,直接将该结点进行判断即可
*如果不调用SearchPrior函数,直接定义新结点p,会导致PNode* q = p->next出错
*/
//for (p = po; p->next != NULL; p = p->next)
if (q != NULL && q->data.expn == val.expn)//指数相同
{
//q->data.coef += val.coef * flag;
q->data.coef = q->data.coef + val.coef * flag;
if (-EPS <= q->data.coef && q->data.coef <= EPS)//系数为0,删除该节点
{
p->next = q->next;//剔除q
free(q);
}
}
else//指数不同
{
//动态创建内存q,将q作为新结点插入进链表
q = (PNode*)malloc(sizeof(PNode));
assert(q != NULL);
if (q == NULL)
return false;
q->data = val;
q->next = p->next;
p->next = q;
}
return true;
}
加法:
说明:
调用插入函数即可
//po2+po1
bool Add(Poly po1, Poly po2)
{
for (PNode* p = po2->next; p != NULL; p = p->next)
Insert(po1, p->data, +1);
return true;
}
减法:
系数改为负数即可
bool Sub(Poly po1, Poly po2)
{
for (PNode* p = po2->next; p != NULL; p = p->next)
Insert(po1, p->data, -1);
return true;
}
销毁:
//销毁多项式po
void Destroy(Poly po)
{
if (po == NULL || po->next == NULL)
return;
PNode* p = po->next;
while (po->next != NULL)
{
p = po->next;
po->next = p->next;
free(p);
}
}
三、完整代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 定义
typedef struct
{
float coef;//系数
int expn;//指数
} ElemType;//类型名 ElemType为Linklist的数据对象名
typedef struct PNode
{
ElemType data;//数据 (系数+指数)
struct PNode* next;//后继指针
}PNode, * Poly;
// 初始化多项式
void InitPoly(Poly po)
{
assert(po != NULL);
if (po == NULL)
return;
po->next = NULL;
}
//查找结点前驱函数
//在po中查找指数为expn的节点前驱,如果存在返回节点地址,不存在返回NULL
static PNode* SearchPrior(Poly po, int expn)
{
PNode* p;
for (p = po; p->next != NULL; p = p->next)
{
if (p->next->data.expn == expn)
return p;
else if (p->next->data.expn > expn)
return p;//return NULL;
}
return p;
}
//往po多项式插入数据val,按指数升序
bool Insert(Poly po, ElemType val, int flag)//flag标记正负号,1->正数,-1->负数
{
#define EPS 0.0000001//在这个数以内,视为0
if (-EPS <= val.coef && val.coef <= EPS)
return false;
//PNode* p;
PNode* p = SearchPrior(po, val.expn);//调用上面SearchPrior函数返回查找后的结点
PNode* q = p->next;//剔除结点需要前驱
/*调用SearchPrior函数返回查找后的结点,就不必再循环查找结点,直接将该结点进行判断即可
*如果不调用SearchPrior函数,直接定义新结点p,会导致PNode* q = p->next出错
*/
//for (p = po; p->next != NULL; p = p->next)
if (q != NULL && q->data.expn == val.expn)//指数相同
{
//q->data.coef += val.coef * flag;
q->data.coef = q->data.coef + val.coef * flag;
if (-EPS <= q->data.coef && q->data.coef <= EPS)//系数为0,删除该节点
{
p->next = q->next;//剔除q
free(q);
}
}
else//指数不同
{
//动态创建内存q,将q作为新结点插入进链表
q = (PNode*)malloc(sizeof(PNode));
assert(q != NULL);
if (q == NULL)
return false;
q->data = val;
q->next = p->next;
p->next = q;
}
return true;
}
//输出多项式
void Show(Poly po)
{
for (PNode* p = po->next; p != NULL; p = p->next)
{
printf("%4.1fx^%d + ", p->data.coef, p->data.expn);//4.1保存一位小数
}
printf("\n");
}
//po2+po1
bool Add(Poly po1, Poly po2)
{
for (PNode* p = po2->next; p != NULL; p = p->next)
Insert(po1, p->data, +1);
return true;
}
//减法
//系数改为负数
bool Sub(Poly po1, Poly po2)
{
for (PNode* p = po2->next; p != NULL; p = p->next)
Insert(po1, p->data, -1);
return true;
}
//销毁多项式po
void Destroy(Poly po)
{
if (po == NULL || po->next == NULL)
return;
PNode* p = po->next;
while (po->next != NULL)// (p!=plist)
{
p = po->next;
po->next = p->next;
free(p);
}
}
int main()
{
PNode po;
InitPoly(&po);
ElemType arr[] = { {1,0},{2,10},{3,20},{4,5},{5,200},{-3,20} };//{1,0}x的0次方,{2,10}2x^10...
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
Insert(&po, arr[i], 1);
}
PNode po2;
InitPoly(&po2);
ElemType arr2[] = { {4,0},{5,1},{6,200},{7,500} };
for (int i = 0; i < sizeof(arr2) / sizeof(arr2[0]); i++)
{
Insert(&po2, arr2[i], 1);
}
printf("多项式1为:\n");
Show(&po);
printf("多项式2为:\n");
Show(&po2);
//对加法测试
Add(&po, &po2);
printf("多项式1+多项式2为:\n");
Show(&po);
//对减法测试
Sub(&po, &po2);
printf("多项式1-多项式2为:\n");
Show(&po);
return 0;
}
测试结果:
注释:多项式后面的“+”是在输出函数为了连接的操作