2.线性结构——数据结构慕课笔记

线性结构__顺序及链式存储——慕课笔记


代码来源:https://www.icourse163.org/course/ZJU-93001#/info

2.1线性表及其实现

线性表:由同类型数据元素构成有序序列的线性结构

  • 长度:表中元素的个数(表中没有元素——空表)

  • 表头:表起始位置;表尾:表结束位置

  • 抽象数据类型表示(ADT)

    • 数据对象集
    • 操作集

2.1.1多项式表示

多项式的关键数据:多项式系数n;各项系数ai及指数i

  • 顺序存储结构直接表示(一维数组)

    • 数组各分量对应多项式各项(下标——指数;数组元素——系数

在这里插入图片描述

  • 顺序存储结构表示非零项(结构数组)
  • 链表结构存储非零项

2.1.2线性表的顺序存储实现

利用数组连续存储空间顺序存放线性表的各元素

  • 数据对象集的定义

    • 访问下标为i的元素:Ptrl->Data[i]或L.Data[i]
    • 线性表的长度:L.Last**+1** 或 PtrL->Last**+1**
      在这里插入图片描述

    注释不是真正意义的表,一个结构里就已经包含了一个数组,而一个数组包含了全部存储的元素

typedef struct LNode *List;
struct LNode{
    
    
	ElementType Data[MAXSIZE];
	int last;//表尾 从0开始
}struct LNode L;
List Ptrl
  • 初始化
List MakeEmpty( )
{
    
     	List PtrL;
	PtrL = (List )malloc( sizeof(struct LNode) );
	PtrL->Last = -1;// -1代表此时链表没有元素
	return PtrL;//返回指向表头的指针
}
  • 查找元素(返回存储位置:下标
int Find( ElementType 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; /* 找到后返回的是存储位置 */
}
  • 插入元素(先移动后插入
    • 移动情况:倒叙向后移动
    • 边界条件
      • 插入位置不合法(i < 1或 i > Last+2)
      • 线性表位置已满(Last == MAXSIZE -1)
void Insert( ElementType X, int i, List PtrL ){
    
    
    if (Ptrl->Last == MAXSIZE-1){
    
    
        printf("表满");
        return;// void 没有返回值
    }
    if (i < 1 || i > Ptrl->Last+2){
    
    
        printf("位置不合法");
        return;
    }
    //移动元素 倒叙向后移动
	for (int j = Ptrl->Last; j >= i-1 ; --j)//第i个位置对应下标为i-1
        Ptrl->Data[j+1] = Ptrl->Data[j];
    Ptrl->Data[i-1] = X;//插入新元素
    ++Ptrl->Last;//表长加1
    return;
}
  • 删除元素(直接移动即可)

    • 移动情况:顺序向前移动
    • 边界条件:删除位置不合法(i < 1或 i > Last+1)
    void Delete( int i, List PtrL ){
          
          
        if (i < 1 || i > Ptrl->Last+1){
          
          
            printf("不存在第%d个元素",i);
            return;
        }
        //移动元素 顺序向前移动
    	for (int j = i-1; j <= Ptrl->Last ; ++j)//第i个位置对应下标为i-1
            Ptrl->Data[j] = Ptrl->Data[j+1];
        --Ptrl->Last;//表长加1
        return;
    }
    

2.1.3线性表的链式存储实现

逻辑上相邻的两个元素物理上不要求相邻;通过"链"建立起数据元素之间的逻辑关系

  • 插入、删除元素不需要挪动元素,只需要修改链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtZn4Gok-1586684135569)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200404171541040.png)]

  • 数据对象集的定义
typedef struct LNode *List;
struct LNode{
    
    
	ElementType Data;
	List Next;//指向下一个结点的指针
};
struct Lnode L;
List PtrL;
  • 求表长
