数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-应用实例—>多项式加法运算

文章目录


一、题意理解与多项式表示

1.1 题意理解

题目:设计函数分别求两个一元多项式的乘积与和
( 1 ) 3 x 4 − 5 x 2 + 6 x − 2 (1)3{x^4} - 5{x^2} + 6x - 2 (1)3x4−5x2+6x−2
( 2 ) 5 x 20 − 7 x 4 + 3 x (2)5{x^{20}} - 7{x^4} + 3x (2)5x20−7x4+3x
输入样例:首先输入多项式项数,然后依次输入各项系数与指数。
4 3 4 -5 2 6 1 -2 0
3 5 20 -7 4 3 1

多项式①有4项,多项式②有3项。相加起来有7项,然后合并同类项后有5项,结果如下:
5 x 20 − 4 x 4 − 5 x 2 + 9 x − 2 5{x^{20}} - 4{x^4} - 5{x^2} + 9x - 2 5x20−4x4−5x2+9x−2
多项式①有4项,多项式②有3项。相乘起来有12项,然后合并同类项后有项,结果如下:
15 x 24 − 25 x 22 + 30 x 21 − 10 x 20 − 21 x 8 + 35 x 6 − 33 x 5 + 14 x 4 − 15 x 3 + 18 x 2 − 6 x {\rm{15}}{x^{24}}{\rm{ - 25}}{x^{22}}{\rm{ + 30}}{x^{21}}{\rm{ - 10}}{x^{20}}{\rm{ - 21}}{x^8}{\rm{ + 35}}{x^6}{\rm{ - 33}}{x^5}{\rm{ + 14}}{x^4}{\rm{ - 15}}{x^3}{\rm{ + 18}}{x^2}{\rm{ - 6}}x 15x24−25x22+30x21−10x20−21x8+35x6−33x5+14x4−15x3+18x2−6x

输出样例:按系数从大到小地输出多项式相乘的结果(第一行)和相加的结果(第二行):
15 24 -25 22 30 21 -10 20 -21 8 35 6 -33 5 14 4 -15 3 18 2 -6 1
5 20 -4 4 -5 2 9 1 -2 0


1.2 多项式表示

我们的求解思路如下

1. 多项式表示
2. 程序框架
3. 读多项式
4. 加法实现
5. 乘法实现
6. 多项式输出

多项式表示:
多项式的表示方法最核心的就是要表现多项式的关键信息:非零项的系数和指数。一对一对的这个信息要把它表示出来,这就是一个线性的序列。线性序列计算机上面有两种存储实现方式:数组、链表。

数组
数组的方法比较简单且调试也比较容易,但是数组来必须要事先确定数组的大小。当你这个多项式的项数事先不知道的时候,我们就只能以最大可能的项数来进行表示。实际上像素可能会没那么多,那就会造成空间的浪费。
链表
链表的方法正好克服了数组的缺点。它不需要事先知道有多少项,你来一个我给你处理一个,并把它作为一个结点插到这个链表里去。所以他的优点是动态性比较强,但是编的程序就会比较复杂,涉及到指针,调试也比较困难。
:对于这道题来讲,有一种比较好的实现方法:动态数组。但为了训练大家的链表方面的编程能力,这里仍然用链表。

数据结构设计:
链表他是由若干个结点所构成的,结点的信息:系数、指数、指向下一个结点的指针。

typedef struct PolyNode *Polynomial;
struct PolyNode {
	int coef;
	int expon;
	Polynomial link;
};

对于前面那两个多项式,我们用链表来表示,他们的结构如下图所示:
数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现


二、程序框架及读入多项式

2.1 程序框架

根据我们程序编程的这个要求,我们整个程序的框架大概如下。

int main()
{
	读入多项式1
	读入多项式2
	乘法运算并输出
	加法运算并输出
	return 0;
}

我们希望设计一些函数来辅助我们整个程序的这个完成,那我们应该设计哪些函数呢?

需要设计的函数:
• 读一个多项式
• 两多项式相乘
• 两多项式相加
• 多项式输出

根据这样的一种想法,我们的main函数的主要过程是这样:

①首先我们定义几个变量,其中P1 P2分别代表这两个输入的多项式,所以我们通过ReadPoly来读入这两个多项式,返回了P1 P2的指针。
②然后将这两个指针传递到函数Mult里面去做乘法运算,乘法运算的结果也是一个链表,所以返回的是一个结构的指针。
③接下来要把这个多项式输出,所以只要把链表的头作为参数传到函数PrintPoly,由他来输出多项式。
④我们还要需要做加法运算,那就把P1 P2作为参数传进Add函数中去。
⑤加法运算之后返回的是一个新的多项式的节点的指针,我把这个和的多项式Print出来。

相应代码如下:

