数据结构第二章

第二章

2.1.1引子:多项式表示

例:一元多项式及其运算
一元多项式: f ( x ) = a 0 + a 1 x + ⋅ ⋅ ⋅ + a n − 1 x n − 1 + a n x n f(x)=a_0+a_1x+···+a_{n-1}x^{n-1}+a_nx^n f(x)=a0+a1x++an1xn1+anxn
主要运算:多项式相加、相减、相乘等

分析:如何表示多项式?
多项式的关键数据:
》多项式系数 n n n
》各项系数 a i a_i ai及指数 i i i

方法1:顺序存储结构直接表示

数组各分量对应多项式各项:
a[i]:项 x I x^I xI的系数 a i a_i ai
例如: f ( x ) = 4 x 3 − 3 x 2 + 1 f(x)=4x^3-3x^2+1 f(x)=4x33x2+1
下标:0 1 2 3 4 5 …
a[i] 1 0 -3 0 0 4 …
1 0 − 3 x 3 -3x^3 3x3 0 0 4 x 3 4x^3 4x3

两个多项式相加:两个数组对应分量相加

问题:如何表示多项式 x + 3 x 2 000 x+3x^2000 x+3x2000
会造成空间的浪费

方法2:顺序存储结构表示非零项

每个非零项 a i x i a_ix^i aixi设计两个信息:系数 a i a_i ai和指数 i i i
可以将一个多项式看成是一个 ( a i , i ) (a_i, i) (ai,i)二元组的集合。

用结构数组表示:数组分量是由系数 a i a^i ai、指数 i i i组成的结构。对应一个非零项

例如: P 1 ( x ) = 9 x 12 + 15 x 8 + 3 x 2 P_1(x)=9x^{12}+15x^8+3x^2 P1(x)=9x12+15x8+3x2
下标i: 0 1 2 …
系数 a i a^i ai: 9 15 3 …
指数i: 12 8 2

按照指数大小有序存储
相加过程:从头开始,比较两个多项式当前对应项的指数
p1: (9, 12), (15, 8), (3, 2)
P2: (26, 19), (-4, 8), (-13, 6), (82, 0)
P3: (26, 19), (9, 12), (11, 8), (-13, 6), (3, 2), (82, 0)

方法3:链表结构存储非零项

链表中每个结点存储多项式中的一个非零项,包括指数和系数两个数据域以及一个指针域

2.1.2线性表及顺序存储

什么是线性表?

多项式表示问题的启示:
1.同一个问题可以有不同的表示(存储)方法
2.有一类共性问题:有序线性序列的组织和管理

“线性表”:由同类型数据结构构成有序序列的线性结构
》表中元素个数称为线性表的长度
》线性表没有元素时,称为空表
》表起始位置称表头,表结束位置称表尾

线性表的抽象数据类型描述

