数据结构与算法学习笔记(C语言)
多项式的链表实现
定义:在数学上,形如
的式子叫做一元多项式,包括系数a、b、c、d…还有幂次0、1、2、3…
根据多项式的特征,我们可以定义一个链表,让节点的数据域放系数和幂次,然后让next指针域指向下一个更高幂次的节点
抽象数据类型多项式Polynomial的实现
typedef struct term {
int coef; /*系数*/
int expn; /*幂次*/
struct term *next; /*next指针*/
}term, *Polynomial;
其中,term是项,即多项式系数连带幂次,项与项之间用next指针穿起来组成一元多项式,Polynomial是term *类型,也就是指向项的指针,这里我们把它固定为头指针,其他项的指针用term *以便区分
/*基本操作的函数原型说明*/
void CreatePolyn(Polynomial *p, int m);
/*输入m项的系数和指数,建立一个表示一元多项式的链表*/
void DestroyPolyn(Polynomial *p);
/*销毁多项式*/
void PrintPolyn(Polynomial p);
/*打印一元多项式*/
int PolynLength(Polyonmial p);
/*返回一元多项式的项数*/
void AddPolyn(Polynomial *pa, Polynomial *pb);
/*多项式相加,pa = pa + pb,销毁pb*/
void SubPolyn(Polynomial *pa, Polynomial *pb);
/*多项式相减,pa = pa -pb,销毁pb*/
void MulPolyn(Polynomial *pa, Polynomial *pb, Polynomial *pc);
/*多项式相乘,pc = pa * pb,销毁pa,pb*/
接下来,用C语言代码对上述操作进行实现
1.创建多项式链表
/*我们假定用户输入的数据都合法并且有效,先创建一个简易的一元多项式链表
假定用户按照规定:输入的每组系数、指数都是整数,并且系数不为0,指数是自然数,
按照每项指数不等,且升幂次序输入*/
void CreatePolyn(Polynomial *p, int m)
/*输入m项的系数和指数,尾插法建立一元多项式的有序链表*/
{
term *q, *r; int i; int coe; int exp;
/*初始化多项式链表,即创建一个只含有头节点的链表*/
*p = (Polynomial)malloc(sizeof(term));
(*p)->coef = 0;
(*p)->expn = -1;
(*p)->next = NULL;
q = *p;
/*让q始终指向尾节点*/
/*插入各项*/
printf("请以coef, expn的格式依次输入每一项的系数和指数:\n");
for (i = 0; i < m; i++) {
scanf("%d, %d", &coe, &exp);
r = (term *)malloc(sizeof(term));
r->coef = coe;
r->expn = exp;
r->next = NULL;
q->next = r;
q = r;
}
}
2.销毁多项式链表
void DestroyPolyn(Polynomial *p)
{
term *q, *r;
q = (*p)->next;
/*执行下面的循环直到链表为空*/
while (q) {
r = q->next;
free(q);
q = r;
}
/*释放头节点*/
free(*p);
*p = NULL;
}
3.打印一元多项式
/*先实现一个简陋的打印函数,依次将多项式每项的系数和指数打印出来*/
void PrintPolyn(Polynomial p) {
term *q; int i = 0;
q = p->next;
while (q) {
++i;
printf("第%d项的系数和指数分别为%d, %d\n", i, q->coef, q->expn);
q = q->next;
}
}
4.返回一元多项式的项数
int PolynLength(Polynomial p)
{
term *q; int i = 0;
q = p->next;
while(q) {
++i;
q = q->next;
}
return i;
}
5.多项式相加
/*pa = pa + pb,释放pb的头节点*/
void AddPolyn(Polynomial *pa, Polynomial *pb)
{
term *ha, *hb, *hc, *tmp;
ha = (*pa)->next; hb = (*pb)->next;
hc = *pa; int sum;
/*hc指向pa的头节点,同时充当结果多项式的尾节点*/
while (ha && hb) {
/*把指数比较小的插入*/
if (ha->expn < hb->expn) {
hc->next = ha;
hc = ha;
ha = ha->next;
}
else if(ha->expn > hb->expn) {
hc->next = hb;
hc = hb;
hb = hb->next;
}
else {
/*当两项指数相等的时候,看系数的和是否为0来做相应操作*/
sum = ha->coef + hb->coef;
/*释放hb*/
tmp = hb;
hb = hb->next;
free(tmp);
if (sum != 0) {
/*如果两项的系数和不为0,更改ha项的系数,插入新多项式链表中*/
ha->expn = sum;
hc->next = ha;
hc = ha;
ha = ha->next;
}
else {
/*如果指数相等系数和为0,把ha,hb这两项都释放掉*/
tmp = ha;
ha = ha->next;
free(tmp);
}
}//else
}//while
pc->next = ha ? ha: hb;
free(*pb);
}//AddPolyn
可以看出,上面多项式相加的算法其实跟有序链表合并的算法相差无几,就是多考虑个两项系数相等需要合并成一项的问题
6.多项式相减
/*pa = pa - pb也就是pa = pa + (-pb)*/
void SubPolyn(Polynomial *pa, Polynomial *pb)
{
/*先遍历pb,让pb中的每一项的系数取相反数,然后和pa相加得到结果pa
把打印多项式函数中的printf语句改为q->coef = -q->coef即得到系数反转函数
NegateCoef(Polynomial* pb);
Add(&pa, &pb);
该函数实现起来较为简单,自己动手补全实现代码吧*/
}
7.多项式相乘
/*
可以构造三个一维数组分别放pa,pb和结果pc的系数,让数组的长度等于最高幂次
加1,假设pa的最高幂次是m,pb的最高幂次是n,那么结果的最高幂次等于m x n,
之所以幂次加1,是因为幂次和数组下标对应,常数项幂次为0,正好在arr[0]处
*/
void MulPolyn(Polynomial *pa, Polynomial *pb)
{
/*首先,获取pa,pb的最高幂次*/
term *q, *r; int i, j;
for (q = *pa; !(q->next); q = q->next);
int m = q->expn;
for (q = *pb; !(q->next); q = q->next);
int n = q->expn;
/*若其中一个多项式链表为空,函数退出*/
if (m == -1 || n == -1) return;
int arr1[m + 1]; int arr2[n + 1]; int arr3[m * n + 1];
/*初始化数组元素为0,等会儿存入系数,幂次不存在的项系数恰为0*/
for (i = 0; i < m + 1; i++) arr1[i] = 0;
for (i = 0; i < n + 1; i++) arr2[i] = 0;
for (i = 0; i < m * n + 1; i++) arr3[i] = 0;
/*分别遍历pa,pb,把它们的系数存到对应的数组中*/
q = (*pa)->next;
while (q) {
i = q->expn;
arr1[i] = q->coef;
q = q->next;
}
q = (*pb)->next;
while (q) {
i = q->expn;
arr2[i] = q->coef;
q = q->next;
}
/*进行相乘操作,把相乘得到的系数存入数组3中*/
for (i = 0; i < m + 1; i++) {
for (j = 0; j < n + 1; j++) {
arr3[i + j] += arr1[i] * arr2[j];
}
}
/*再根据arr3[]幂次系数表建立多项式链表*/
*pc = (Polynomial)malloc(sizeof(term));
q = *pc;
/*让q始终指向结果多项式链表的尾节点,尾插法建立链表*/
for (i = 0; i < m * n + 1; i++) {
if (arr3[i] != 0) {
r = (term *)malloc(sizeof(term));
r->coef = arr3[i];
r->expn = i;
r->next = NULL;
q->next = r;
q = r;
}//if
}//for
/*如果不再需要pa,pb调用DestroyPolyn()函数销毁pa,pb*/
DestroyPolyn(&pa);
DestroyPolyn(&pb);
}//MulPolyn
哦,对了,补充一句,之前实现的链表合并或者是多项式相加等,通过改变原有节点链接关系得到新链表的方法都会使原有的链表遭到破坏,如果又需要,就得重新建立
如果不想使原有的链表遭到破坏,应该新建一个链表,通过创建新节点,复制原有链表节点的数据,然后把新节点链接起来,该怎么操作还怎么操作就是了
到这里,多项式的基本操作就完成了,要说确实不尽如人意,毕竟第一个创建多项式的函数写的太随意了,寄希望于用户遵守规定输入数据真就无健壮性可言呗
接下来,改进一下创建多项式的代码
void CreatePolyn(Polynomial *p, int m) {
term *q, *r, *s; int sum;
/*初始化多项式链表*/
*p = (Polynomial)malloc(sizeof(term));
(*p)->next = NULL;
(*p)->coef = 0;
(*p)->expn = -1;
printf("请按格式coef, expn输入每项的系数和指数");
printf("其中,系数为非0整数,指数为自然数:\n");
for (i = 0; i < m; i++) {
while (scanf("%d, %d", &coe, &exp) != 2) {
/*输入的两个数不都是整数*/
printf("输入错误!请重新输入:\n");
}
while (coe == 0 || exp < 0) {
/*输入的系数为0或者指数小于0*/
printf("输入的系数或者指数不合法!请重新输入:\n");
}
q = (term *)malloc(sizeof(term));
q->next = NULL;
q->coef = coe;
q->expn = exp;
/*接下来通过遍历多项式链表确定新项插入的位置*/
r = *p;
while(r->next && r->expn < q->expn) {
s = r;
/*s负责记录r的前一个节点位置*/
r = r->next;
}
if (r->expn < q->expn) {
/*表尾幂次低于新项,在多项式表尾插入新项
在空表的特殊情况下仍旧适用*/
r->next = q;
}
else if (r->expn > q->expn) {
/*需要在s与r节点之间插入*/
q->next = r;
s->next = q;
}
else {
/*r->expn == q->expn,需要改变既有项*/
sum = r->coef + q->coef;
/*新项不插入直接释放*/
free(q);
if (sum) {
/*修改既有项的系数*/
r->coef = sum;
}
else {
/*删除既有项*/
s->next = r->next;
free(r);
}
}
}//for
}
再美化一下打印一元多项式的函数
/*实现一个打印函数,依次将多项式按照开篇那样的形式打印出来*/
void PrintPolyn(Polynomial p) {
printf("p(x) =")
term *q;
q = p->next;
if (q) {
/*如果第一项指数为0,即是个常数,那么直接打印出来*/
if (q->expn == 0) {
printf(" %d", q->coef);
}
else {
/*如果第一项不是常数,按照 ax^n 打印*/
printf(" %dx^%d", q->coef, q->expn);
}
q = q->next;
}
while (q) {
/*非首项必不为常数,系数为正,则按照 + bx^m 打印出来*/
if (q->coef > 0) {
printf(" + %dx^%d", q->coef, q->expn);
}
else {
/*系数为负,则按照 - cx^k打印出来,注意不是 -cx^k*/
printf(" - %dx^%d", -q->coef, q->expn);
}
q = q->next;
}
printf("\n")
}
好,多项式的链表小演练就到这里,算是对之前链表学习的复习和巩固,为之后更高难度的数据结构的学习打好基础!