一、题目
将两个一元多项式相乘,最后将相乘的结果输出。
一元多项式指数默认递增。输入时依次输入每项系数和指数。
比如:
多项式A:1+2*x-4*x^4
多项式B:2-2*x+5^x^4
输入:
0 1 2 1 -4 4 0 0
2 0 -2 1 5 4 0 0
输出:
2 0 2 1 -4 2 -3 4 18 5 -20 8
代码重写
博客下面有留言,说代码错了。我确认了下,确实是错误的,输出结果不对
这还是我上学那会写的代码,而我又很久不写C语言的代码了。。
这道题目有两个难点:其一是数据结构--链表的实现;其二是多项式链表的乘法。
PS: 本文完整代码见仓库:03-linked-multi
先看第一点,链表结构的实现。我很久没写C语言了,也不想重新撸一遍链表结构。我之前在用户空间使用过linux内核的双链表结构,见:linux内核链表结构。所以,上网找了下,在Can I use Linux kernel linked list outside kernel code?中看到~kazutomo/list/list.h。链接中的代码,是个双链表。我们可以直接拿过来用,该双链表的函数接口,仍然遵循List Management Functions。我建议读下这个双链表结构的代码,质量还不错。
再来看第二点,多项式的乘法。一遍计算,一遍合并就好。大体过程如下。
下面是链表相乘部分的代码。双链表(list.h)的代码,见上面链接,或者上面仓库。
#include "list.h"
#include <stdio.h>
/**
* 链表相乘:(1+2x-4x^4) * (2-2x+5x^4) = (2+2x-4x^2-3x^4+18x^5-20x^8)
* 输入时依次输入每项系数和指数(系数和指数都为0结束):
* 注:输入假定,一元多项式已按照指数进行非降序排列
* 1 0 2 1 -4 4
* 2 0 -2 1 5 4
* 输出:
* 2 0 2 1 -4 2 -3 4 18 5 -20 8
*/
typedef struct _node {
struct list_head list;
int coeff; // 系数
int expon; // 指数
} node;
void print_linked(node *head) {
struct list_head *pos;
list_for_each(pos, &head->list) {
node *entry = list_entry(pos, node, list);
printf("coeff: %d, expon: %d\n", entry->coeff, entry->expon);
}
}
void create_linked(node *head, int info[], int len) {
for (int i = 0; i < len; i += 2) {
if ((i + 1 < len) && (info[i] != 0 || info[i + 1] != 0)) {
node *elem = (node *)malloc(sizeof(node));
elem->coeff = info[i];
elem->expon = info[i + 1];
list_add_tail(&elem->list, &head->list);
} else {
;
}
}
}
void destroy_linked(node *head) {
node *elem, *tmp;
list_for_each_entry_safe(elem, tmp, &head->list, list) {
list_del(&elem->list);
free(elem);
}
}
void multi_linked(node *linked_a, node *linked_b, node *linked_c) {
struct list_head *pos_a;
struct list_head *pos_b;
struct list_head *pos_c;
list_for_each(pos_a, &linked_a->list) {
node *entry_a = list_entry(pos_a, node, list);
int coeff_a = entry_a->coeff;
int expon_a = entry_a->expon;
list_for_each(pos_b, &linked_b->list) {
node *entry_b = list_entry(pos_b, node, list);
int coeff_b = entry_b->coeff;
int expon_b = entry_b->expon;
node *elem = (node *)malloc(sizeof(node));
elem->coeff = coeff_a * coeff_b;
elem->expon = expon_a + expon_b;
if (list_empty(&linked_c->list)) { // 存储结果的链表为空,直接插入
list_add_tail(&elem->list, &linked_c->list);
} else {
list_for_each(pos_c, &linked_c->list) {
node *entry_c = list_entry(pos_c, node, list);
int coeff_c = entry_c->coeff;
int expon_c = entry_c->expon;
if (elem->expon < expon_c) {
// 指数非降序,遇到第一个大于elem的指数,插入它之前
list_add(&elem->list, pos_c->prev);
break;
} else if (elem->expon == expon_c) {
// 指数非降序,遇到等于elem的指数,合并
entry_c->coeff += elem->coeff;
break;
} else {
// 指数非降序,遇到小于elem的指数,通常是继续向后找
// 如果此时是查找的最后一个元素--即,elem的指数最大,直接插入到最后
if (pos_c->next == &linked_c->list) {
list_add(&elem->list, pos_c);
break;
}
}
}
}
}
}
}
int main(int argc, char *argv[]) {
node linked_a;
node linked_b;
node linked_c;
INIT_LIST_HEAD(&linked_a.list);
INIT_LIST_HEAD(&linked_b.list);
INIT_LIST_HEAD(&linked_c.list);
int a_info[] = {1, 0, 2, 1, -4, 4};
int b_info[] = {2, 0, -2, 1, 5, 4};
create_linked(&linked_a, a_info, sizeof(a_info) / sizeof(a_info[0]));
print_linked(&linked_a);
printf("----------------------\n");
create_linked(&linked_b, b_info, sizeof(b_info) / sizeof(b_info[0]));
print_linked(&linked_b);
printf("----------------------\n");
multi_linked(&linked_a, &linked_b, &linked_c);
print_linked(&linked_c);
destroy_linked(&linked_a);
destroy_linked(&linked_b);
destroy_linked(&linked_c);
return 0;
}
编译与输出如下。
gcc.exe main.c -o main
./main
coeff: 1, expon: 0
coeff: 2, expon: 1
coeff: -4, expon: 4
----------------------
coeff: 2, expon: 0
coeff: -2, expon: 1
coeff: 5, expon: 4
----------------------
coeff: 2, expon: 0
coeff: 2, expon: 1
coeff: -4, expon: 2
coeff: -3, expon: 4
coeff: 18, expon: 5
coeff: -20, expon: 8
后面为错误代码,未删除。可跳过。
二、题目分析
首先得会一元多项式的相加:点击打开链接
注意:如果单纯的使用多个一元多项式相加,由于不知道链表的长度,不能建立与链表长度数相同的多项式个数。则需重新建立新链表在其上累加,且保证要相加两链表的不变。
一种写法是利用递归函数。还有一种是利用循环。
建立链表可以用单链表,由于多次用到所指元素的前一个节点,也可以用双向链表。
下面是利用单链表及循环实现目标。
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int coef;//coefficient:系数
int index;//index:指数
struct Node *next;
}node;
node *creat()
{
int coef,index;
node *head,*p;
head=(node *)malloc(sizeof(node));
p=head;
while(1)//系数和指数都为0,作为结束标识
{
scanf("%d",&coef);
scanf("%d",&index);
if(coef==0&&index==0)
break;
p->next=(node *)malloc(sizeof(node));
p=p->next;
p->coef=coef;
p->index=index;
}
p->next=NULL;
return head;
}
node *ridepolylist(node *la,node *lb)//ride:乘
{
int temp1,temp2;
node *p;
node *lc;
node *pa,*pb,*pc;
node *pc1,*pc2;
pa=la->next;
pb=lb->next;
//复制la指数到lc中
lc=(node *)malloc(sizeof(node));
lc=pc;
while(pa)
{
pc->next=(node *)malloc(sizeof(node));
pc=pc->next;
pc->coef=0;//lc初始化系数为0
pc->index=pa->index;
pa=pa->next;
}
pc->next=NULL;
//复制完成
//逐项相乘
pa=la->next;
while(pa)
{
pb=lb->next;
while(pb)
{
pc1=lc;
pc2=pc1->next;
temp1=(pa->coef)*(pb->coef);
temp2=(pa->index)+(pb->index);
while(pc2)//寻找在lc中位置
{
if(pc2->index<temp2)
{
pc1=pc2;
pc2=pc1->next;
}
else if(pc2->index==temp2)
{
pc2->coef=pc2->coef+temp1;//如果是0进行最后处理
break;
}
else
{
//这里要插在前方,用单项链表不好写,建立时应该用双向链表
p=(node *)malloc(sizeof(node));
p->coef=temp1;
p->index=temp2;
pc1->next=p;
p->next=pc2;
break;
}
}
if(!pc2)//插在最后位置
{
p=(node *)malloc(sizeof(node));
p->coef=temp1;
p->index=temp2;
pc1->next=p;
p->next=NULL;
}
pb=pb->next;
}
pa=pa->next;
}
return lc;
}
node *delay0elem(node *lc)//删除
{
node *p,*q;
p=lc;
q=p->next;
while(q)
{
if(q->coef==0)
{
p->next=q->next;
}
p=q;
q=p->next;
}
return lc;
}
void pri(node *head)
{
node *p;
p=head->next;
while(p)
{
printf("+%d*x^%d ",p->coef,p->index);
p=p->next;
}
printf("\n");
}
void main()
{
node *creat();
node *ridepolylist(node *la,node *lb);
node *delay0elem(node *lc);
void pri(node *head);
node *la,*lb,*lc;
printf("输入多项式la,以0 0作为结束标识:");
la=creat();
pri(la);
printf("输入多项式lb,以0 0作为结束标识:");
lb=creat();
pri(lb);
printf("输出两多项式乘机lc:");
lc=ridepolylist(la,lb);
lc=delay0elem(lc);
pri(lc);
}
pri(lc);
}
四、代码分析lc链表初始化时,复制了la.这步可以省略。它可以包含在创立新节点的情况里。