int main()
{
	Polynomial P1, P2, PP, PS;
	P1 = ReadPoly();
	P2 = ReadPoly();
	PP = Mult( P1, P2 );
	PrintPoly( PP );
	PS = Add( P1, P2 );
	PrintPoly( PS );
	return 0;
}

2.2 读入多项式

有了这个框架了之后,首先我们来看一下怎么读多项式,相应代码结构如下:

Polynomial ReadPoly()
{
	……
	scanf("%d", &N);
	……
	while ( N-- ) {
		scanf("%d %d", &c, &e);
		Attach(c, e, &Rear);
	}
	……
	return P;
}

接着我们来详细分析一下其结构:

题目要求输入的数他的格式是这样的:
4 3 4 -5 2 6 1 -2 0
第一个整数是代表他有多少项,接下来是一对一对的系数指数。所以读多项式这个程序来讲,他应该是先读这个整数,接下来是一对一对的读入系数跟指数。所以我们整个这个程序框架如下:
①首先使用scanf把这个4读进来赋给变量N
②接下来做四轮的循环,那么就是while(N–)这种形式来控制循环的次数。
③然后每轮循环里面要做什么事情呢?就是系数指数读进来,分别放到c跟e里面去并由此构造一个结点,把这个节点插到多项式里面去。读的过程是从指数递降的顺序来进行读的,所以读入一个新的节点的时候,应该插在前面一个节点的后面。
所以,最后就形成我们所需要P1所指向的这样的一张链表:
数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现


在上面分析中有一些关键问题:
1、新的结点应该插到原来结果的后面,所以我们这里就需要有个指针Rear代表当前结果多项式的最后一项,使得我们后面构造的一个结点能找到最后一项并插在其后面。这个插入的过程就通过函数 Attach 来完成。在这个过程当中我们应该注意到,Rear原来是指向一个链表的最后。你插入了一个新结点之后,这个新的结点就成为这个链表的最后一项。所以Rear要指到新的结点上面去,那么也就意味着Rear这个变量在Attach这个函数里面必须要被改变,因此我们需要将Rear的地址传递进Attach函数。
数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现


2、我们说Rear是指向到结果多项指的最后一项,那么一开始的时候Rear值是什么呢?这里有两种处理方法

①Rear一开始都设为NULL,在Attach函数中根据Rear是否为NULL做不同处理
Rear是NULL的时候,说明是刚开始的第一个结点,这个时候就要申请这个结点,然后把Rear指向这个结点。如果Rear值不为NULL,那么我们就直接把这个新的结点插到Rear的后面。
②一开始我们构造一个空的结点,然后让Rear值指向这个空的结点,以后所有的新插入的结点全插在这个Rear后面。
如果是这样的一种进行处理,那么在Attach函数里面就不需要判别Rear是不是NULL。这是一种利用临时申请一个空的结点的方法,使得程序的处理起来一致性比较强,而且代码比较简单。当然,到了最后的时候你必须要把这个空结点删掉。
数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现


根据上面创建空结点的思路,Attach函数设计如下:

void Attach( int c, int e, Polynomial *pRear )
{ 
	Polynomial P;
	P = (Polynomial)malloc(sizeof(struct PolyNode));
	P->coef = c; /* 对新结点赋值 */
	P->expon = e;
	P->link = NULL;
	(*pRear)->link = P;
	*pRear = P; /* 修改pRear值 */
}

根据前面的这个分析,我们读入多项式的这个完整的程序就出来了:

Polynomial ReadPoly()
{
	Polynomial P, Rear, t;
	int c, e, N;
	scanf("%d", &N);
	P = (Polynomial)malloc(sizeof(struct PolyNode)); /* 链表头空结点 */
	P->link = NULL;
	Rear = P;
	while ( N-- ) {
		scanf("%d %d", &c, &e);
		Attach(c, e, &Rear); /* 将当前项插入多项式尾部 */
	}
	t = P; P = P->link; free(t); /* 删除临时生成的头结点 */
	return P;
}

三、加法、乘法运算及多项式输出

3.1 加法运算

多项式加法运算我们前面已经提过,具体步骤为:

①我们一开始可以用t1 t2分别指向这两个多项式。接下来为了处理方便,我们先构造一个空结点,可以把t1 t2的这些项插入到后面
②当t1 t2都不空的时候,使用一个while循环,比较当前t1 t2所指向的指数。指数相等就做系数相加,然后看看是不等于0,等于0就不管他了。如果不等于0,那就把新的这个系数和指数加到Rear后面。最后,t1 t2分别往后挪。如果t1 t2指向的指数不相等,那我们就比较一下看哪个大,把大的拷贝到Rear的后面。这个循环不断的做,只要这两个多项式都不空。
③当t1 t2有一个空的时候,循环就退出来了。退出来了之后要把另外一个可能还不空的全部接到Rear的后面。所以接下来分别要用两个while循环分别来处理 t1不空的情况跟t2不空的情况。然后最后返回多项式的头。