int Length (const List PtrL )//指向表头的指针
{
    
    	List p = PtrL; /* p指向表的第一个结点*/
	int j = 0;
	while ( p ) {
    
    //p≠NULL,则链表还未到结尾
		p = p->Next;
		j++; /* 当前p指向的是第 j 个结点*/
	}
	return j;
}
  • 查找

    • 按序号查找:FindKth(查找第K序号的元素)
      • 成功:返回指向该节点的指针
      • 失败:返回空指针
        • 失败一:空表
      • 失败二:序号超出链表的最大范围
    • 非法输入:查找位置不合法(i < 1或 i > Last+1)
    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;
    	/* 否则返回空 */
    }
    
    • 按值查找: Find
    • 成功:返回指向该节点的指针
      • 失败:返回空指针
        • 失败一:空表
        • 失败二:找到最后一个结点还没找到该值
List Find( ElementType X, List PtrL )
  {
    
    
  	List p = PtrL;
  	while ( p!=NULL && p->Data != X )
  		p = p->Next;
  	return p;
  }
  • 插入

    • 先构造一个新结点,用s指向
    • 找到链表的第 i-1个结点,用p指向
    • 然后修改指针,插入结点 ( p之后插入新结点是 s)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SdDeKzx1-1586684135570)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200404181455758.png)]

    • 边界条件新结点插入表头(不注意的话会导致调用FindKth函数出错)
List Insert( ElementType X, int i, List PtrL )
{
    
     
    List p,s;
    if (i == 1){
    
     //boundary condition 新结点插入表头
        s = (ist)malloc(sizeof(struct Node));//申请新结点
        s->Data = X;//填装新结点
        s->Next = Ptrl;
        return s;
    }
    p = FindKth( i-1, List PtrL );//找到第i-1个结点
   if (p == NULL){
    
    //找不到第i-1个结点
        printf("参数i错");
        return NULL;//返回空指针
    }else{
    
    
        s = (ist)malloc(sizeof(struct Node));//申请新结点
        s->Data = X;//填装新结点
        s->Next = p->Next;
   	 	p->next = s;
        return Ptrl;//返回指向表头的指针
    }
}

  • 删除 (第i个结点)
  • 边界条件(Attention)
    • 删除结点为第一个结点
    • 删除结点为末尾结点下一个结点
  • 步骤
    • ​ 先找到链表的第i-1个结点,用p指向
    • 再用s指针要被删除的结点(p的下一个结点)
    • 修改p的指向(p->Next = s->Next)
    • 释放s所指向结点的空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBW0cOz3-1586684135572)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200404203600394.png)]

List Delete( int i, List PtrL )
{
    
     	
    List p, s;
	if ( i == 1 ) {
    
     /* 若要删除的是表的第一个结点 */
		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;
	}
}

2.2堆栈

2.2.1什么是堆栈(stack)

堆栈:具有一定操作约束的线性表

  • 操作约束:只在一端(Top,栈顶)做插入、删除
    • 插入数据:PUSH
    • 删除数据:POP
  • 遵循原则:LIFO(Last In First Out)
  • 堆栈的抽象数据类型描述
    • 数据对象集:一个有0个或多个元素的有穷线性表
    • 操作集:长度为MaxSize的堆栈S∈Stack,堆栈元素item∈ElementType

2.2.2堆栈的顺序存储实现

由一个一维数组和一个记录栈顶元素位置(下标)的变量组成

  • 2.2.2.1堆栈的顺序存储的抽象数据类型描述
#define MaxSize <储存数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
    
    
    ElementType Data[Maxsize];
    int top;//记录栈顶元素的位置
};
  • 2.2.2.2堆栈的顺序存储的相关操作
  • 入栈(指向栈顶的下标先加一,然后再插入元素)

void Push( Stack PtrS, ElementType item )
{
    
    
	if ( PtrS->Top == MaxSize-1 ) {
    
    
		printf(“堆栈满”); return;
	}else {
    
    
		PtrS->Data[++(PtrS->Top)] = item;//指向栈顶的下标先加一,后插入元素
		return;
	}
}
	-  出栈(先弹出元素,然后指向栈顶的下标再减一)
ElementType Pop( Stack PtrS )
{
    
    
	if ( PtrS->Top == -1 ) {
    
    
		printf(“堆栈空”);
        return;
	}else 	
        return PtrS->Data[(PtrS->Top)--] = item;
}