类型名称:线性表
数据对象集:线性表是n(>=0)个元素构成的有序序列( a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an
操作集:线性表$L \in List $,整数i表示位置,元素 X ∈ E l e m e n t T y p e X \in ElementType XElementType
线性表的基本操作主要有:
1、List MakeEmpty(); 初始化一个空线性表
2、ElementType FindKth(int K, List L); 根据位序K,返回相应元素
3、int Find(ElementType X, List L); 在线性L中查找X的第一次出现位置
4、void insert(ElementType X, int i, List L); 在位序i前插入一个新元素X;
5、void Delete(int i, List L); 删除指定位序i的元素
6、int Length(List L); 返回线性表 L 的长度 n

线性的顺序存储实现

主要操作的实现

1.初始化(建立空的顺序表)
List MakeEmpty() {
	List PtrL;
	PtrL = (List)malloc(sizeof(struct LNode));
	PtrL->Last = -1;

	return Ptrl;
}
2.查找
int Find(ElenentType X, List Ptrl) {
	int i = 0;
	while (i <= PtrL->Last && PtrL->Data[i] != X) {
		i++;
	}
	if (i > PtrL->Last) {
		return -1;/*如果没找到,返回-1*/
	}
	else
		return i;/*找到后返回的是存储位置*/
}
3.插入(第i(1<=i<=i+1)个位置上插入一个值为一个值为X的新元素)
/*
* 插入之前要判断数组是否已满
* 以及插入位置是否合法
* 插入时需要将数组元素从最后一位开始向后移动一位直到第i位(包括第i位)
* 最后将要插入的元素值赋给第i位,并且线性表长度加1
*/
void Insert(ElenmentType X, int i, List Ptrl) {
	int j;
	if (Ptrl->last == MAXSIZE - 1) {/*列表控件已满,不能插入*/
		printf("表满");
		return;
	}
	if (1 < 1 || i->Ptrl->Last + 2) {/*检查插入位置的合法性*/
		printf("位置不合法");
	}
	if (j = Ptrl->Last; j = i - 1; j--) {
		Ptrl->Data[j + 1] = Ptrl->[j];/*将ai~an倒序向后移动*/
	}
	Ptrl->Data[i - 1] = X;
	Ptrl->Last++;
}
4.删除(删除表的第i(1<=i<=n)个位置上的元素)
/*
* 删除之前要判断数组是否为空
* 删除时要判读删除的位置是否合法
* 删除时,先将第i个位置的元素删除,然后依次将第i-1位到最后一位元素向前移动一位
* 最后线性表长度减1
*/
void Delete(int i, List Ptrl) {
	int j;
	if (0 == Ptrl->last) {/*判读是否为空表*/
		printf("为空");
		return;
	}
	if (i < 1 || i > Ptrl->Last + 1) {
		printf("删除的位置不合法");/*检查删除位置的合法性*/
	}
	for (j = i; j <= Ptrl->Last; j++) {
		Ptrl->Data[j - 1] = Ptrl->Data[j];/*将a{i+1}~an顺序向前移动*/
	}
	Ptrl->Last--;            /*Last仍然指向最后元素*/
	return;
}

线性表的链式存储实现

不要求逻辑上相邻的两个元素物理上也相邻:通过“链”建立起数据元素之间的逻辑关系。
·插入、删除不需要移动元素,只需要修改“链”

主要操作的实现

1.求表长
/*
*首先令指针P指向链表第一个节点
*此时记录表长的变量为0;
*如果P!=0,记录表长的变量值加1
*p=p->Next;
*/
int Length(List PtrL){
List p = PtrL;    /*p指向表的第一个节点*/
int j = 0;
while(p){
j++;
p=p->Next
}
}
2.查找

(1)按序号查找:FindKth;

/*
*首先判断链表是否为空
*然后判断要查找的序号是否合法
*令指针P指向链表第一个元素,i=1, 如果i=K,K为要查找的序号,则返回P
*如果i!=K,令P=P->Next,i++,再次判断
*如果直到 P=NULL 都不能找到 i=k 的情况,则返回NULL
*/
List FindKth(int K, List PtrL){
List p = PtrL;
int i = 1;
while(p != NULL && i < K)
p = p->Next;
i++;
}
if(i == K)return p;/*找到第K个,返回指针*/
else return NULL;/*否则返回空*/

(2)按值查找:Find

/*
*首先判断链表是否为空
*令指针P指向链表第一个元素,如果P->value=X, X为要查找的值,则返回P
*如果P->value!= X, 令 P=P->Next; 
*如果直到 P=NULL 都不能找到 P->value = X 的情况,则返回NULL
*/
List Find(ElementType X, List PtrL){
List p = Ptrl;
while(p!=NULL && p->Data != X){
p = p->Next;
}
retutn p;//p=NULL 时就是返回 NULL
}
3.插入(在第i-1(1<=i<=n+1))个结点后插入一个值为X的新结点
/*
*首先判断链表是否为空
*然后判断插入位置是否合法
*然后令指针 S 指向 要插入的结点
*指针 P 指向第i-1个结点,指针 Q 指向 第i个结点
注意:(不包括插入第1个结点)指针Q可以换成P->Next
* S->Next = Q;     P->Next = S;
*/
List Inseert(ElementType X, int i, List PtrL){
List p, s;
if(1==i){      /*新结点插入表头*/
s=(List)malloc(sizeof(struct LNode));/*申请、填装结点*/
s->Data = X;
s->Next = PtrL;
return s;/*返回新表头指针*/
}
p = FindKth(i-1, PtrL);/*查找第i-1个结点*/
if(p == NULL){          /*第i-1个不存在,不能插入*/
printf("参数i错");
return NULL;
}
else{
s = (List)malloc(sizeof(struct LNode));/*申请、填装结点*/
s->Data = X;
s->Next = p->Next;           /*新结点插入在第i-1个结点的后面*/
p->Next = s;
return PtrL;
}
}
4.删除(删除链表的第i(1<=i<=n)个位置上的结点)
/*
*判断链表是否为空
*判断删除位置是否合法(1<=i<=n)
*注意:还要判断删除位置上是否有元素
*对于合法的删除位置具体分析(如要删除的元素位于表头)
*如果要删除的元素为于表头,令 s 指向该结点,head = s->Next;
*如果要删除的元素不位于表头,p = FindKth(i-1); 
如果 p 等于 NULL,
如果 p 不等于 NULL,p->Next = s->Next
*最后,要注意,释放s所指向结点的空间。
*/
List Delete(int i, List PtrL){
List p, s;
if(1 == i){/*若要删除的是表的第一个节点*/
s = PtrL;/*s指向第1个结点*/
if(PtrL != NULL){
PtrL = PtrL->Next;  /*从链表中删除*/
}
else{
return NULL;
}
free(s);/*释放被删除结点*/
return PtrL;
}
p = FindKth(i-1, PtrL);/*查找第i-1个结点*/
if(p == NULL){
printf("第%d个结点不存在", i-1);
return NULL;
}
else if(p->Next == NULL){/*如果要删除位置上没有结点*/
printf("第%d个结点不存在", i);
return NULL;
}
else{
s = p->Next;/*s指向第i个结点*/
p->Next = s->Next;/*从链表中删除*/
free(s);/*释放被删除结点*/
return PtrL;
}
}

(1)先找到链表的第i-1个结点,用p指向;
(2)再用指针s指向要被删除的结点(p的下一个结点);
(3)然后修改指针,删除s所指结点;
注意:(4)最后释放所指结点的空间

2.1.3广义表

》广义表是线性表的推广
》对于线性而言,n个元素都是基本的单元素
》广义表中,这些元素不仅可以是单元素也可以是另一个广义表

typedef struct GNode *GList;
struct GNode{
int Tag;/*标志域:0表示结点是单元素,1表示结点是广义表*/
union{/*子表指针域Sublist与单元素数据域Data复用,即共用存储空间*/
ElementType Data;
GList SubList;
}URegion;
GList Next;/*指向后继结点*/
}

在这里插入图片描述

2.1.4多重链表

多重链表:链表中的结点可能同时隶属于多个链
》多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域
》但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表

·多重链表有广泛的用途
基本上如树、图这样相对复杂的结构都可以采用多重链表方式实现存储。

列:矩阵可以用二维数组表示,但二维数组表示又两个缺陷
》一是数组的大小需要事先确定
》对于“稀疏矩阵”,将造成大量的存储空间浪费

分析:采用一种典型的多重链表——十字链表来存储稀疏矩阵
·只存储矩阵非0元素项
结点的数据域:行坐标Row、列坐标Col、数值Value
·每个结点通过两个指针域,把同行、同列串起来:
》行指针(或称为向右指针)Right
》列指针(或称为向下指针)Down
在这里插入图片描述
左上Term是整个十字链表的入口结点
表示矩阵 4行5列共7个非零元素

在这里插入图片描述

2.2堆栈

计算机如何进行表达式求值?
算术表达式 5 + 6 / 2 - 3 * 4,正确理解
5 + 6 / 2 - 3 * 4 = 5 + 3 - 3 * 4 = 8 - 3 * 4 = 8 - 12 = 4

》由两类对象构成的:
》》运算数,如2、3、4
》》运算符号,如+、-、*、/

》不同运算符号优先级不一样

后缀表达式
》中缀表达式:运算符号位于两个运算数之间。如 a + b * c - d / e
》后缀表达式:运算符号位于两个运算数之后。如 a b c * + d e / -

后缀表达式求值策略:
从左向右“扫描”,逐个处理运算数和运算符号
1.遇到运算数怎么办?如何“记住”目前还未参与运算的数?
2.遇到运算符号怎么办?对应的运算数是什么?

启示:需要有种存储方法,能顺序存储运算数,并在需要时“倒序”输出

例子 6 2 / 3 - 4 2 * + = ?
在这里插入图片描述

堆栈的抽象数据类型描述

堆栈(Stack):具有一定操作的线性约束的线性表
》只在一端(栈顶,Top)做插入、删除

插入数据:入栈(Push)
删除数据:出栈(Pop)
后入先出:Last In First Out(LIFO)

类型名称:堆栈
数据对象集:一个有0个或多个元素的有穷线性表
操作集:长度为MaxSize的堆栈 S ∈ S t a c k S \in Stack SStack,堆栈元素 i t e m ∈ E l e m e n t T y p e item \in ElementType itemElementType

1.Stack CreateStack(int MaxSize);生成空堆栈,其最大长度为MaxSize
2.int IsFull(Stack S, int MaxSize);判断堆栈S是否已满
3.void Push(Stack S, ElementType item);将元素item压入堆栈
4.int IsEmpty(Stack S);判断堆栈S是否为空
5.ElementType Pop(Stack S);删除并返回栈顶元素

栈的顺序存储实现

栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成。

//栈的顺序存储实现
//一个一维数组,一个记录栈顶元素位置的变量
#define MaxSize <存储数据元素的最大个数>
typedef struct SNode* Stack;//SNode* 相当于 Stack
struct SNode{
ElementType Data[MaxSize];
int Top;
}
(1)入栈
//顺序存储的栈 入栈操作
//入栈后,一维数组中多一个元素,栈顶元素位置加1
//入栈之前要判断栈是否满
void Push(Stack PtrS, ElementType item){
if(PtrS->Top == MaxSize-1){
printf("堆栈满");
return
}
else{
PtrS->Data[++(PtrS->Top)] = item;
//相当于  (PtrS->Top)++;  PtrS->Data[PtrS->Top];
}
}
(2)出栈
//顺序存储的栈 出栈操作
//入栈后,一维数组中少一个元素,栈顶元素位置减1
//首先判断操作对象的状态是否支持操作,即栈是否为空
//然后判断操作行为本身是否合法,如位置是否越界等
ElementType Pop(Stack PtrS){
if(-1 == PtrS->Top){
printf("堆栈空");
return ERROR; //ERROR是ElementType的特殊值,标志错误
}
else{
//ElementType e = PtrS->Data[PtrS->Top];
//PtrS->Top--;
//return e;
//上述三行代码可替换为:
return PtrS->Data[(PtrS->Top)--];
}
}
例:用一个数组实现两个堆栈,最大利用数组空间

我的想法:随着元素进栈,两个堆栈的栈顶逐渐向中间移动,若两个栈顶相隔为1,则栈满

分析:使这两个栈分别从数组的两头开始向中间生长:当两个栈的栈顶指针相遇是,表示两个栈都满了

//首先创建两个堆栈
//其中一个的StrP->Top = -1, 另一个的StrP->Top = MaxSize;
//其中一个进栈StrP->Top++,另一个进栈StrP->Top--;
//出栈相反
//栈顶指针相遇的标志是,StrP1-StrP2=1;
#define MaxSize <存储数据元素的最大个数>
struct DStack{
ElementType Data[MaxSize];
int Top1;
int Top2;
}S;
S.Top1 = -1;
S.Top2 = MaxSize;

void Push(struct DStack* PtrS, ElementType item, int Tag){
/*Tag作为区分两个堆栈的标志,取值为1和2*/
if(PtrS->Top2 - PtrS->Top1){
printf("堆栈满");
}
if(Tag == 1){//对第一个堆栈操作
PtrS->Data[++(PtrS->Top1)] = item;
}
else{//对第二个堆栈操作
PtrS->Data[--(PtrS->Top2)] = item;
}
}

ElementType Pop(struct DStack* PtrS, int Tag){
//Tag作为区分两个堆栈的标致,取值为1和2
if(Tag == 1){//对第一个堆栈操作
if(-1 == PtrS->Top1){//堆栈1空
printf("堆栈1空");
return NULL;
}
else{
return PtrS->Data[(PtrS->Top1)--];
}
}
else{
if(MaxSize == PtrS->Top2){//堆栈2空
printf("堆栈2空");
return NULL;
}
else{
return PtrS->Data[(PtrS->Top2)++];
}
}
}

堆栈的链式存储实现

栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。栈顶指针Top应该在链表的哪一头?

链尾不能作为头,如果用链尾作为头,进行Pop操作时,无法找到上一个元素(在单链表中)

typedef struct SNode* Stack;
struct SNode{
ElementType Data;
struct SNode* Next;
};

Stack CreateStack(){
//构建一个堆栈的头结点,返回指针
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}

int IsEmpty(Stack S){
//判断堆栈S是否为空,若为空返回整数1,否者返回0
return (S->Next == NULL);
}

void Push(ElementType item, Stack S){
//将元素item压入堆栈S
//链栈无需判断是否满
//首先创建一个临时结点
//将要进栈的值存入这个临时结点
//先让这个结点的Next指针指向链栈头结点的Next指针指向的结点
//然后让头结点的Next指针指向这个临时结点
struct SNode* TmpCell;
TmpCell = (struct SNode*)mallco(sizeof(struct SNode));
TmpCell->Element = item;
TmpCell->Next = S->Next;
S=>Next = TmpCell
}

Element Pop(Stack S){
//Pop时,链栈也要判断是否为空
//先创建一个存放栈顶元素的变量,一个临时结点,将栈顶结点赋值给该临时结点,
即 FirstCell = S->Next;
//然后让 头结点 指向栈顶元素的下一个元素, 
S->Next = S->Next->Next;//即 S->Next = FirstCell->Next;
//接着将临时结点存放的变量值赋值给准备好的变量
TopElem = FirstCell->Element;
//然后释放临时结点的指针
//最后返回变量值
struct SNode* FirstCell;
ElementType TopElem;
if(IsEmpty(S)){
print("堆栈空");
return NULL;
}
else{
FirstCell = S->Next;
S->Next = FirstCell->Next;
TopElem = FirstCell->Element;
free(FirstCell);
return TopElem;
}
}

堆栈应用:表达式求值

中缀表达式求值
基本策略:将中缀表达式转换为后缀表达式,然后求值
如何将中缀表达式转换为后缀?
观察:2 + 9 / 3 - 5 —> 2 9 3 / + 5 -
1.运算数相对顺序不变
2.运算符号顺序发生改变
》》需要存储“等待中”的运算符号
》》要将当前运算符号与“等待中”的最后一个运算符比较

例:a * ( b + c) / d = ?
后缀表达式: a b c + * d /
注意:运算符号在括号里和括号外的优先级可能不一样

遇到 ),不将其存入堆栈,
而是将堆栈顶的运算符号一个个抛出,直到遇到(

中缀表达式如何转换为后缀表达式
》从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理
(1)运算数:直接输出
(2)左括号:压入堆栈
(3)右括号:将栈顶的运算符弹出并输出,知道遇到右括号(出栈,不输出)
(4)运算符
·若优先级大于栈顶运算符时,则把它压栈;
·若优先级熊宇栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,知道该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
·若各对象处理完毕,则把堆栈中存留的运算符一并输出。

堆栈的其他应用:

》函数调用及递归实现
》深度优先搜索
》回溯算法
》。。。

2.3队列及实现

什么是队列
队列(Quuue):具有一定操作约束的线性表
》插入和删除操作:只能在一端插入,而在另一端删除

》数据插入:入队列(AddQ)
》数据删除:出队列(DeleteQ)
》先来先服务
》先进先出:FIFO

队列的抽象数据类型描述

类型名称:队列(Queue)
数据对象集:一个有0个或多个元素的有穷线性表
操作集:长度为MaxSize的队列 Q ∈ Q u e u e Q \in Queue QQueue,队列元素 i t e m ∈ E l e m e t T y p e item \in ElemetType itemElemetType
1.Queue CreatQueue(int MaxSize);生成长度为MaxSize的空队列
2.int IsFullQ(Queue Q, int MaxSize);判断队列Q是否已满;
3.void AddQ(Queue Q, ElementType item);将数据item插入队列Q中
4.int IsEmptyQ(Queue Q);判断队列Q是否为空
5.ElementType DeleteQ(Queue Q);将队头数据元素从队列中删除并返回

先来的元素在队列头,后来的元素在队列尾

队列的顺序存储实现

队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量front以及一个记录队列为元素的变量rear组成。

//顺序存储的队列包含
//一个数组
//一个队列头指针
//一个队列尾指针
#define MaxSize <存储数据元素的最大个数>
struct QNode{
ElementType Data[MaxSize];
int front;
int rear;
};  //注意:结构体中括号后有分号 ";"
typedef QNode* Queue
循环队列

队列空:front=rear

队列空满无法区分

解决方案:
(1)使用额外标记:Size或者tag域
(2)仅仅使用n-1个数组空间

(1)入队列(循环队列)

//首先判断队列是否满 队列满的条件是(rear + 1) % MaxSize = front
//如果不满
//先将rear后移,再添加元素

void AddQ(Queue PtrQ, ElementType item){
if((PtrQ->rear+1)%MaxSize == PtrQ->front){
printf("队列满");
return;
}
else{
PtrQ->rear = (PtrQ->rear+1)%MaxSize;
PtrQ->Data[PtrQ->rear] = item;
}
}

(2)出队列(循环队列)

//首先判断队列是否为空
//如果不空
//先front后移,再返回元素值(不是链表,不用担心后移后元素消失)

ElementType DeleteQ(Queue PtrQ){
if(PtrQ->front == PtrQ->rear){
printf("队列空");
return ERROR;
}
else{
PtrQ->front = (PtrQ->front+1)%MaxSize;
return PtrQ->Data[PtrQ->front];
}
}

队列的链式存储实现

队列的链式存储结构也可以用一个单链表实现,插入和删除操作分别在链表的两头进行:队列指针front和rear应该分别指向链表的哪一头?
front 做删除操作
rear 做插入操作
front 做链表头

//链式存储队列有:

//队列结点,包含:
//1.元素值
//2.Next指针
//队列结构,包含
//1.队列头指针
//2.队列尾指针
struct Node{
ElementType Data;
struct Node* Next;
};
struct QNode{/*链队列结构*/
struct Node* rear;/*指向队尾结点*/
struct Node* front;/*指向队头结点*/
};
typedef QNode* Queue;
Queue PtrQ;
不带头结点的链式队列出队操作的一个示例:
//首先判断队列是否为空
//如果不为空
//先创建一个临时指针(队列结点类型)
//使得这个临时结点指针指向要出队列的结点
//将要出队列结点的值(由于有两个指针指向该结点,所以有两种表示方法)赋值给同类型变量
/*
     然后使队列长度减1
     由于不带头结点,分两种情况
     1.队列中仅有一个结点
     2.队列中多余一个结点
*/
//然后释放临时指针
//最后返回出队列的元素值
ElementType DeleteQ(Queue PtrQ){
struct Node* FrontCell = (Node*)malloc(sizeof(Node));
ElementType FrontElem;

if(PtrQ->front == NULL){
printf("队列空");
return ERROR;
}
else{
FrontCell = PtrQ->front;
if(PtrQ->rear==PtrQ->front){//若队列中只有一个结点
PtrQ->front = PtrQ->rear = NULL;
}
else{
PtrQ->front=PtrQ->front->Next;
}
FrontElem = FrontCell->Data;
free(FrontElem);
return FrontElem;
}
}

2.4多项式加法运算

在这里插入图片描述

采用不带头结点的单向链表

按照指数递减的顺序排列各项
在这里插入图片描述

//首先定义单链表结点
//包括,一个记录系数的整型变量,一个记录指数的整型变量,一个指向下一结点的指针
struct PolyNode{
int coef;//系数
int expon;//指数
struct PolyNode* link;//指向下一个结点的指针
};
typedef struct PolyNode* Polynomial;

Polynomial P1, P2;


//多项式加法运算
//算法思路:两个指针P1和P2分别指向这两个多项式的第一个节点,不断循环
//P1->expon==P2->expon;系数相加,若结果不为0,则作为结果多项式对应项的系数,
//同时,P1和P2分别指向下一项
//P1->expon>P2->expon;将当前项存入结果多项式,并使P1指向下一项
//P1->expon<P2->expon;将当前结果存入多项式,并使P2指向下一项

//当某一多项式处理完时,将另一个多项式的所有节点依次复制到结果多项式中去

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 PolyAdd(Polynomial P1, Polymomial P2){
Polynomial front, rear, temp;
int sum;
rear = (Polynomial)malloc(sizeof(struct PolyNode));//rear记录多项式表尾结点
// int* a = (int*)malloc(sizeof(int));
front = rear;//front记录结果多项式表头结点
while(P1&&P2){//当两个多项式都有非零项
switch(Compare(P1->expon, P2->expon)){
case 1:
       Attach(P1->coef, P2->expon, &rear);
       P1 = P1->link;
       break;

case -1:
      Attach(P2->coef, P2->expon, &rear);
      P2 = P2->link;
      break;

case 0:
      sum = P1->coef + P2->coef;
      if(sum)
      Attach(sum, P1->expon, &rear);
      P1 = P1->link;
      P2 = P2->link;
      break;
}//P1或P2有一个为NULL后退出循环
/*将未处理完的另一个多项式的所有节点依次复制到结果多项式中去*/
for(;P1;P1 = P1->link){
Attach(P1->coef, P1->expon, &rear);
}
for(;P2;P2 = P2->link){
Attach(P2->coef, P2->expon, &rear);
}
rear->link = NULL;
temp = front;
front = front->link//令front指向结果多项式第一个非零项
free(temp);//释放临时空表头结点

return front;
}

多项式乘法与加法运算

在这里插入图片描述

//求解思路:
//1.多项式表示
//仅表示非零项
//数组:——编程简单、调试容易 ——需事先确定数组大小
//链表:——动态性强 ——编程略为复杂、调试比较困难
//数据结构设计
typedef struct PolyNode* Polynomial;
struct PolyNode{
int coef;
int expon;
Polynomial link;
}
//2.程序框架
int main(){
//读入多项式1
//读入多项式2
//乘法运算并输出
//加法运算并输出

return 0;
}
//需要设计的函数:
//》读一个多项式
//》两多项式相乘
//》两多项式相加
//》多项式输出
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;
}
//3.读多项式
Polynomial ReadPoly(){
...
scanf("%d", &N);
...
while(N--){
scanf("%d%d", &c, &e);
Attach(c, e, &Rear);
}
...
retunr P;
}
//Rear初值是多少?
//》两种处理方法:
//》1.Rear初值为NULL
//》》在Attach函数中根据Rear是否为NULL做不同处理
//》2.Rear指向一个空结点
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;//此时P已经不向空结点,但t仍然指向空结点
free(t);//删除临时生成的头结点

return P;
}
//4.加法实现
Polynomial Add(Polynomial P1, Polynomial P2){
...
t1 = P1;
t2 = P2;
P = (Polynomial)malloc(sizeof(struct PolyNode));
P->link = NULL;
Rear = P;
while(t1 && t2){
if(t1->expon == t2 ->expon){
...
}
else if(t1->expon > t2->expon){
...
}
else{
...
}
}
while(t1){
...
}
while(t2){
...
}

return P;
}
//5.乘法实现
//方法
//》1.将乘法运算转换为加法运算
//将当前项 (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当前项 (c1i, e1i) 乘 P2当前项 (c2i, e2i),并插入到结果多项式中,
//关键是要找到插入位置
//初始结果多项式可由P1第一项乘P2获得
Polynomial Mult(Polynomial P1, Polynomial P2){
...
t1 = P1;
t2 = P2;
...
while(t2){//先用P1的第一项乘以P2,得到P
...
}
t1 = t1->link;
while(t1){
t2 = P2;
Rear = P;
while(t2){
e = t1->expon + t2->expon;
c = t1->coef * t2->coef;
...
t2 = t2->link;
}
t1 = t1->link;
}
...
}
//先用P1的第一项乘以P2,得到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){
...
t2 = t2->link;
}
t1 = t1->link;
}
...
}

