数据结构实验一
-
实验序号(一)
-
实验名称:线性表的应用
-
实验目的:巩固顺序表和链表知识及其编程实现能力,训练解决实际问题的能力。
-
实验内容:一元稀疏多项式的表示和基本运算
-
实验类型:综合类
// ============================================================================a
// 20252328 数据结构实验一
// ============================================================================l
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// ============================================================================o
// 第一部分:数据结构设计
// ============================================================================n
typedef struct {
float coef; // 系数:使用 float 支持小数运算
int expn; // 指数:整数,代表 x 的幂次
} ElemType;
typedef struct LNode {
ElemType data;
struct LNode *next;
} LNode, *LinkList;
// 为提升代码可读性,将 LinkList 重命名为 polynomial
typedef LinkList polynomial;
// ============================================================================e
// 第二部分:核心基础操作
// ============================================================================p
//初始化带头结点的空链表
polynomial InitList() {
polynomial L = (polynomial)malloc(sizeof(LNode));
if (!L) { printf("内存分配失败\n"); exit(1); }
L->next = NULL; // 头结点 next 置空,表示多项式当前为 0
return L;
}
/**
* 有序插入 + 自动合并同类项 + 零系数过滤
* 时间复杂度:O(N),N 为当前链表长度。遍历找到插入位置。
* 算法思想:维护链表按指数升序排列。插入时只需比较指数:
* 1. 若指数已存在 → 系数累加。累加后若 |coef| < 1e-6,说明相互抵消,直接删除节点。
* 2. 若指数不存在 → 申请新节点,插入到 pre 和 pre->next 之间。
* 浮点数比较:计算机中 float 存在精度误差,用 fabs() < EPSILON。
*/
int InsertPolyn(polynomial P, ElemType e) {
if (fabs(e.coef) < 1e-6) return 0; // 零系数项无意义,直接过滤
polynomial pre = P;
// 寻找插入位置:跳过所有指数小于 e.expn 的节点
while (pre->next && pre->next->data.expn < e.expn) {
pre = pre->next;
}
// 情况A:指数已存在,合并同类项
if (pre->next && pre->next->data.expn == e.expn) {
pre->next->data.coef += e.coef;
// 合并后系数趋近于0,说明正负抵消,释放该节点保持稀疏性
if (fabs(pre->next->data.coef) < 1e-6) {
polynomial q = pre->next;
pre->next = q->next;
free(q);
}
}
// 情况B:指数不存在,有序插入
else {
polynomial q = (polynomial)malloc(sizeof(LNode));
q->data = e;
q->next = pre->next; // 新节点指向后继
pre->next = q; // 前驱指向新节点
}
return 1;
}
/**
* 多项式创建
* 调用 InitList 创建空表,循环读入 m 个项
*/
polynomial CreatPolyn(int m) {
polynomial P = InitList();
ElemType e;
printf("请输入%d个项(系数 指数):\n", m);
for (int i = 0; i < m; i++) {
scanf("%f %d", &e.coef, &e.expn);
InsertPolyn(P, e); // 每读入一项,立即触发有序插入与合并
}
return P;
}
/**
* 多项式备份
* 备份了 Pa/Pb 原始数据
* 实现:遍历原链表, 复用核心插入逻辑,InsertPolyn
*/
polynomial CopyPolyn(polynomial P) {
polynomial C = InitList();
for (polynomial p = P->next; p; p = p->next) {
InsertPolyn(C, p->data);
}
return C;
}
/**
* 链表销毁
* 标准单链表遍历释放,从头结点开始。
*/
void DestroyPolyn(polynomial P) {
polynomial p = P, q;
while (p) { q = p->next; free(p); p = q; }
}
/**
* 加法
* 核心逻辑:
* 使用三指针 p1(被加数), p2(加数), p3(结果尾指针)。
* 1. p1.expn < p2.expn:p1 指数小,直接链入结果,p1 后移。
* 2. p1.expn > p2.expn:p2 指数小,直接链入结果,p2 后移。
* 3. 指数相等:系数相加。若和不为0,更新 p1 系数后链入,释放 p2 节点(节省内存);
* 若和为0,释放 p1、p2 两个节点(抵消消失)。
* 内存管理:本函数为会改变 Pa/Pb 的数据节点,并释放它们的头结点。
* 所以外部调用前保留原多项式,先 CopyPolyn。
*/
polynomial AddPolyn(polynomial Pa, polynomial Pb) {
polynomial Pc = InitList();
polynomial p1 = Pa->next, p2 = Pb->next, p3 = Pc;
while (p1 && p2) {
if (p1->data.expn < p2->data.expn) {
p3->next = p1; p3 = p1; p1 = p1->next;
} else if (p1->data.expn > p2->data.expn) {
p3->next = p2; p3 = p2; p2 = p2->next;
} else { // 指数相同,合并系数
float sum = p1->data.coef + p2->data.coef;
if (fabs(sum) > 1e-6) { // 未完全抵消
p1->data.coef = sum;
p3->next = p1; p3 = p1; p1 = p1->next;
polynomial tmp = p2; p2 = p2->next; free(tmp); // 释放加数节点
} else { // 完全抵消
polynomial tmp1 = p1, tmp2 = p2;
p1 = p1->next; p2 = p2->next;
free(tmp1); free(tmp2); // 两项均消失
}
}
}
// 将剩余未比较完的链表直接拼接
p3->next = p1 ? p1 : p2;
free(Pa); free(Pb); // 释放原头结点,防止内存泄漏
return Pc;
}
/**
* 减法
* Pa - Pb = Pa + (-Pb)
* - 算法步骤:
* 1. 将 Pa 所有项插入结果链表 Pc。
* 2. 遍历 Pb,将每一项系数取反(e.coef = -e.coef),再插入 Pc。
* 3. 用InsertPolyn 完成指数对齐、系数相加(实为相减)、同类项合并。
* 时间复杂度 O(m+n)。
*/
polynomial SubPolyn(polynomial Pa, polynomial Pb) {
polynomial Pc = InitList();
for (polynomial p = Pa->next; p; p = p->next) InsertPolyn(Pc, p->data);
for (polynomial p = Pb->next; p; p = p->next) {
ElemType e = p->data; e.coef = -e.coef; // 系数取反
InsertPolyn(Pc, e);
}
return Pc;
}
/**
* 多项式乘法
* (a1*x^i + a2*x^j) * (b1*x^k + b2*x^l)
* = a1*b1*x^(i+k) + a1*b2*x^(i+l) + a2*b1*x^(j+k) + a2*b2*x^(j+l)
* - 算法步骤:双重循环遍历 Pa 和 Pb。新系数 = c1*c2,新指数 = e1+e2。
* 将新项调用 InsertPolyn 插入结果链表。
* - 自动处理:InsertPolyn 会自动按指数排序、合并同类项。
* - 复杂度:O(m*n*logK),K 为结果项数(受插入排序影响)。
*/
polynomial MulPolyn(polynomial Pa, polynomial Pb) {
polynomial Pc = InitList();
for (polynomial p1 = Pa->next; p1; p1 = p1->next)
for (polynomial p2 = Pb->next; p2; p2 = p2->next) {
ElemType e;
e.coef = p1->data.coef * p2->data.coef;
e.expn = p1->data.expn + p2->data.expn;
InsertPolyn(Pc, e); // 插入时自动排序与合并
}
return Pc;
}
/**
* ai实现 : 多项式除法(长除法求商)
* - 类比整数长除法。每次用【当前余式的首项】除以【除式的首项】,得到商的一项。
* 商项 × 除式 → 从余式中减去 → 更新余式 → 循环直到余式最高次 < 除式最高次。
* - 算法步骤:
* 1. 检查除数是否为零多项式。
* 2. 拷贝 Pa 作为当前余式 R。
* 3. while(R最高指数 >= Pb最高指数):
* - 计算商项:coef = R.lead.coef / Pb.lead.coef,expn = R.lead.expn - Pb.lead.expn
* - 将商项加入结果 Pc。
* - 计算修正项:修正项 = -(商项 × Pb),调用 InsertPolyn(R, 修正项) 实现 R = R - 商项*Pb。
* 4. 循环结束,Pc 即为商。若 R 非空,说明有余式(本程序仅返回商)。
* - 注意:多项式除法通常有余式,本实现遵循工程习惯,打印提示并忽略余式。
*/
polynomial DivPolyn(polynomial Pa, polynomial Pb) {
if (!Pb->next) { printf("⚠️ 错误:除数不能为零多项式!\n"); return InitList(); }
polynomial Pc = InitList();
polynomial R = CopyPolyn(Pa); // 创建余式副本,保护原数据
// 长除法核心循环:只要余式最高次 >= 除式最高次,就能继续除
while (R->next && R->next->data.expn >= Pb->next->data.expn) {
ElemType term;
// 商项指数 = 余式首项指数 - 除式首项指数
term.expn = R->next->data.expn - Pb->next->data.expn;
// 商项系数 = 余式首项系数 / 除式首项系数
term.coef = R->next->data.coef / Pb->next->data.coef;
if (fabs(term.coef) < 1e-6) break; // 系数趋近0,停止除法
InsertPolyn(Pc, term); // 商项加入结果
// 关键步骤:R = R - term * Pb
// 实现技巧:计算 -(term * Pb) 的每一项,插入 R。InsertPolyn 会自动完成减法与合并
for (polynomial p_div = Pb->next; p_div; p_div = p_div->next) {
ElemType sub;
sub.coef = -(term.coef * p_div->data.coef); // 负号实现减法
sub.expn = term.expn + p_div->data.expn;
InsertPolyn(R, sub);
}
}
if (R->next) printf("(注:存在余式,已自动忽略)\n");
DestroyPolyn(R); // 释放余式临时链表
return Pc;
}
/**
* 多项式打印(格式化输出)
* 书写习惯:首项正号不显示,负号直接带出;后续项显式显示 + 或 -。
* 指数处理:expn=0 不输出 x;expn=1 输出 x;expn>1 输出 x^n。
* 系数处理:使用 fabs() 输出绝对值,符号由前面的 +/- 控制。
*/
void PrintPolyn(polynomial P) {
if (!P->next) { printf("0\n"); return; } // 零多项式特判
polynomial p = P->next;
int first = 1; // 标记是否为首项
while (p) {
float c = p->data.coef;
int e = p->data.expn;
if (first) { if (c < 0) printf("-"); } // 首项负号处理
else { printf(c > 0 ? " + " : " - "); } // 后续项符号处理
printf("%.2f", fabs(c)); // 输出系数绝对值
if (e > 0) printf("x"); // 指数>0 输出变量 x
if (e > 1) printf("^%d", e); // 指数>1 输出幂次
first = 0; p = p->next;
}
printf("\n");
}
// ============================================================================
// 第三部分:交互菜单与main主函数
// ============================================================================
void PrintMenu() {
printf("\n╔════════════════════════════════════╗\n");
printf("║ 一元稀疏多项式四则运算系统 \n");
printf("╠════════════════════════════════════╣\n");
printf("║ 1. 创建/重置多项式 Pa & Pb ║\n");
printf("║ 2. 计算加法 Pa + Pb ║\n");
printf("║ 3. 计算减法 Pa - Pb ║\n");
printf("║ 4. 计算乘法 Pa * Pb ║\n");
printf("║ 5. 计算除法 Pa / Pb (求商) \n");
printf("║ 0. 退出程序 ║\n");
printf("╚════════════════════════════════════╝\n");
printf("请输入操作编号: ");
}
int main() {
// 编码切换:若直接双击 .exe 出现中文乱码,需要取消下一行注释
// system("chcp 65001 > nul");
polynomial Pa = InitList();
polynomial Pb = InitList();
polynomial Pr = NULL; // 用于接收运算结果
int m1 = 0, m2 = 0;
int choice;
// 初始化空多项式,防止空指针访问
Pa = CreatPolyn(0); Pb = CreatPolyn(0);
//
while (1) {
PrintMenu();
// 安全输入处理:检查 scanf 返回值,防止用户输入字母导致死循环
if (scanf("%d", &choice) != 1) {
while (getchar() != '\n'); // 清空输入缓冲区中的非法字符
printf("❌ 输入无效,请重新输入数字!\n");
continue;
}
switch (choice) {
case 1: // 重建多项式
DestroyPolyn(Pa); DestroyPolyn(Pb); // 先释放旧内存
printf("=== 重新创建多项式 ===\n");
printf("Pa 项数: "); scanf("%d", &m1);
Pa = CreatPolyn(m1);
printf("Pb 项数: "); scanf("%d", &m2);
Pb = CreatPolyn(m2);
printf("✅ 当前 Pa = "); PrintPolyn(Pa);
printf(" 当前 Pb = "); PrintPolyn(Pb);
break;
case 2: // 加法
if (!Pa->next && !Pb->next) { printf("⚠️ 请先创建多项式!\n"); break; }
// 必须拷贝!因为 AddPolyn 会销毁传入的链表节点
polynomial Pa_c1 = CopyPolyn(Pa);
polynomial Pb_c1 = CopyPolyn(Pb);
Pr = AddPolyn(Pa_c1, Pb_c1);
printf("📊 计算结果: Pa + Pb = "); PrintPolyn(Pr);
DestroyPolyn(Pr); Pr = NULL;
break;
case 3: // 减法
if (!Pa->next && !Pb->next) { printf("⚠️ 请先创建多项式!\n"); break; }
Pr = SubPolyn(Pa, Pb);
printf(" 计算结果: Pa - Pb = "); PrintPolyn(Pr);
DestroyPolyn(Pr); Pr = NULL;
break;
case 4: // 乘法
if (!Pa->next && !Pb->next) { printf("️ 请先创建多项式!\n"); break; }
Pr = MulPolyn(Pa, Pb);
printf("📊 计算结果: Pa * Pb = "); PrintPolyn(Pr);
DestroyPolyn(Pr); Pr = NULL;
break;
case 5: // 除法
if (!Pa->next && !Pb->next) { printf("⚠️ 请先创建多项式!\n"); break; }
Pr = DivPolyn(Pa, Pb);
printf("📊 计算结果: Pa / Pb = "); PrintPolyn(Pr);
DestroyPolyn(Pr); Pr = NULL;
break;
case 0: // 退出
printf("👋 感谢使用,程序已退出。\n");
DestroyPolyn(Pa); DestroyPolyn(Pb); // 善后清理
return 0;
default:
printf("❌ 无效选项,请重新选择!\n");
}
}
}

浙公网安备 33010602011771号