2.2.3堆栈的链式存储实现

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

注:对于单向链表:只能在表头一端进行插入和删除的操作。表尾不可(删除操作后不能找到前一个结点

  • 2.2.3.1数据对象集的定义
typedef struct SNode *Stack;
struct SNode{
    
    
	ElementType Data;
	struct SNode *Next;//指向下一结点
};
  • 2.2.3.2堆栈的链式存储的相关操作

  • 初始化栈堆(头结点不放元素——只起链接作用)

    • 堆栈初始化(建立空栈)
    • 判断堆栈s是否为空
    Stack CreateStack()
    {
          
           /* 构建一个堆栈的头结点,返回指针 */
    	Stack S;
    	S =(Stack)malloc(sizeof(struct SNode));
    	S->Next = NULL;
    	return S;
    }
    bool IsEmpty(Stack S)
    {
          
           /*判断堆栈S是否为空,若为空函数返回true,否则返回false */
    	return ( S->Next == NULL );
    }
    
  • 入栈(链表可无限延伸,不用判断栈堆是否满)

void Push( ElementType item, Stack S){
    
    
    struct Stack TmpCell;
    TmpCell = (struct SNode *)malloc(sizeof(struct SNode));
    TmpCell->Element = item;//填充新结点
    TmpCell->Next = S->Next;
    S->Next =  TmpCell;
}
  • 出栈
ElementType Pop(Stack S){
    
    
    struct Stack FirstCell;
    ElementType TopElem;
    if ( S ){
    
    
        printf("堆栈空");return ERROR;
    }else{
    
    
   		FirstCell = S->Next;//指向栈顶元素
   		S->Next =  FirstCell->Next;
    	TopElem = FirstCell->Element;//弹出元素
    	free(FirstCell);
   		return TopElem;        
    }
};

2.2.4堆栈应用

  • 请用一个数组实现两个堆栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功。

    • 分析

      • 两个堆栈分别从数组头和尾插入元素,腾出中部未使用的空间用于入栈元素
      • 如何判别堆栈满:当两个栈的栈顶指针相遇(PtrS->Top2 – PtrS->Top1 == 1)时,表示两个栈都满
    • 具体实现

      • 定义
      #define MaxSize <存储数据元素的最大个数>
      struct DStack {
              
              
      	ElementType Data[MaxSize];
      	int Top1; /* 堆栈1的栈顶指针 */
      	int Top2; /* 堆栈2的栈顶指针 */
      }S;
      //栈堆1,2空
      //S.Top1 = -1;
      //S.Top2 = MaxSize;
      
      • 入栈
      void Push( struct DStack *PtrS, ElementType item, int Tag ){
              
              
          /* Tag作为区分两个堆栈的标志,取值为1和2 */
          if (PtrS->Top2 – PtrS->Top1 == 1){
              
              
              printf("栈堆满");
              return;
          }
          if (Tag == 1)//对第一个栈堆操作
              PtrS->Data[++(PtrS->Top1)] = item;        
          else //对第二个栈堆操作
              PtrS->Data[--(PtrS->Top2)] = item;
      }
      
      • 出栈(根据Tag讨论不同栈堆是否堆栈空)
      ElementType Pop( struct DStack *PtrS, int Tag ){
              
              
          /* Tag作为区分两个堆栈的标志,取值为1和2 */
          if (Tag == 1){
              
              
              if (Ptrs->Top1 == -1){
              
              
                  printf("栈堆1为空");return ERROR;//ERROR 预先定义的错误返回信息
              }
              else 
                 return PtrS->Data[(PtrS->Top1)--];  
          }else{
              
              
              if (Ptrs->Top2 == -1){
              
              
                  printf("栈堆2为空");return ERROR;
              }else 
                 return PtrS->Data[(PtrS->Top2)++];  
          }
      }
      
  • 求表达式的值(后缀表达式求值)

从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。

  • 后缀表达式:运算符号位于两个运算数之后
  • 好处:利用堆栈即可求解表达式的值,不用判别左右运算符的优先级(不用判别到底先算谁,要不要等),直接见到运算符号就取运算数就行
  • 运算数:直接输出
  • 左括号:压入堆栈
  • 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出
  • 运算符:
    • 若优先级大于栈顶运算符时,则把它压栈
    • 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
  • 若各对象处理完毕,则把堆栈中存留的运算符一并输出。

2.3队列

具有一定操作约束的线性表

  • 插入和删除操作:只能在一端插入,而在另一端删除
  • 遵循原则:FIFO(First In First Out)
2.3.1队列的顺序存储实现

由一个一维数组一个记录队列头元素位置的变量front以及一个记录队列尾元素位置的变量rear组成。

  • 数据对象集的定义
    • 队列头指向头一个元素的前一个元素
    • 队列尾指向最后一个元素
#define MaxSize <储存数据元素的最大个数>
typedef struct QNode *Queue;
struct QNode {
    
    
	ElementType Data[ MaxSize ];
	int rear;//队列头元素位置
	int front;//队列尾元素位置
};
  • 数据对象集的相关操作

  • 入队列

    • 易错:PtrQ->Data[rear++] = item;
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;
    }
}
  • 出队列