Polynomial Mult(Polynomial P1, Polynimial P2){
...
while(1){
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)){
...
}
else{
...
}
t2 = t2->linkk;
}
t1 = t1->link;
}

Polynomial Mult(Polynomial P1, Polynomial P2){
...
while(Rear->link && (Rear->link->expon > e)){
Rear = Rear->link;
}
if(Rear->link && (Rear->link->expon ==e)){
if(Rear->link->coef + c){//判断加了之后是否等于0
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;
}
...
}

Polynomial Mult(Polynomial P1, Polynomial P2){
...
t1 = P1;
t2 = P2;
...
while(t2){//先用P1的第1项乘以P2,得到P
...
}
t1 = t1->link;
while(t1){
t2 = P2;
Rear = P;
while(t2){
e = t1->expon + t2->expon;
c = t1->coef * t2->coef;
...
t2 = t2->link;
}
t1 = t1->link;
}

t2 = P;
P = P->link;
free(t2);

return P;
}
//6.多项式输出
void PrintPoly(Polynomial P){
//输出多项式

int flag = 0;//辅助调整输出格式用

if(IP){
printf("0 0\n");
return;
}
while(P){
if(!flag){//如果是第一项
flag = 1;
}
else{
printf(" ");
}

printf("%d %d", P->coef, P->expon);
P = P->link;
}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值