加法运算相应代码如下:

Polynomial Add(Polynomial P1, Polynomial P2)
{
	Polynomial t1 = P1;
	Polynomial t2 = P2;
	Polynomial temp;
	Polynomial P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL;
	Polynomial Rear = P;
	int sum;

	while (t1 && t2) {
		if (t1->expon == t2->expon) {
			sum = t1->coef + t2->coef; //记录这两项的系数
			if (sum) Attach(sum, t1->expon, &Rear); //如果系数相加不为0,则接到rear后
			t1 = t1->link; //P1 P2都往后挪
			t2 = t2->link;
		}
		else if (t1->expon > t2->expon) {
			Attach(t1->coef, t1->expon, &Rear);
			t1 = t1->link;
		}
		else {
			Attach(t2->coef, t2->expon, &Rear);
			t2 = t2->link;
		}
	}
	while (t1) {
		Attach(t1->coef, t1->expon, &Rear);
		t1 = t1->link;
	}
	while (t2) {
		Attach(t2->coef, t2->expon, &Rear);
		t2 = t2->link;
	}

	Rear->link = NULL;
	temp = P;
	P = P->link; /*令front指向结果多项式第一个非零项 */
	free(temp); /* 释放临时空表头结点 */

	return P;
}

3.2 乘法运算

两个多项式相乘有两种方法。

1、将乘法运算转换为加法运算
将乘法运算转换为加法运算,将P1当前项(ci,ei)乘P2多项式,然后把这些结果都加起来。

t1 = P1; t2 = P2; 
P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL; 
Rear = P; 
while (t2) { 
	Attach(t1->coef*t2->coef, t1->expon+t2->expon, &Rear); 
	t2 = t2->link; 
}

2、逐项插入
将P1当前项 ( c 1 i , e 1 i ) {\rm{(c}}{{\rm{1}}_{\rm{i}}}{\rm{,e}}{{\rm{1}}_{\rm{i}}}{\rm{)}} (c1i​,e1i​)乘P2当前项 ( c 2 i , e 2 i ) {\rm{(c}}{{\rm{2}}_{\rm{i}}}{\rm{,e}}{{\rm{2}}_{\rm{i}}}{\rm{)}} (c2i​,e2i​),并插入到结果多项式中。初始结果多项式可由P1第一项乘P2获得。接下来就是把P1的当前项跟P2当前项相乘,逐步的插到这里面去。具体代码如下所示:

Polynomial Mult(Polynomial P1, Polynomial P2)
{
	Polynomial P, Rear, t1, t2, t;
	int c, e;
	if (!P1 || !P2) return NULL;
	t1 = P1; t2 = P2;
	P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL;
	Rear = P;
	while (t2) { /* 先用P1的第1项乘以P2,得到P */
		Attach(t1->coef*t2->coef, t1->expon + t2->expon, &Rear);
		t2 = t2->link;
	}
	t1 = t1->link;
	//两重循环,让t1的每一项乘t2的每一项
	while (t1) {
		t2 = P2; Rear = P;
		while (t2) {
			e = t1->expon + t2->expon; //指数相加,系数相乘
			c = t1->coef * t2->coef;
			while (Rear->link && Rear->link->expon > e) //当前系数较小,则Rear往后挪动
				Rear = Rear->link;
			if (Rear->link && Rear->link->expon == e) {//指数相等则系数相加
				if (Rear->link->coef + c)
					Rear->link->coef += c;
				else {  //系数相加等于0,将其删掉
					t = Rear->link;
					Rear->link = t->link;
					free(t);
				}
			}
			else { //指数不相等,要做插入操作
				t = (Polynomial)malloc(sizeof(struct PolyNode));
				t->coef = c; t->expon = e;
				//开始插入
				t->link = Rear->link;
				Rear->link = t; Rear = Rear->link;
			}
			t2 = t2->link;
		}
		t1 = t1->link;
	}
	//删掉最开始的空结点,让返回的指针指向结果多项式的第一项
	t2 = P; P = P->link; free(t2);

	return P;
}

3.3 多项式输出

多项式输出实际上是一个链表的遍历问题,遍历链表的每一项,并输出对应系数和指数。

void PrintPoly(Polynomial P)
{ /* 输出多项式 */
	int flag = 0; /* 辅助调整输出格式用 */
	if (!P) { printf("0 0\n"); return; }
	while (P) {
		if (!flag)
			flag = 1;
		else
			printf(" ");
		printf("%d %d", P->coef, P->expon);
		P = P->link;
	}
	printf("\n");
}

四、整体代码与测试结果

整体代码如下所示:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

typedef struct PolyNode *Polynomial;
struct PolyNode {
	int coef;
	int expon;
	Polynomial link;
};