ElementType DeleteQ ( Queue PtrQ ){
    
    
    if ( PtrQ->front == PtrQ->rear ) {
    
    
		printf(“队列空”);
		return ERROR;
	} else {
    
    
		PtrQ->front = (PtrQ->front+1)% MaxSize;//如果front=5,通过该变化使其又变成0
		return PtrQ->Data[PtrQ->front];
	}
}

注意:

  • 如何识别队列空OR满?

    • 法一:队列不放满元素(只放n-1个元素留一个空的位置),利用front和rear的相对距离来判别
    • 法二:另外增设一个变量。如记录当前队列数量的Size;或记录最后一次操作是删除OR插入的Flag
  • 如何实现循环队列?

    利用加1取余表达式——>Front和rear的数值范围一直都在[0,n-1]

  • front与rear指向的位置?

    • front:指向队列首元素的前一个位置(假设a[0]有元素,则front = -1)
    • rear:指向队列尾元素(尾元素a[2],则rear=2)
2.3.2队列的链式存储实现

以用一个单链表实现。插入和删除操作分别在链表的两头进行

  • 链头:队列指针front(删除元素方便,很容易找到下一个元素)
  • 链尾:队列指针rear(插入元素方便,删除不可行,不能找到上一个元素)
  • 数据对象集的定义
    • 队列头指向头一个元素
    • 队列尾指向最后一个元素
struct Node{
    
    //存放元素的结点
	ElementType Data;
	struct Node *Next;
};
struct QNode{
    
     /* 链队列结构 */
	struct Node *rear; /* 指向队尾结点 */
	struct Node *front; /* 指向队头结点 */
};
typedef struct QNode *Queue;
Queue PtrQ;
  • 数据对象集的相关操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIj3R1HB-1586684135574)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200407160952881.png)]

  • 入队列
void AddQ( Queue PtrQ, ElementType item){
    
    
{
    
     	struct Node *TmpCell;
	TmpCell = (struct Node*)malloc(sizeof(struct Node));
 	TmpCell->Data = item;//填充新结点
	if ( PtrQ->front == NULL) {
    
    //若队列为空
		PtrQ->front = PtrQ->rear = Tmpcell;
	}else
		PtrQ->rear = Tmpcell;;//指向队列尾的下一个元素
 	return;
}
  • 出队列(注意只有一个元素的临界情况
ElementType DeleteQ ( Queue PtrQ )
{
    
     	struct Node *FrontCell;
	ElementType FrontElem;
	if ( PtrQ->front == NULL) {
    
    
		printf(“队列空”); return ERROR;
	}
	FrontCell = PtrQ->front;
	if ( PtrQ->front == PtrQ->rear) /* 若队列只有一个元素 */
		PtrQ->front = PtrQ->rear = NULL; /* 删除后队列置为空 */
	else
		PtrQ->front = PtrQ->front->Next;//指向队列头的下一个元素
	FrontElem = FrontCell->Data;
	free( FrontCell ); /* 释放被删除结点空间 */
	return FrontElem;
}

2.4应用实例

2.4.1多项式加法运算

主要思路:相同指数项系数相加,其余部分进行拷贝

  • 储存方法:采用不带头结点单向链表,按照指数递减的顺序排列各项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpDf24S3-1586684135576)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200408221452287.png)]

