【ZZULI数据结构实验一】多项式的三则运算

【ZZULI数据结构实验一】多项式的四则运算

  • ♋ 结构设计
  • ♋ 方法声明
  • ♋ 方法实现
    • 🐇 定义一个多项式类型并初始化---CreateDataList
    • 🐇 增加节点---Getnewnode
    • 🐇 打印多项式类型的数据-- PrintPoly
    • 🐇 单链表的尾插--Listpush_back
    • 🐇 多项式相加--PolyAdd
    • 🐇 多项式相减-- PolySub
    • 🐇 多项式相乘--PolyMult
    • 🐇 销毁单链表--Destroy
  • ♋ 测试

📃博客主页: 小镇敲码人
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞

前言:本篇博客旨在个人学习,实现了多项式的加减乘运算,对代码的正确性不作100%的保证。

♋ 结构设计

这里我们使用带头单链表来存储多项式的数据(各项系数及其幂次),也可以用顺序表来存数据,但是这样头插不太方便,而且需要另外创建一个节点类型放一个项的系数和幂次。如果你对单链表还留有疑问,可以看看博主的这篇文章单链表详解
这个和下面的方法声明都放到头文件里。
在这里插入图片描述

typedef struct DataList
{int coe;//系数int power;//幂次struct DataList* next;
}List;

♋ 方法声明

List* CreateDataList();//输入数据并构造数据单链表的函数List* PolyAdd(List* A,List* B);//实现多项式相加List* PolySub(List* A,List* B);//实现多项式相减List* PolyMult(List* A,List* B);//实现多项式相乘List* Getnewnode(int coe_, int power_);//申请一个节点,并初始化void PrintPoly(List* A);//打印多项式void Listpush_back(List* C, int coe_,int power_);//尾插节点void Destroy(List* A);//销毁节点

♋ 方法实现

方法实现放到.c文件里。

在这里插入图片描述

🐇 定义一个多项式类型并初始化—CreateDataList

初始化就是放数据的过程,这里直接走一个循环,然后申请空间就可以了,如果你对申请空间有疑问,请看博主这篇文章C语言动态内存管理,这里我们规定输入数据时,应该先输入幂次大的节点(先输入系数,再输入幂次),然后下一次节点链接我们直接头插就可以保证多项式类型的节点从左到右是按照幂次升序存储的(方便后序的四则运算),单链表的头插比尾插简单(尾插需要找尾)