void Attach(int c, int e, Polynomial *pRear);
Polynomial ReadPoly();
void PrintPoly(Polynomial P);
Polynomial Add(Polynomial P1, Polynomial P2);
Polynomial Mult(Polynomial P1, Polynomial P2);

int main()
{
	Polynomial P1, P2, PP, PS;
	P1 = ReadPoly();
	P2 = ReadPoly();
	PrintPoly(P1);
	PrintPoly(P2);
	PP = Mult(P1, P2);
	printf("乘法结果为:");
	PrintPoly(PP);
	PS = Add(P1, P2);
	printf("加法结果为:");
	PrintPoly(PS);

	return 0;
}


void Attach(int c, int e, Polynomial *pRear)
{
	Polynomial P;
	P = (Polynomial)malloc(sizeof(struct PolyNode));
	P->coef = c; /* 对新结点赋值 */
	P->expon = e;
	P->link = NULL;
	(*pRear)->link = P;
	*pRear = P; /* 修改pRear值 */
}

Polynomial ReadPoly()
{
	Polynomial P, Rear, t;
	int c, e, N;
	printf("请输入多项式的项数:\n");
	scanf("%d", &N);
	P = (Polynomial)malloc(sizeof(struct PolyNode)); /* 链表头空结点 */
	P->link = NULL;
	Rear = P;
	printf("请输入各项系数与指数:\n");
	while (N--) {
		scanf("%d %d", &c, &e);
		Attach(c, e, &Rear); /* 将当前项插入多项式尾部 */
	}
	printf("\n");
	t = P; P = P->link; free(t); /* 删除临时生成的头结点 */
	return P;
}

Polynomial Add(Polynomial P1, Polynomial P2)
{
	Polynomial t1 = P1;
	Polynomial t2 = P2;
	Polynomial temp;
	Polynomial P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL;
	Polynomial Rear = P;
	int sum;

	while (t1 && t2) {
		if (t1->expon == t2->expon) {
			sum = t1->coef + t2->coef; //记录这两项的系数
			if (sum) Attach(sum, t1->expon, &Rear); //如果系数相加不为0,则接到rear后
			t1 = t1->link; //P1 P2都往后挪
			t2 = t2->link;
		}
		else if (t1->expon > t2->expon) {
			Attach(t1->coef, t1->expon, &Rear);
			t1 = t1->link;
		}
		else {
			Attach(t2->coef, t2->expon, &Rear);
			t2 = t2->link;
		}
	}
	while (t1) {
		Attach(t1->coef, t1->expon, &Rear);
		t1 = t1->link;
	}
	while (t2) {
		Attach(t2->coef, t2->expon, &Rear);
		t2 = t2->link;
	}

	Rear->link = NULL;
	temp = P;
	P = P->link; /*令front指向结果多项式第一个非零项 */
	free(temp); /* 释放临时空表头结点 */

	return P;
}

Polynomial Mult(Polynomial P1, Polynomial P2)
{
	Polynomial P, Rear, t1, t2, t;
	int c, e;
	if (!P1 || !P2) return NULL;
	t1 = P1; t2 = P2;
	P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL;
	Rear = P;
	while (t2) { /* 先用P1的第1项乘以P2,得到P */
		Attach(t1->coef*t2->coef, t1->expon + t2->expon, &Rear);
		t2 = t2->link;
	}
	t1 = t1->link;
	while (t1) {
		t2 = P2; Rear = P;
		while (t2) {
			e = t1->expon + t2->expon;
			c = t1->coef * t2->coef;
			while (Rear->link && Rear->link->expon > e)
				Rear = Rear->link;
			if (Rear->link && Rear->link->expon == e) {
				if (Rear->link->coef + c)
					Rear->link->coef += c;
				else {
					t = Rear->link;
					Rear->link = t->link;
					free(t);
				}
			}
			else {
				t = (Polynomial)malloc(sizeof(struct PolyNode));
				t->coef = c; t->expon = e;
				t->link = Rear->link;
				Rear->link = t; Rear = Rear->link;
			}
			t2 = t2->link;
		}
		t1 = t1->link;
	}
	t2 = P; P = P->link; free(t2);

	return P;
}

void PrintPoly(Polynomial P)
{ /* 输出多项式 */
	int flag = 0; /* 辅助调整输出格式用 */
	if (!P) { printf("0 0\n"); return; }
	while (P) {
		if (!flag)
			flag = 1;
		else
			printf(" ");
		printf("%d %d", P->coef, P->expon);
		P = P->link;
	}
	printf("\n");
}

运行,输入题目中给的测试样例,结果正确:
数据结构基础:P2.5-应用实例--->多项式乘法与加法运算-C实现

上一篇:CentOS7+hadoop2.6.4+spark-1.6.1


下一篇:开源中国社区创始人红薯:J2Cache开源中国两级缓存实践