struct PolyNode {
    
    
	int coef; // 系数
	int expon; // 指数
	struct PolyNode *link; // 指向下一个节点的指针
};
typedef struct PolyNode *Polynomial;
Polynomial P1, P2;
  • 实现算法
  • 两个指针P1和P2分别指向这两个多项式第一个结点,不断循环
  • 循环判定条件同指数则合并系数;不同指数取指数大的项存入结果多项式(同时指针向后移一项
  • 当某一多项式处理完时,将另一个多项式的所有结点依次复制到结果多项式中去
Polynomial PolyAdd (Polynomial P1, Polynomial P2){
    
    
    Polynomial front, rear, temp;
	int sum;
    //建立临时空表头结点————>方便插入结点
	rear = (Polynomial) malloc(sizeof(struct PolyNode));
	front = rear; /* 由front 记录结果多项式链表头结点 */
	while ( P1 && P2 ) /* 当两个多项式都有非零项待处理时 */
		switch ( Compare(P1->expon, P2->expon) ) {
    
    
		case 1:
			Attach( P1->coef, P1->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;
		}
/* 将未处理完的另一个多项式的所有节点依次复制到结果多项式中去 */
	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;
}
  • 大小比较函数
int Compare(const int expon1,const int expon2){
    
    
    if(expon1 > expon2)
        return 1;
    else if (expon1 < expon2)
        return -1;
    else (expon1 == expon2)
        return 0;
}
  • 结点接到尾巴的函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ainsskvo-1586684135578)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200410201539396.png)]

void Attach( int c, int e, Polynomial *pRear )//结点指针的地址————>为了修改其值
{
    
     	/* 由于在本函数中需要改变当前结果表达式尾项指针的值, */
	/* 所以函数传递进来的是结点指针的地址,*pRear指向尾项*/
	Polynomial P;
	P =(Polynomial)malloc(sizeof(struct PolyNode)); /* 申请新结点 */
	P->coef = c; /* 对新结点赋值 */
	P->expon = e;
	P->link=NULL;
	/* 将P指向的新结点插入到当前结果表达式尾项的后面 */
	(*pRear)->link = P;
	*pRear = P; /* 修改pRear值 */
}

2.5小白专场

一元多项式的加法乘法运算

2.5.1.1多项式表示(仅表示非零项)

  • 链表:动态性强;但编程略为复杂、调试比较困难
  • 数组:编程简单,调试容易;但需要实现确定数组的大小
//链表的表示
//每个结点存放每项的指数以及系数
typedef struct PolyNode *Polynomial;
struct PolyNode{
    
    
    int coef;//系数
    int expon;//指数
    Polynomial link;//指向下一点结点的指针
};

2.5.1.2程序框架

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.5.1.3读多项式

  • Q: Rear 初值是多少?

  • 思路1:Rear初始值为NULL,在Attach函数中根据Rear是否为NULL做不同处理(建立空链表——没有任何结点

  • 思路2:Rear指向一个空结点,Attach函数实现较为简单,不用判断Rear的值(建立带有一个空结点的链表)

Polynomial ReadPoly()
{
    
    
    Polynomial P, Rear, t;
    scanf("%d", &N);//多项式的项数
    //建立带有一个空结点的链表
    P = (Polynomial)molloc(sizeof(struct Polynomial));
    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;
}
  • Attach的实现(思路2)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TcnoOQED-1586684135579)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200410203251532.png)]

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值 */
}