这里当然你也可以乱输入,增加一个排序函数就可以(按照幂次排)。小小实验我们并不需要这么麻烦,直接输入让它有序就可以了(dog。

这里我们选择带头的单链表,是为了避免头插时没有数据的判空。

代码实现:

List* CreateDataList()
{int coe_ = 0, power_ = 0;//初始化两个临时变量,用于输入每个节点的数据printf("请依次按照幂次从大到小输入数据,先输入多项式的系数,后输入幂次:\n");//提示信息List* Head = Getnewnode(-1, -1);//设置空的头节点while(scanf("%d%d", &coe_, &power_) != EOF){List* newnode = Getnewnode(coe_, power_);newnode->next = Head->next;Head->next = newnode;printf("请依次按照幂次从大到小输入数据,先输入多项式的系数,后输入幂次:\n");//提示信息,每次输入数据前都打印一次,这里会多打印一次无伤大雅} return Head;//返回头节点
}

🐇 增加节点—Getnewnode

我们在很多地方都需要申请节点,而且要初始化,为了不造成代码冗余,我们把其单独作为一个函数写出来,用的时候直接调用就好了。

代码实现:

List* Getnewnode(int coe_, int power_)
{List* newnode = (List*)malloc(sizeof(List));//在堆上申请空间if (newnode == NULL)//如果申请失败{printf("malloc Failed\n");exit(-1);}//初始化新节点newnode->coe = coe_;newnode->power = power_;newnode->next = NULL;//注意next要置空,否则会造成野指针的问题return newnode;//返回新节点
}

🐇 打印多项式类型的数据-- PrintPoly

这个函数就是单纯的打印,为了方便我们后序打印多项式类型看相加和相乘、以及相减的结果,当然你也可以通过调试查看。

代码实现:

void PrintPoly(List* A)//打印多项式
{assert(A);//防止A为空,我们不能对空解引用,assert函数相当于暴力检查List* cur = A->next;//从头节点的下一个节点开始遍历,因为头节点不存数据if (cur == NULL)//cur为空printf("多项式为空\n");while (cur != NULL)//cur不为空{if (cur->coe > 0 && cur != A->next)printf("+%d*(x^%d)", cur->coe, cur->power);elseprintf("%d*(x^%d)", cur->coe, cur->power);cur = cur->next;}printf("\n");
}

🐇 单链表的尾插–Listpush_back

在多项式相加的函数里面我们会用到单链表的尾插,如果你对其原理不太熟悉,请自行翻阅博主之前的博客。

代码实现:

void Listpush_back(List* C, int coe_, int power_)//尾插数据
{assert(C);//C不为空List* newnode = Getnewnode(coe_, power_);//申请新节点并初始化List* tail = C;while (tail->next)//找尾节点{tail = tail->next;}tail->next = newnode;//把新节点链接到尾节点的后面
}

🐇 多项式相加–PolyAdd

我们的多项式相加的思路类似双指针,O(N)时间复杂度内完成。

下面我们画图来分析一下这个过程:

在这里插入图片描述

  • 注意:这里为什么是尾插到C中呢?因为尾插才能保证C中数据是按幂次升序的,因为A和B中的数据都是按幂次升序排列的,我们为什么不把数据放到A和B中的其中一个呢,因为这样会改变A和B的内容,后面我们可能会对A、B做其它操作。

代码实现:

List* PolyAdd(List* A, List* B)
{assert(A && B);//断言,防止A或者B为空指针List* C = Getnewnode(-1, -1);//创建新的链表C,我们不希望改变链表A和B的值,所以把它们相加的结果存入C中List* curA = A->next;List* curB = B->next;while (curA && curB)//A和B的当前位置都不能为空{if (curA->power == curB->power)//如果当前幂次相等,A和B的系数相加,幂次不变,尾插到C中{int new_coe = curA->coe + curB->coe;if (new_coe != 0)Listpush_back(C, new_coe, curA->power);curA = curA->next;curB = curB->next;}else if (curA->power < curB->power)//如果B大,尾插curA{Listpush_back(C, curA->coe, curA->power);curA = curA->next;}else//否则尾插curB{Listpush_back(C, curB->coe, curB->power);curB = curB->next;}}//如果两个链表还有一个剩余,把剩下的数据尾插到C中while (curA){Listpush_back(C, curA->coe, curA->power);curA = curA->next;}while (curB){Listpush_back(C,curB->coe, curB->power);curB = curB->next;}return C;//返回相加的结果链表的头节点指针
}

🐇 多项式相减-- PolySub

多项式相减的大致思路都和多项式相加是一样的,有一个地方要注意,因为是A-B,当B中式子多时,A中对应项的系数就是0,要给B的系数加上负号。

代码实现:

// 定义PolySub函数,接受两个多项式链表A和B作为参数,返回相减后的多项式链表C  
List* PolySub(List* A, List* B)  
{  // 断言A和B都不为空,确保传入的多项式链表是有效的  assert(A && B);  // 创建一个新的链表C,并初始化其头节点(这里用-1作为占位符,实际使用中可能需要其他方式初始化)  List* C = Getnewnode(-1, -1);  // 初始化两个指向A和B链表中第一个实际节点的指针curA和curB  List* curA = A->next;  List* curB = B->next;  // 当curA和curB都不为空时,循环进行以下操作  while (curA && curB)  {  // 如果curA和curB指向的项的指数相同  if (curA->power == curB->power)  {  // 计算两个相同指数项的系数之差  int new_coe = curA->coe - curB->coe;  // 如果系数之差不为0,则将新的项(系数和指数)添加到C链表中  if (new_coe != 0)  Listpush_back(C, new_coe, curA->power);  // 移动curA和curB到下一个节点  curA = curA->next;  curB = curB->next;  }  // 如果curA指向的项的指数小于curB指向的项的指数  else if (curA->power < curB->power)  {  // 将curA指向的项(系数和指数)添加到C链表中  Listpush_back(C, curA->coe, curA->power);  // 移动curA到下一个节点  curA = curA->next;  }  // 否则(即curA指向的项的指数大于curB指向的项的指数)  else  {  // 将curB指向的项的相反数(系数取反,指数不变)添加到C链表中,实现减法  Listpush_back(C, -curB->coe, curB->power);  // 移动curB到下一个节点  curB = curB->next;  }  }  // 如果curA还有剩余节点(即A的某些项在B中没有对应项)  while (curA)  {  // 将这些项(系数和指数)添加到C链表中  Listpush_back(C, curA->coe, curA->power);  // 移动curA到下一个节点  curA = curA->next;  }  // 如果curB还有剩余节点(即B的某些项在A中没有对应项)  while (curB)  {  // 将这些项的相反数(系数取反,指数不变)添加到C链表中,实现减法  Listpush_back(C, -curB->coe, curB->power);  // 移动curB到下一个节点  curB = curB->next;  }  // 返回相减后的多项式链表C  return C;  
}

🐇 多项式相乘–PolyMult

相乘的思路是复用多项式相加的函数,先让B链表中的第一个项和A中各个项相乘(系数相乘,幂次相加),然后得到链表C,然后移动B继续和A中的各个相相乘,并执行多项式相加,最终得到结果。

代码实现:

// 定义PolyMult函数,接受两个多项式链表A和B作为参数,返回相乘后的多项式链表C  
List* PolyMult(List* A, List* B)  
{  // 断言A和B都不为空,确保传入的多项式链表是有效的  assert(A && B);  // 创建一个新的链表C,并初始化其头节点(这里用-1作为占位符)  List* C = Getnewnode(-1, -1);  // 初始化两个指针curA和curB,分别指向A和B链表中第一个实际节点  List* curA = A->next;  List* curB = B->next;  // 遍历curA指向的A链表中的每个项  while (curA)  {  // 计算当前curA指向的项与B链表中第一个项(curB指向的项)的乘积的系数和指数  int new_coe = (curA->coe) * (curB->coe);  int new_power = curA->power + curB->power;  // 将新的项(系数和指数)添加到C链表中  Listpush_back(C, new_coe, new_power);  // 移动curA到下一个节点  curA = curA->next;  }  // 初始化ans为NULL,用于存储最终的乘法结果  List* ans = NULL;  // 遍历curB指向的B链表中的每个项(从第二个项开始,因为第一个项已经在上面的循环中处理过了)  while (curB->next)  {  // 移动curB到下一个节点  curB = curB->next;  // 创建一个新的链表C1,用于存储当前curB指向的项与A链表中所有项的乘积结果  List* C1 = Getnewnode(-1, -1);  // 初始化curA,重新指向A链表中第一个实际节点  List* curA = A->next;  // 遍历curA指向的A链表中的每个项  while (curA)  {  // 计算当前curA指向的项与当前curB指向的项的乘积的系数和指数  int new_coe = (curA->coe) * (curB->coe);  int new_power = curA->power + curB->power;  // 将新的项(系数和指数)添加到C1链表中  Listpush_back(C1, new_coe, new_power);  // 移动curA到下一个节点  curA = curA->next;  }  // 将C链表和C1链表相加,得到当前curB指向的项与A链表相乘的结果,并更新ans  ans = PolyAdd(C, C1);  // 销毁C1链表,释放其占用的内存  Destroy(C1);  // 销毁C链表,释放其占用的内存(C现在保存的是上一轮的结果,我们不再需要它)  Destroy(C);  // 将ans赋值给C,准备进行下一轮的乘法运算  C = ans;  }  // 返回最终的乘法结果链表C  return C;  
}

这里由于我们的C1、和C链表每一次循环都会变成新的链表,我们要及时把旧的链表空间释放,防止内存泄漏的出现。

🐇 销毁单链表–Destroy

这里在多项式相乘函数里,会出现内存泄漏,我们需要及时回收空间,防止出现这种情况。

代码实现:

void Destroy(List* A)//销毁链表
{assert(A);List* cur = A;while (cur){List* cur_next = cur->next;//保存下一个节点的地址free(cur);//释放当前节点的空间cur = cur_next;//指针指向下一个节点}
}

♋ 测试

测试函数我们放到了main.c函数里,主要测试函数的各种功能是否和我们的预期一样,当然由于测试的数据有限,如有bug,欢迎指出。

#include"polynomial.h"void Test()
{List* A = CreateDataList();List* B = CreateDataList();List* AAddB = PolyAdd(A, B);List* ASubB = PolySub(A, B);List* AMultB = PolyMult(A, B);printf("多项式A: ");PrintPoly(A);printf("多项式B: ");PrintPoly(B);printf("多项式A+B: ");PrintPoly(AAddB);printf("多项式A-B: ");PrintPoly(ASubB);printf("多项式A*B: ");PrintPoly(AMultB);
}
int main()
{Test();return 0;
}

这里我们A输入:3 3 2 2 1 1
B输入: 4 4 3 3 2 2 1 1

运行结果:
在这里插入图片描述

和我们预期的结果一致。

这里我们A输入:1 5 1 3 1 1
B输入: 16 1 4 1 2

运行结果:

在这里插入图片描述
与预期结果一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/286459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java多线程实战-从零手搓一个简易线程池(一)定义任务等待队列

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️本系列源码仓库&#xff1a;多线程并发编程学习的多个代码片段(github) &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正…

【C语言】结构体

个人主页点这里~ 结构体 一、结构体类型的声明1、结构的声明2、结构体变量的创建和初始化3、声明时的特殊情况4、自引用 二、结构体内存对齐1、对齐规则2、存在内存对齐的原因3、修改默认对齐数 三、结构体传参四、结构体实现位段 一、结构体类型的声明 我们在指针终篇中提到过…

从零自制docker-5-【USER Namespace NETWORK Namespace】

文章目录 USER Namespace代码NETWORK Namespace代码块 USER Namespace 即进程运行在一个新的namespace中&#xff0c;且该namespace中的User ID和Group IDA在该namespace内外可以不同&#xff0c;可以实现在namspace的用户是root但是对应到宿主机并不是root Cloneflags增加一…

3款免费甘特图制作工具的比较和选择指南

GanntProject GanttProject https://www.ganttproject.biz/ 是一款项目管理和调度应用&#xff0c;适用于 Windows、macOS 和 Linux。它易于使用&#xff0c;无需任何设置&#xff0c;适用于个人用户和小型团队。该应用提供任务层次结构和依存关系、里程碑、基准行、Gantt 图表…

AI论文速读 | 具有时间动态的路网语义增强表示学习

论文标题&#xff1a; Semantic-Enhanced Representation Learning for Road Networks with Temporal Dynamics 作者&#xff1a; Yile Chen&#xff08;陈亦乐&#xff09; ; Xiucheng Li&#xff08;李修成&#xff09;; Gao Cong&#xff08;丛高&#xff09; ; Zhifeng Ba…

卓健易控zj-v8.0设备智能控费系统

卓健易控zj-v8.0设备智能控费系统 详细可联系&#xff1a;19138173009 在现今医疗技术日新月异、突飞猛进的时代&#xff0c;我院服务患者的实力与日俱增。随着先进辅助检查设备的不断完善和引进&#xff0c;医生们如同得到了得力助手&#xff0c;能够为患者做出更加精确的诊断…

TCP重传机制详解——04FACK

文章目录 TCP重传机制详解——04FACK什么是FACKFACK的发展为什么要引入FACK实战抓包讲解开启FACK场景&#xff0c;且达到dup ACK门限值开启FACK场景&#xff0c;未达到dup ACK门限值 为什么要淘汰FACK总结REF TCP重传机制详解——04FACK 什么是FACK FACK的全称是forward ackn…

JVM(二)——垃圾回收

三、垃圾回收 1、如何判断对象可以回收 1&#xff09;引用计数法 定义&#xff1a; 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是…

Java面试篇:Redis使用场景问题(缓存穿透,缓存击穿,缓存雪崩,双写一致性,Redis持久化,数据过期策略,数据淘汰策略)

目录 1.缓存穿透解决方案一:缓存空数据解决方案二&#xff1a;布隆过滤器 2.缓存击穿解决方案一:互斥锁解决方案二:设置当前key逻辑过期 3.缓存雪崩1.给不同的Key的TTL添加随机值2.利用Redis集群提高服务的可用性3.给缓存业务添加降级限流策略4.给业务添加多级缓存 4.双写一致性…

MySQL substr函数使用详解

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

景联文科技上新高质量大模型训练数据!

在过去的一年中&#xff0c;人工智能领域呈现出了风起云涌的态势&#xff0c;其中模型架构、训练数据、多模态技术、超长上下文处理以及智能体发展等方面均取得了突飞猛进的发展。 在3月24日举办的2024全球开发者先锋大会的大模型前沿论坛上&#xff0c;上海人工智能实验室的领…

c语言--内存函数的使用(memcpy、memcmp、memset、memmove)

目录 一、memcpy()1.1声明1.2参数1.3返回值1.4memcpy的使用1.5memcpy模拟使用1.6注意 二、memmove()2.1声明2.2参数2.3返回值2.4使用2.5memmove&#xff08;&#xff09;模拟实现 三、memset3.1声明3.2参数3.3返回值3.4使用 四、memcmp()4.1声明4.2参数4.3返回值4.4使用 五、注…

MySQL-extra常见的额外信息

本文为大家介绍MySQL查看执行计划时&#xff0c;extra常见的额外信息 Using index 表示使用了覆盖索引&#xff0c;即通过索引树可以直接获取数据&#xff0c;不需要回表。 表结构: CREATE TABLE t1 (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,ag…

IP SSL证书注册流程

使用IP地址申请SSL证书&#xff0c;需要用公网IP地址申请&#xff0c;申请之前确保直接的IP地址可以开放80或者443端口两者选择1个就好&#xff0c;端口不需要一直开放&#xff0c;只要认证的几分钟内开放就可以了&#xff0c;然后IP地址根目录可以上传txt文件。 IP SSL证书认…

vue3+vite - 报错 import.meta.glob() can only accept string literals.(详细解决方案)

报错说明 在vue3+vite项目中,解决报错: [plugin:vite:import-analysis] import.meta.glob() can only accept string literals. 如果我们报错差不多,就可以完美搞定这个错误。 解决教程 这个错误,是因为

【STM32嵌入式系统设计与开发】——9Timer(定时器中断实验)

这里写目录标题 一、任务描述二、任务实施1、ActiveBeep工程文件夹创建2、函数编辑&#xff08;1&#xff09;主函数编辑&#xff08;2&#xff09;USART1初始化函数(usart1_init())&#xff08;3&#xff09;USART数据发送函数&#xff08; USART1_Send_Data&#xff08;&…

蓝桥杯学习笔记(贪心)

在很久很久以前&#xff0c;有几个部落居住在平原上&#xff0c;依次编号为1到n。第之个部落的人数为 t 有一年发生了灾荒&#xff0c;年轻的政治家小蓝想要说服所有部落一同应对灾荒&#xff0c;他能通过谈判来说服部落进行联台。 每次谈判&#xff0c;小蓝只能邀请两个部落参…

HarborCDN技术分析

一、介绍 简要介绍 ​​Harbor​​ 是由VMware公司开源的企业级的Docker Registry管理项目&#xff0c;它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。Harbor 的所有组件都在 Dcoker 中部署&#xff0c;所以 Harbor 可使用 Docker C…

php反序列化刷题1

[SWPUCTF 2021 新生赛]ez_unserialize 查看源代码想到robots协议 看这个代码比较简单 直接让adminadmin passwdctf就行了 poc <?php class wllm {public $admin;public $passwd; }$p new wllm(); $p->admin "admin"; $p->passwd "ctf"; ec…

Redis中的事件

事件 概述 Redis服务器是一个事件驱动程序:服务器需要处理以下两类事件: 1.文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接&#xff0c;而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或者其他服务器)的通信会产生相应的文件…