2.5.1.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) {
    
    
        if (t1->coef + t2->coef ) Attach(t1->coef + t2->coef,t1->expon,&Rear); 
        else{
    
    
            t1 = t1->link; t2 = t2->link;
        }
    }
    else if (t1->expon > t2->expon) {
    
    
        	Attach(t1->coef,t1->expon,&Rear);
        	t1 = t1->link;//P1指针向后移
    }
    else {
    
    
        	Attach(t2->coef,t2->expon,&Rear); 
        	t2 = t2->link;//P2指针向后移
    }
    while (t1) {
    
    
        Attach(t1->coef,t1->expon,&Rear);
        t1 = t1->link;//P1指针向后移
    }
    while (t2) {
    
    
        Attach(t2->coef,t2->expon,&Rear);
        t2 = t2->link;//P2指针向后移
   }
   t = P; P= P->link ; free(t);//释放表头空结点
   return P;
}

2.5.1.5乘法实现

  • 方法一

将乘法运算转变为加法运算

  • 将P1当前项(ci,ei)乘P2多项式,再加到结果多项式里
  • 遍历P1所有项,对P1每一项循环操作步骤一(黑->红->蓝->绿
  • 最后对所有的结果多项式合并∑Pi(P1每一个当前项乘P2多项式即为一个结果多项式Pi)
    • 注意:不是只利用一个结果多项式来存放结果,否则后期无法合并

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gF7VCH6V-1586684135581)(C:\Users\YZN\AppData\Roaming\Typora\typora-user-images\image-20200412164710597.png)]

t1 = P1; t2 = P2; 
P = (Polynomial)malloc(sizeof(struct PolyNode)); 
P->link = NULL; Rear = P; 
while (t2) {
    
     //求出P1当前项(ci,ei)乘P2多项式的结果多项式Pi
    Attach(t1->coef*t2->coef, t1->expon+t2->expon, &Rear); t2 = t2->link; //系数相乘,指数相加
}
  • 方法二:逐项加入

将P1当前项(c1i,e1i)乘P2当前项(c2i,e2i),并插入到结果多项式中

  • 关键:找到插入位置(查找)

  • 初始结果多项式可由P1第一项乘P2获得(如上)

Polynomial Mult( Polynomial P1, Polynomial P2 )
{
    
    
    Polynomial P, Rear, t1, t2, t;
    int c,e;
	t1 = P1; t2 = P2;
    
    //初始化结果多项式
    if (!P1 || !P2) return NULL;//判断两个多项式是否为空
	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) {
    
    //外层嵌套 P1多项式每一项
		t2 = P2; Rear = P;
		while (t2) {
    
    //内层嵌套 P2多项式每一项
			e = t1->expon + t2->expon;
			c = t1->coef * t2->coef;
            //查找插入位置(结果多项式按照指数递减排列)
			while (Rear->link){
    
    
                if ( Rear->link->expon > e)
                	Rear = Rear->link;
            	else if (Rear->link->expon == e){
    
    
                    int sum = Rear->link->c + c;
                    if (sum)
                        Rear->link->coef += c;
                    else{
    
    //删除结点
                        t = Rear->link; Rear->link = t->link; free(t);
                    } 
                }
                 else{
    
    //插入结点(插在Rear->link的前面)
                     t = (Polynomial)malloc(sizeof(struct PolyNode));
                     t->expon = e;t->coef = c;//填充结点
					 t->link = Rear->link; Rear->link = t;
					//不能漏掉,因为最后新插入一个结点,rear要指向这个结点,否则指向的是该结点的前一结点
                     Rear = Rear->link;
                 }     
            }
            //P2多项式的下一项乘积任务
			t2 = t2->link;
		}
		t1 = t1->link;
	}
    //释放表头空结点
    t = P; P= P->link ; free(t);
   return P;
}

2.5.1.6多项式输出

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");
}

2.5.2总结收获

  • 指针使用之前一定要判断是否为NULL,否则会出错
  • 创建带有一个空结点的链表方便结点的插入(不用判断结点是否要插在第一个位置,不用修改头指针的指向)

猜你喜欢

转载自blog.csdn.net/jimmy33777/article/details/105472787
今日推荐